Ver código fonte

Merge branch 'upstream-curl' into update-curl

* upstream-curl:
  curl 2025-09-10 (11b99123)
Brad King 1 mês atrás
pai
commit
8145958e01
100 arquivos alterados com 3490 adições e 3003 exclusões
  1. 1 1
      Utilities/cmcurl/CMake/CurlTests.c
  2. 0 72
      Utilities/cmcurl/CMake/FindMSH3.cmake
  3. 7 17
      Utilities/cmcurl/CMake/FindMbedTLS.cmake
  4. 13 4
      Utilities/cmcurl/CMake/FindNGTCP2.cmake
  5. 21 5
      Utilities/cmcurl/CMake/FindWolfSSL.cmake
  6. 1 1
      Utilities/cmcurl/CMake/PickyWarnings.cmake
  7. 2 13
      Utilities/cmcurl/CMake/win32-cache.cmake
  8. 78 76
      Utilities/cmcurl/CMakeLists.txt
  9. 114 104
      Utilities/cmcurl/include/curl/curl.h
  10. 3 4
      Utilities/cmcurl/include/curl/curlver.h
  11. 2 2
      Utilities/cmcurl/include/curl/easy.h
  12. 1 1
      Utilities/cmcurl/include/curl/header.h
  13. 3 3
      Utilities/cmcurl/include/curl/mprintf.h
  14. 45 1
      Utilities/cmcurl/include/curl/multi.h
  15. 1 1
      Utilities/cmcurl/include/curl/options.h
  16. 19 13
      Utilities/cmcurl/include/curl/system.h
  17. 90 10
      Utilities/cmcurl/include/curl/typecheck-gcc.h
  18. 1 1
      Utilities/cmcurl/include/curl/urlapi.h
  19. 17 4
      Utilities/cmcurl/include/curl/websockets.h
  20. 6 7
      Utilities/cmcurl/lib/CMakeLists.txt
  21. 3 2
      Utilities/cmcurl/lib/Makefile.inc
  22. 4 4
      Utilities/cmcurl/lib/altsvc.c
  23. 62 49
      Utilities/cmcurl/lib/asyn-ares.c
  24. 35 9
      Utilities/cmcurl/lib/asyn-base.c
  25. 230 162
      Utilities/cmcurl/lib/asyn-thrdd.c
  26. 12 8
      Utilities/cmcurl/lib/asyn.h
  27. 14 4
      Utilities/cmcurl/lib/bufq.c
  28. 8 6
      Utilities/cmcurl/lib/cf-h1-proxy.c
  29. 24 14
      Utilities/cmcurl/lib/cf-h2-proxy.c
  30. 1 2
      Utilities/cmcurl/lib/cf-h2-proxy.h
  31. 9 6
      Utilities/cmcurl/lib/cf-haproxy.c
  32. 1 1
      Utilities/cmcurl/lib/cf-haproxy.h
  33. 22 21
      Utilities/cmcurl/lib/cf-https-connect.c
  34. 2 8
      Utilities/cmcurl/lib/cf-https-connect.h
  35. 951 0
      Utilities/cmcurl/lib/cf-ip-happy.c
  36. 59 0
      Utilities/cmcurl/lib/cf-ip-happy.h
  37. 31 32
      Utilities/cmcurl/lib/cf-socket.c
  38. 0 8
      Utilities/cmcurl/lib/cf-socket.h
  39. 168 202
      Utilities/cmcurl/lib/cfilters.c
  40. 28 67
      Utilities/cmcurl/lib/cfilters.h
  41. 37 2
      Utilities/cmcurl/lib/conncache.c
  42. 4 0
      Utilities/cmcurl/lib/conncache.h
  43. 6 912
      Utilities/cmcurl/lib/connect.c
  44. 0 23
      Utilities/cmcurl/lib/connect.h
  45. 12 12
      Utilities/cmcurl/lib/content_encoding.c
  46. 48 42
      Utilities/cmcurl/lib/cookie.c
  47. 5 5
      Utilities/cmcurl/lib/cookie.h
  48. 27 12
      Utilities/cmcurl/lib/cshutdn.c
  49. 4 12
      Utilities/cmcurl/lib/curl_addrinfo.c
  50. 0 6
      Utilities/cmcurl/lib/curl_config.h.cmake
  51. 3 4
      Utilities/cmcurl/lib/curl_fnmatch.c
  52. 2 2
      Utilities/cmcurl/lib/curl_gethostname.c
  53. 1 1
      Utilities/cmcurl/lib/curl_gssapi.c
  54. 1 1
      Utilities/cmcurl/lib/curl_hmac.h
  55. 2 2
      Utilities/cmcurl/lib/curl_md4.h
  56. 2 2
      Utilities/cmcurl/lib/curl_md5.h
  57. 31 22
      Utilities/cmcurl/lib/curl_mem_undef.h
  58. 0 68
      Utilities/cmcurl/lib/curl_memory.h
  59. 18 23
      Utilities/cmcurl/lib/curl_ntlm_core.c
  60. 2 2
      Utilities/cmcurl/lib/curl_ntlm_core.h
  61. 32 32
      Utilities/cmcurl/lib/curl_rtmp.c
  62. 5 5
      Utilities/cmcurl/lib/curl_sasl.c
  63. 168 28
      Utilities/cmcurl/lib/curl_setup.h
  64. 0 6
      Utilities/cmcurl/lib/curl_setup_once.h
  65. 2 2
      Utilities/cmcurl/lib/curl_sha256.h
  66. 50 68
      Utilities/cmcurl/lib/curl_sha512_256.c
  67. 6 2
      Utilities/cmcurl/lib/curl_sspi.c
  68. 11 27
      Utilities/cmcurl/lib/curl_sspi.h
  69. 44 13
      Utilities/cmcurl/lib/curl_threads.c
  70. 27 10
      Utilities/cmcurl/lib/curl_threads.h
  71. 54 12
      Utilities/cmcurl/lib/curl_trc.c
  72. 15 3
      Utilities/cmcurl/lib/curl_trc.h
  73. 0 9
      Utilities/cmcurl/lib/curlx/base64.c
  74. 7 6
      Utilities/cmcurl/lib/curlx/dynbuf.c
  75. 1 1
      Utilities/cmcurl/lib/curlx/dynbuf.h
  76. 1 1
      Utilities/cmcurl/lib/curlx/inet_ntop.c
  77. 3 3
      Utilities/cmcurl/lib/curlx/inet_ntop.h
  78. 3 3
      Utilities/cmcurl/lib/curlx/inet_pton.h
  79. 2 6
      Utilities/cmcurl/lib/curlx/multibyte.h
  80. 1 1
      Utilities/cmcurl/lib/curlx/nonblock.c
  81. 1 1
      Utilities/cmcurl/lib/curlx/strparse.c
  82. 5 5
      Utilities/cmcurl/lib/curlx/timeval.c
  83. 1 1
      Utilities/cmcurl/lib/curlx/wait.c
  84. 3 3
      Utilities/cmcurl/lib/curlx/warnless.c
  85. 72 37
      Utilities/cmcurl/lib/cw-out.c
  86. 7 7
      Utilities/cmcurl/lib/dict.c
  87. 2 4
      Utilities/cmcurl/lib/dllmain.c
  88. 25 24
      Utilities/cmcurl/lib/doh.c
  89. 6 6
      Utilities/cmcurl/lib/doh.h
  90. 61 34
      Utilities/cmcurl/lib/easy.c
  91. 1 7
      Utilities/cmcurl/lib/easy_lock.h
  92. 1 1
      Utilities/cmcurl/lib/easygetopt.c
  93. 10 10
      Utilities/cmcurl/lib/file.c
  94. 3 0
      Utilities/cmcurl/lib/fileinfo.c
  95. 1 1
      Utilities/cmcurl/lib/fopen.c
  96. 14 12
      Utilities/cmcurl/lib/formdata.c
  97. 529 500
      Utilities/cmcurl/lib/ftp.c
  98. 8 2
      Utilities/cmcurl/lib/ftp.h
  99. 9 9
      Utilities/cmcurl/lib/gopher.c
  100. 5 3
      Utilities/cmcurl/lib/hmac.c

+ 1 - 1
Utilities/cmcurl/CMake/CurlTests.c

@@ -87,7 +87,7 @@ int main(void)
 #elif defined(HAVE_GETHOSTBYNAME_R_5) || \
       defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT)
   rc = gethostbyname_r(address, &h, buffer, 8192, &h_errnop);
-  (void)hp; /* not used for test */
+  (void)hp;
   (void)h_errnop;
 #elif defined(HAVE_GETHOSTBYNAME_R_6) || \
       defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT)

+ 0 - 72
Utilities/cmcurl/CMake/FindMSH3.cmake

@@ -1,72 +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 msh3 library
-#
-# Input variables:
-#
-# - `MSH3_INCLUDE_DIR`:   The msh3 include directory.
-# - `MSH3_LIBRARY`:       Path to `msh3` library.
-#
-# Result variables:
-#
-# - `MSH3_FOUND`:         System has msh3.
-# - `MSH3_INCLUDE_DIRS`:  The msh3 include directories.
-# - `MSH3_LIBRARIES`:     The msh3 library names.
-# - `MSH3_LIBRARY_DIRS`:  The msh3 library directories.
-# - `MSH3_PC_REQUIRES`:   The msh3 pkg-config packages.
-# - `MSH3_CFLAGS`:        Required compiler flags.
-# - `MSH3_VERSION`:       Version of msh3.
-
-set(MSH3_PC_REQUIRES "libmsh3")
-
-if(CURL_USE_PKGCONFIG AND
-   NOT DEFINED MSH3_INCLUDE_DIR AND
-   NOT DEFINED MSH3_LIBRARY)
-  find_package(PkgConfig QUIET)
-  pkg_check_modules(MSH3 ${MSH3_PC_REQUIRES})
-endif()
-
-if(MSH3_FOUND)
-  string(REPLACE ";" " " MSH3_CFLAGS "${MSH3_CFLAGS}")
-  message(STATUS "Found MSH3 (via pkg-config): ${MSH3_INCLUDE_DIRS} (found version \"${MSH3_VERSION}\")")
-else()
-  set(MSH3_PC_REQUIRES "")  # Depend on pkg-config only when found via pkg-config
-
-  find_path(MSH3_INCLUDE_DIR NAMES "msh3.h")
-  find_library(MSH3_LIBRARY NAMES "msh3")
-
-  include(FindPackageHandleStandardArgs)
-  find_package_handle_standard_args(MSH3
-    REQUIRED_VARS
-      MSH3_INCLUDE_DIR
-      MSH3_LIBRARY
-  )
-
-  if(MSH3_FOUND)
-    set(MSH3_INCLUDE_DIRS ${MSH3_INCLUDE_DIR})
-    set(MSH3_LIBRARIES    ${MSH3_LIBRARY})
-  endif()
-
-  mark_as_advanced(MSH3_INCLUDE_DIR MSH3_LIBRARY)
-endif()

+ 7 - 17
Utilities/cmcurl/CMake/FindMbedTLS.cmake

@@ -71,23 +71,13 @@ else()
   find_library(MBEDCRYPTO_LIBRARY NAMES "mbedcrypto" "libmbedcrypto")
 
   unset(MBEDTLS_VERSION CACHE)
-  if(MBEDTLS_INCLUDE_DIR)
-    if(EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h")  # 3.x
-      set(_version_header "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h")
-    elseif(EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h")  # 2.x
-      set(_version_header "${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h")
-    else()
-      unset(_version_header)
-    endif()
-    if(_version_header)
-      set(_version_regex "#[\t ]*define[\t ]+MBEDTLS_VERSION_STRING[\t ]+\"([0-9.]+)\"")
-      file(STRINGS "${_version_header}" _version_str REGEX "${_version_regex}")
-      string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}")
-      set(MBEDTLS_VERSION "${_version_str}")
-      unset(_version_regex)
-      unset(_version_str)
-      unset(_version_header)
-    endif()
+  if(MBEDTLS_INCLUDE_DIR AND EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h")
+    set(_version_regex "#[\t ]*define[\t ]+MBEDTLS_VERSION_STRING[\t ]+\"([0-9.]+)\"")
+    file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h" _version_str REGEX "${_version_regex}")
+    string(REGEX REPLACE "${_version_regex}" "\\1" _version_str "${_version_str}")
+    set(MBEDTLS_VERSION "${_version_str}")
+    unset(_version_regex)
+    unset(_version_str)
   endif()
 
   include(FindPackageHandleStandardArgs)

+ 13 - 4
Utilities/cmcurl/CMake/FindNGTCP2.cmake

@@ -26,11 +26,12 @@
 # This module accepts optional COMPONENTS to control the crypto library (these are
 # mutually exclusive):
 #
-# - quictls:    Use `libngtcp2_crypto_quictls`.   (choose this for LibreSSL)
-# - BoringSSL:  Use `libngtcp2_crypto_boringssl`. (choose this for AWS-LC)
-# - wolfSSL:    Use `libngtcp2_crypto_wolfssl`.
+# - BoringSSL:  Use `libngtcp2_crypto_boringssl`. (also for AWS-LC)
 # - GnuTLS:     Use `libngtcp2_crypto_gnutls`.
+# - LibreSSL:   Use `libngtcp2_crypto_libressl`. (requires ngtcp2 1.15.0+)
 # - ossl:       Use `libngtcp2_crypto_ossl`.
+# - quictls:    Use `libngtcp2_crypto_quictls`. (also for LibreSSL with ngtcp2 <1.15.0)
+# - wolfSSL:    Use `libngtcp2_crypto_wolfssl`.
 #
 # Input variables:
 #
@@ -38,6 +39,7 @@
 # - `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.
@@ -55,7 +57,7 @@
 if(NGTCP2_FIND_COMPONENTS)
   set(_ngtcp2_crypto_backend "")
   foreach(_component IN LISTS NGTCP2_FIND_COMPONENTS)
-    if(_component MATCHES "^(BoringSSL|GnuTLS|ossl|quictls|wolfSSL)")
+    if(_component MATCHES "^(BoringSSL|GnuTLS|LibreSSL|ossl|quictls|wolfSSL)")
       if(_ngtcp2_crypto_backend)
         message(FATAL_ERROR "NGTCP2: Only one crypto library can be selected")
       endif()
@@ -74,11 +76,13 @@ if(_ngtcp2_crypto_backend)
   list(APPEND NGTCP2_PC_REQUIRES "lib${_crypto_library_lower}")
 endif()
 
+set(_tried_pkgconfig FALSE)
 if(CURL_USE_PKGCONFIG AND
    NOT DEFINED NGTCP2_INCLUDE_DIR AND
    NOT DEFINED NGTCP2_LIBRARY)
   find_package(PkgConfig QUIET)
   pkg_check_modules(NGTCP2 ${NGTCP2_PC_REQUIRES})
+  set(_tried_pkgconfig TRUE)
 endif()
 
 if(NGTCP2_FOUND)
@@ -125,4 +129,9 @@ else()
   endif()
 
   mark_as_advanced(NGTCP2_INCLUDE_DIR NGTCP2_LIBRARY NGTCP2_CRYPTO_LIBRARY)
+
+  if(NOT NGTCP2_FOUND AND _tried_pkgconfig)  # reset variables to allow another round of detection
+    unset(NGTCP2_INCLUDE_DIR CACHE)
+    unset(NGTCP2_LIBRARY CACHE)
+  endif()
 endif()

+ 21 - 5
Utilities/cmcurl/CMake/FindWolfSSL.cmake

@@ -91,10 +91,26 @@ else()
   mark_as_advanced(WOLFSSL_INCLUDE_DIR WOLFSSL_LIBRARY)
 endif()
 
-if(WOLFSSL_FOUND AND NOT WIN32)
-  find_library(MATH_LIBRARY NAMES "m")
-  if(MATH_LIBRARY)
-    list(APPEND WOLFSSL_LIBRARIES ${MATH_LIBRARY})  # for log and pow
+if(WOLFSSL_FOUND)
+  if(APPLE)
+    find_library(SECURITY_FRAMEWORK NAMES "Security")
+    mark_as_advanced(SECURITY_FRAMEWORK)
+    if(NOT SECURITY_FRAMEWORK)
+      message(FATAL_ERROR "Security framework not found")
+    endif()
+    list(APPEND WOLFSSL_LIBRARIES "-framework Security")
+
+    find_library(COREFOUNDATION_FRAMEWORK NAMES "CoreFoundation")
+    mark_as_advanced(COREFOUNDATION_FRAMEWORK)
+    if(NOT COREFOUNDATION_FRAMEWORK)
+      message(FATAL_ERROR "CoreFoundation framework not found")
+    endif()
+    list(APPEND WOLFSSL_LIBRARIES "-framework CoreFoundation")
+  elseif(NOT WIN32)
+    find_library(MATH_LIBRARY NAMES "m")
+    if(MATH_LIBRARY)
+      list(APPEND WOLFSSL_LIBRARIES ${MATH_LIBRARY})  # for log and pow
+    endif()
+    mark_as_advanced(MATH_LIBRARY)
   endif()
-  mark_as_advanced(MATH_LIBRARY)
 endif()

+ 1 - 1
Utilities/cmcurl/CMake/PickyWarnings.cmake

@@ -305,7 +305,7 @@ if(PICKY_COMPILER)
         list(APPEND _picky "-Wno-conversion")  # Avoid false positives
       endif()
     endif()
-  elseif(MSVC AND MSVC_VERSION LESS_EQUAL 1943)  # Skip for untested/unreleased newer versions
+  elseif(MSVC AND MSVC_VERSION LESS_EQUAL 1944)  # Skip for untested/unreleased newer versions
     list(APPEND _picky "-Wall")
     list(APPEND _picky "-wd4061")  # enumerator 'A' in switch of enum 'B' is not explicitly handled by a case label
     list(APPEND _picky "-wd4191")  # 'type cast': unsafe conversion from 'FARPROC' to 'void (__cdecl *)(void)'

+ 2 - 13
Utilities/cmcurl/CMake/win32-cache.cmake

@@ -199,20 +199,10 @@ if(MINGW OR MSVC)
     set(HAVE_SIZEOF_SSIZE_T 0)
     set(HAVE_FILE_OFFSET_BITS 0)
     curl_prefill_type_size("OFF_T" 4)
-    curl_prefill_type_size("ADDRESS_FAMILY" 2)
   else()
     # SSIZE_T: 8 for _WIN64, 4 otherwise
-    if(MINGW64_VERSION)
-      if(MINGW64_VERSION VERSION_GREATER_EQUAL 3.0)
-        set(HAVE_FILE_OFFSET_BITS 1)
-        curl_prefill_type_size("OFF_T" 8)
-      endif()
-      if(MINGW64_VERSION VERSION_GREATER_EQUAL 2.0)
-        curl_prefill_type_size("ADDRESS_FAMILY" 2)
-      else()
-        set(HAVE_SIZEOF_ADDRESS_FAMILY 0)
-      endif()
-    endif()
+    set(HAVE_FILE_OFFSET_BITS 1)  # mingw-w64 v3+
+    curl_prefill_type_size("OFF_T" 8)  # mingw-w64 v3+
   endif()
 endif()
 
@@ -233,7 +223,6 @@ if(WINCE)
     set(HAVE_STRTOK_R 0)
     set(HAVE__SETMODE 0)
     set(HAVE_FILE_OFFSET_BITS 0)
-    set(HAVE_SIZEOF_ADDRESS_FAMILY 0)
     curl_prefill_type_size("SSIZE_T" 4)
     curl_prefill_type_size("OFF_T" 4)
   endif()

+ 78 - 76
Utilities/cmcurl/CMakeLists.txt

@@ -347,6 +347,7 @@ else()
 endif()
 
 set(LIB_NAME "libcurl")
+set(EXE_NAME "curl")
 
 set_property(DIRECTORY APPEND PROPERTY INCLUDE_DIRECTORIES "${PROJECT_SOURCE_DIR}/include")
 
@@ -419,6 +420,9 @@ if(WIN32)
       string(REGEX REPLACE "MINGW64_VERSION=" "" MINGW64_VERSION "${CURL_TEST_OUTPUT}")
       if(MINGW64_VERSION)
         message(STATUS "Found MINGW64_VERSION=${MINGW64_VERSION}")
+        if(MINGW64_VERSION VERSION_LESS 3.0)
+          message(FATAL_ERROR "mingw-w64 3.0 or upper is required")
+        endif()
       endif()
     endif()
     unset(MINGW64_VERSION CACHE)  # Avoid storing in CMake cache
@@ -443,7 +447,7 @@ endif()
 include(PickyWarnings)
 endif() # XXX(cmake): end
 
-if(CYGWIN OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
+if(CYGWIN OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "GNU")
   set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "_GNU_SOURCE")  # Required for accept4(), pipe2(), sendmmsg()
   list(APPEND CMAKE_REQUIRED_DEFINITIONS "-D_GNU_SOURCE")  # Apply to all feature checks
 endif()
@@ -470,6 +474,7 @@ if(CURL_CLANG_TIDY)
   list(APPEND _tidy_checks "-clang-analyzer-security.insecureAPI.bzero")  # for FD_ZERO() (seen on macOS)
   list(APPEND _tidy_checks "-clang-analyzer-security.insecureAPI.strcpy")
   list(APPEND _tidy_checks "-clang-analyzer-optin.performance.Padding")
+  list(APPEND _tidy_checks "-clang-analyzer-security.ArrayBound")  # false positives with clang-tidy v21.1.0
   list(APPEND _tidy_checks "-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling")
   string(REPLACE ";" "," _tidy_checks "${_tidy_checks}")
   find_program(CLANG_TIDY NAMES "clang-tidy" REQUIRED)
@@ -529,7 +534,7 @@ if(WIN32)
 endif()
 
 # Override to force-disable or force-enable the use of pkg-config.
-if((UNIX AND NOT ANDROID AND (NOT APPLE OR CMAKE_SYSTEM_NAME MATCHES "Darwin")) OR
+if((UNIX AND NOT ANDROID AND (NOT APPLE OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")) OR
    VCPKG_TOOLCHAIN OR
    (MINGW AND NOT CMAKE_CROSSCOMPILING))
   set(_curl_use_pkgconfig_default ON)
@@ -539,6 +544,7 @@ endif()
 option(CURL_USE_PKGCONFIG "Enable pkg-config to detect dependencies" ${_curl_use_pkgconfig_default})
 
 # Initialize variables collecting dependency libs, paths, pkg-config names.
+set(CURL_NETWORK_AND_TIME_LIBS "")
 set(CURL_LIBS "")
 set(CURL_LIBDIRS "")
 set(LIBCURL_PC_REQUIRES_PRIVATE "")
@@ -657,10 +663,11 @@ option(CURL_DISABLE_VERBOSE_STRINGS "Disable verbose strings" OFF)
 mark_as_advanced(CURL_DISABLE_VERBOSE_STRINGS)
 
 if(CURL_DISABLE_HTTP)
-  set(CURL_DISABLE_IPFS ON)
-  set(CURL_DISABLE_RTSP ON)
   set(CURL_DISABLE_ALTSVC ON)
   set(CURL_DISABLE_HSTS ON)
+  set(CURL_DISABLE_IPFS ON)
+  set(CURL_DISABLE_RTSP ON)
+  set(CURL_DISABLE_WEBSOCKETS ON)
 endif()
 
 # Corresponds to HTTP_ONLY in lib/curl_setup.h
@@ -673,11 +680,11 @@ if(HTTP_ONLY)
   set(CURL_DISABLE_FTP ON)
   set(CURL_DISABLE_GOPHER ON)
   set(CURL_DISABLE_IMAP ON)
+  set(CURL_DISABLE_IPFS ON)
   set(CURL_DISABLE_LDAP ON)
   set(CURL_DISABLE_LDAPS ON)
   set(CURL_DISABLE_MQTT ON)
   set(CURL_DISABLE_POP3 ON)
-  set(CURL_DISABLE_IPFS ON)
   set(CURL_DISABLE_RTSP ON)
   set(CURL_DISABLE_SMB ON)
   set(CURL_DISABLE_SMTP ON)
@@ -709,13 +716,8 @@ option(BUILD_LIBCURL_DOCS "Build libcurl man pages" ON)
 option(BUILD_MISC_DOCS "Build misc man pages (e.g. curl-config and mk-ca-bundle)" ON)
 option(ENABLE_CURL_MANUAL "Build the man page for curl and enable its -M/--manual option" ON)
 
-if(ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS)
-  if(PERL_FOUND)
-    set(HAVE_MANUAL_TOOLS ON)
-  endif()
-  if(NOT HAVE_MANUAL_TOOLS)
-    message(WARNING "Perl not found. Will not build manuals.")
-  endif()
+if((ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS) AND NOT PERL_FOUND)
+  message(WARNING "Perl not found. Will not build manuals.")
 endif()
 endif() # XXX(cmake): end
 
@@ -726,9 +728,9 @@ endif()
 
 # If we are on Haiku, make sure that the network library is brought in.
 if(CMAKE_SYSTEM_NAME STREQUAL "Haiku")
-  list(APPEND CURL_LIBS "network")
+  list(APPEND CURL_NETWORK_AND_TIME_LIBS "network")
 elseif(AMIGA)
-  list(APPEND CURL_LIBS "net" "m" "atomic")
+  list(APPEND CURL_NETWORK_AND_TIME_LIBS "net" "m" "atomic")
   list(APPEND CMAKE_REQUIRED_LIBRARIES "net" "m" "atomic")
 endif()
 
@@ -777,7 +779,7 @@ if(ENABLE_THREADED_RESOLVER)
     find_package(Threads REQUIRED)
     set(USE_THREADS_POSIX ${CMAKE_USE_PTHREADS_INIT})
     set(HAVE_PTHREAD_H ${CMAKE_USE_PTHREADS_INIT})
-    list(APPEND CURL_LIBS ${CMAKE_THREAD_LIBS_INIT})
+    list(APPEND CURL_NETWORK_AND_TIME_LIBS ${CMAKE_THREAD_LIBS_INIT})
   endif()
 endif()
 
@@ -800,7 +802,7 @@ elseif(DOS)
   if(WATT_ROOT)
     set(USE_WATT32 ON)
     # FIXME upstream: must specify the full path to avoid CMake converting "watt" to "watt.lib"
-    list(APPEND CURL_LIBS "${WATT_ROOT}/lib/libwatt.a")
+    list(APPEND CURL_NETWORK_AND_TIME_LIBS "${WATT_ROOT}/lib/libwatt.a")
     include_directories(SYSTEM "${WATT_ROOT}/inc")
     list(APPEND CMAKE_REQUIRED_INCLUDES "${WATT_ROOT}/inc")
   else()
@@ -820,7 +822,7 @@ elseif(AMIGA)
 elseif(NOT APPLE)
   check_library_exists("socket" "connect" "" HAVE_LIBSOCKET)
   if(HAVE_LIBSOCKET)
-    set(CURL_LIBS "socket" ${CURL_LIBS})
+    set(CURL_NETWORK_AND_TIME_LIBS "socket" ${CURL_NETWORK_AND_TIME_LIBS})
   endif()
 endif()
 
@@ -840,7 +842,7 @@ if(ENABLE_IPV6)
       set(ENABLE_IPV6 OFF CACHE BOOL "Enable IPv6 support" FORCE)  # Force the feature off as we use this name as guard macro
     endif()
 
-    if(APPLE AND NOT ENABLE_ARES)
+    if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT ENABLE_ARES)
       set(_use_core_foundation_and_core_services ON)
 
       find_library(SYSTEMCONFIGURATION_FRAMEWORK NAMES "SystemConfiguration")
@@ -867,6 +869,9 @@ endif()
 if(WIN32)
   cmake_dependent_option(CURL_USE_SCHANNEL "Enable Windows native SSL/TLS (Schannel)" OFF CURL_ENABLE_SSL OFF)
   option(CURL_WINDOWS_SSPI "Enable SSPI on Windows" ${CURL_USE_SCHANNEL})
+else()
+  set(CURL_USE_SCHANNEL OFF)
+  set(CURL_WINDOWS_SSPI OFF)
 endif()
 cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
 cmake_dependent_option(CURL_USE_WOLFSSL "Enable wolfSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
@@ -907,6 +912,9 @@ elseif(_enabled_ssl_options_count EQUAL 0)
 endif()
 
 if(CURL_USE_SCHANNEL)
+  if(WINDOWS_STORE)
+    message(FATAL_ERROR "UWP does not support Schannel.")
+  endif()
   set(_ssl_enabled ON)
   set(USE_SCHANNEL ON)  # Windows native SSL/TLS support
   set(USE_WINDOWS_SSPI ON)  # CURL_USE_SCHANNEL requires CURL_WINDOWS_SSPI
@@ -915,7 +923,7 @@ if(CURL_USE_SCHANNEL)
     set(_valid_default_ssl_backend TRUE)
   endif()
 endif()
-if(CURL_WINDOWS_SSPI)
+if(CURL_WINDOWS_SSPI AND NOT WINDOWS_STORE)
   set(USE_WINDOWS_SSPI ON)
 endif()
 
@@ -999,6 +1007,9 @@ endif()
 
 if(CURL_USE_MBEDTLS)
   find_package(MbedTLS REQUIRED)
+  if(MBEDTLS_VERSION VERSION_LESS 3.2.0)
+    message(FATAL_ERROR "mbedTLS v3.2.0 or newer is required.")
+  endif()
   set(_ssl_enabled ON)
   set(USE_MBEDTLS ON)
   list(APPEND CURL_LIBS ${MBEDTLS_LIBRARIES})
@@ -1275,7 +1286,7 @@ if(USE_SSLS_EXPORT)
   if(_ssl_enabled)
     message(STATUS "SSL export enabled.")
   else()
-    message(FATAL_ERROR "SSL session export requires SSL enabled")
+    message(WARNING "SSL session export requires SSL enabled")
   endif()
 endif()
 
@@ -1298,22 +1309,27 @@ endif()
 
 option(USE_NGTCP2 "Use ngtcp2 and nghttp3 libraries for HTTP/3 support" OFF)
 if(USE_NGTCP2)
-  if(USE_OPENSSL OR USE_WOLFSSL)
+  if(CURL_WITH_MULTI_SSL)
+    message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.")
+  elseif(USE_OPENSSL OR USE_WOLFSSL)
     if(USE_WOLFSSL)
-      find_package(NGTCP2 REQUIRED "wolfSSL")
+      find_package(NGTCP2 REQUIRED COMPONENTS "wolfSSL")
     elseif(HAVE_BORINGSSL OR HAVE_AWSLC)
-      find_package(NGTCP2 REQUIRED "BoringSSL")
+      find_package(NGTCP2 REQUIRED COMPONENTS "BoringSSL")
     elseif(OPENSSL_VERSION VERSION_GREATER_EQUAL 3.5.0 AND NOT USE_OPENSSL_QUIC)
-      find_package(NGTCP2 REQUIRED "ossl")
+      find_package(NGTCP2 REQUIRED COMPONENTS "ossl")
       if(NGTCP2_VERSION VERSION_LESS 1.12.0)
         message(FATAL_ERROR "ngtcp2 1.12.0 or upper required for OpenSSL")
       endif()
       set(OPENSSL_QUIC_API2 1)
-    else()
-      find_package(NGTCP2 REQUIRED "quictls")
-      if(NOT HAVE_LIBRESSL)
-        set(_openssl "quictls")
+    elseif(HAVE_LIBRESSL)
+      find_package(NGTCP2 COMPONENTS "LibreSSL")
+      if(NOT NGTCP2_FOUND)
+        find_package(NGTCP2 REQUIRED COMPONENTS "quictls")  # for ngtcp2 <1.15.0
       endif()
+    else()
+      find_package(NGTCP2 REQUIRED COMPONENTS "quictls")
+      set(_openssl "quictls")
     endif()
     curl_openssl_check_quic()
   elseif(USE_GNUTLS)
@@ -1346,6 +1362,8 @@ option(USE_QUICHE "Use quiche library for HTTP/3 support" OFF)
 if(USE_QUICHE)
   if(USE_NGTCP2)
     message(FATAL_ERROR "Only one HTTP/3 backend can be selected")
+  elseif(CURL_WITH_MULTI_SSL)
+    message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.")
   endif()
   find_package(Quiche REQUIRED)
   if(NOT HAVE_BORINGSSL)
@@ -1369,31 +1387,11 @@ if(USE_QUICHE)
   endif()
 endif()
 
-option(USE_MSH3 "Use msh3/msquic library for HTTP/3 support" OFF)
-if(USE_MSH3)
-  if(USE_NGTCP2 OR USE_QUICHE)
-    message(FATAL_ERROR "Only one HTTP/3 backend can be selected")
-  endif()
-  if(NOT WIN32)
-    if(NOT USE_OPENSSL)
-      message(FATAL_ERROR "msh3/msquic requires OpenSSL fork with QUIC API")
-    endif()
-    curl_openssl_check_quic()
-  endif()
-  find_package(MSH3 REQUIRED)
-  list(APPEND CURL_LIBS ${MSH3_LIBRARIES})
-  list(APPEND CURL_LIBDIRS ${MSH3_LIBRARY_DIRS})
-  list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${MSH3_PC_REQUIRES})
-  include_directories(SYSTEM ${MSH3_INCLUDE_DIRS})
-  link_directories(${MSH3_LIBRARY_DIRS})
-  if(MSH3_CFLAGS)
-    string(APPEND CMAKE_C_FLAGS " ${MSH3_CFLAGS}")
-  endif()
-endif()
-
 if(USE_OPENSSL_QUIC)
-  if(USE_NGTCP2 OR USE_QUICHE OR USE_MSH3)
+  if(USE_NGTCP2 OR USE_QUICHE)
     message(FATAL_ERROR "Only one HTTP/3 backend can be selected")
+  elseif(CURL_WITH_MULTI_SSL)
+    message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.")
   endif()
   find_package(OpenSSL 3.3.0 REQUIRED)
 
@@ -1409,10 +1407,6 @@ if(USE_OPENSSL_QUIC)
   endif()
 endif()
 
-if(CURL_WITH_MULTI_SSL AND (USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC))
-  message(FATAL_ERROR "MultiSSL cannot be enabled with HTTP/3 and vice versa.")
-endif()
-
 if(NOT CURL_DISABLE_SRP AND (HAVE_GNUTLS_SRP OR HAVE_OPENSSL_SRP))
   set(USE_TLS_SRP 1)
 endif()
@@ -1706,12 +1700,13 @@ endif()
 option(ENABLE_UNIX_SOCKETS "Enable Unix domain sockets support" ON)
 if(ENABLE_UNIX_SOCKETS AND NOT WINCE)
   if(WIN32 OR DOS)
-    set(USE_UNIX_SOCKETS ON)
+    set(USE_UNIX_SOCKETS 1)
   else()
     include(CheckStructHasMember)
     check_struct_has_member("struct sockaddr_un" "sun_path" "sys/un.h" USE_UNIX_SOCKETS)
   endif()
 else()
+  set(USE_UNIX_SOCKETS 0)
   unset(USE_UNIX_SOCKETS CACHE)
 endif()
 
@@ -1723,12 +1718,16 @@ if(_curl_ca_bundle_supported)
   set(CURL_CA_BUNDLE "auto" CACHE
     STRING "Path to the CA bundle. Set 'none' to disable or 'auto' for auto-detection. Defaults to 'auto'.")
   set(CURL_CA_FALLBACK OFF CACHE
-    BOOL "Use built-in CA store of TLS backend. Defaults to OFF")
+    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'.")
   set(CURL_CA_EMBED "" CACHE
     STRING "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.")
+  endif()
+
   if(CURL_CA_BUNDLE STREQUAL "")
     message(FATAL_ERROR "Invalid value of CURL_CA_BUNDLE. Use 'none', 'auto' or file path.")
   elseif(CURL_CA_BUNDLE STREQUAL "none")
@@ -1822,14 +1821,16 @@ if(WIN32)
   endif()
 
   # Pre-fill detection results based on target OS version
-  if(HAVE_WIN32_WINNT AND HAVE_WIN32_WINNT GREATER_EQUAL 0x0600 AND  # Windows Vista or newer
-     (MINGW OR MSVC) AND
-     NOT WINCE AND NOT WINDOWS_STORE)
-    set(HAVE_IF_NAMETOINDEX 1)
-  else()
-    set(HAVE_IF_NAMETOINDEX 0)
+  if(_CURL_PREFILL)
+    if(NOT HAVE_WIN32_WINNT OR HAVE_WIN32_WINNT LESS 0x0600 OR  # older than Windows Vista
+       WINCE OR WINDOWS_STORE)
+      set(HAVE_IF_NAMETOINDEX 0)
+      unset(HAVE_IF_NAMETOINDEX CACHE)
+    elseif(MSVC OR MINGW)
+      set(HAVE_IF_NAMETOINDEX 1)
+      unset(HAVE_IF_NAMETOINDEX CACHE)
+    endif()
   endif()
-  unset(HAVE_IF_NAMETOINDEX CACHE)
 endif()
 
 if(NOT WIN32)
@@ -1971,12 +1972,15 @@ check_function_exists("eventfd"       HAVE_EVENTFD)
 check_symbol_exists("ftruncate"       "unistd.h" HAVE_FTRUNCATE)
 check_symbol_exists("getpeername"     "${CURL_INCLUDES}" HAVE_GETPEERNAME)  # winsock2.h unistd.h proto/bsdsocket.h
 check_symbol_exists("getsockname"     "${CURL_INCLUDES}" HAVE_GETSOCKNAME)  # winsock2.h unistd.h proto/bsdsocket.h
-check_function_exists("if_nametoindex"  HAVE_IF_NAMETOINDEX)  # iphlpapi.h (Windows Vista+ non-UWP), net/if.h
 check_function_exists("getrlimit"       HAVE_GETRLIMIT)
 check_function_exists("setlocale"       HAVE_SETLOCALE)
 check_function_exists("setrlimit"       HAVE_SETRLIMIT)
 
-if(NOT WIN32)
+if(WIN32)
+  # include wincrypt.h as a workaround for mingw-w64 __MINGW64_VERSION_MAJOR <= 5 header bug */
+  check_symbol_exists("if_nametoindex"  "winsock2.h;wincrypt.h;iphlpapi.h" HAVE_IF_NAMETOINDEX)  # Windows Vista+ non-UWP */
+else()
+  check_function_exists("if_nametoindex"  HAVE_IF_NAMETOINDEX)  # net/if.h
   check_function_exists("realpath"        HAVE_REALPATH)
   check_function_exists("sched_yield"     HAVE_SCHED_YIELD)
   check_symbol_exists("strcasecmp"      "string.h" HAVE_STRCASECMP)
@@ -2019,11 +2023,7 @@ if(HAVE_FSETXATTR)
 endif()
 
 cmake_push_check_state()
-if(WIN32)
-  list(APPEND CMAKE_EXTRA_INCLUDE_FILES "winsock2.h")
-  check_type_size("ADDRESS_FAMILY" SIZEOF_ADDRESS_FAMILY)
-  set(HAVE_ADDRESS_FAMILY ${HAVE_SIZEOF_ADDRESS_FAMILY})
-else()
+if(NOT WIN32)
   list(APPEND CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h")
   check_type_size("sa_family_t" SIZEOF_SA_FAMILY_T)
   set(HAVE_SA_FAMILY_T ${HAVE_SIZEOF_SA_FAMILY_T})
@@ -2156,9 +2156,9 @@ include(CMake/OtherTests.cmake)
 set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "HAVE_CONFIG_H")
 
 if(WIN32)
-  list(APPEND CURL_LIBS "${_win32_winsock}")
+  list(APPEND CURL_NETWORK_AND_TIME_LIBS "${_win32_winsock}")
   if(NOT WINCE AND NOT WINDOWS_STORE)
-    list(APPEND CURL_LIBS "iphlpapi")
+    list(APPEND CURL_NETWORK_AND_TIME_LIBS "iphlpapi")
   endif()
   if(NOT WINCE)
     list(APPEND CURL_LIBS "bcrypt")
@@ -2185,6 +2185,8 @@ if(WIN32)
   endif()
 endif()
 
+list(APPEND CURL_LIBS ${CURL_NETWORK_AND_TIME_LIBS})
+
 if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")  # MSVC but exclude clang-cl
   set_property(DIRECTORY APPEND PROPERTY COMPILE_OPTIONS "-MP")  # Parallel compilation
 endif()
@@ -2259,7 +2261,7 @@ else()
   set(CURL_BUILD_TESTING OFF)
 endif()
 
-if(HAVE_MANUAL_TOOLS)
+if(PERL_FOUND)
   set(CURL_MANPAGE "${PROJECT_BINARY_DIR}/docs/cmdline-opts/curl.1")
   set(CURL_ASCIIPAGE "${PROJECT_BINARY_DIR}/docs/cmdline-opts/curl.txt")
   add_subdirectory(docs)
@@ -2376,7 +2378,7 @@ curl_add_if("NTLM"          NOT CURL_DISABLE_NTLM AND
                             (_use_curl_ntlm_core OR USE_WINDOWS_SSPI))
 curl_add_if("TLS-SRP"       USE_TLS_SRP)
 curl_add_if("HTTP2"         USE_NGHTTP2)
-curl_add_if("HTTP3"         USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC)
+curl_add_if("HTTP3"         USE_NGTCP2 OR USE_QUICHE OR USE_OPENSSL_QUIC)
 curl_add_if("MultiSSL"      CURL_WITH_MULTI_SSL)
 curl_add_if("HTTPS-proxy"   NOT CURL_DISABLE_PROXY AND _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS
                             OR USE_SCHANNEL OR USE_RUSTLS OR USE_MBEDTLS OR
@@ -2411,7 +2413,7 @@ curl_add_if("${_openssl} v3+"  _ssl_enabled AND USE_OPENSSL AND OPENSSL_VERSION
 curl_add_if("mbedTLS"          _ssl_enabled AND USE_MBEDTLS)
 curl_add_if("wolfSSL"          _ssl_enabled AND USE_WOLFSSL)
 curl_add_if("GnuTLS"           _ssl_enabled AND USE_GNUTLS)
-curl_add_if("rustls"           _ssl_enabled AND USE_RUSTLS)
+curl_add_if("Rustls"           _ssl_enabled AND USE_RUSTLS)
 
 if(_items)
   if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)

+ 114 - 104
Utilities/cmcurl/include/curl/curl.h

@@ -102,7 +102,7 @@
 #include <sys/time.h>
 #endif
 
-#ifdef  __cplusplus
+#ifdef __cplusplus
 extern "C" {
 #endif
 
@@ -124,7 +124,7 @@ typedef void CURLSH;
 #elif defined(_WIN32) || \
      (CURL_HAS_DECLSPEC_ATTRIBUTE(dllexport) && \
       CURL_HAS_DECLSPEC_ATTRIBUTE(dllimport))
-#  if defined(BUILDING_LIBCURL)
+#  ifdef BUILDING_LIBCURL
 #    define CURL_EXTERN  __declspec(dllexport)
 #  else
 #    define CURL_EXTERN  __declspec(dllimport)
@@ -788,20 +788,24 @@ typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl,    /* easy handle */
                                                           mbedtls_ssl_config */
                                           void *userptr);
 
+#define CURLPROXY_HTTP            0L /* added in 7.10, new in 7.19.4 default is
+                                        to use CONNECT HTTP/1.1 */
+#define CURLPROXY_HTTP_1_0        1L /* force to use CONNECT HTTP/1.0
+                                        added in 7.19.4 */
+#define CURLPROXY_HTTPS           2L /* HTTPS but stick to HTTP/1
+                                        added in 7.52.0 */
+#define CURLPROXY_HTTPS2          3L /* HTTPS and attempt HTTP/2
+                                        added in 8.2.0 */
+#define CURLPROXY_SOCKS4          4L /* support added in 7.15.2, enum existed
+                                        already in 7.10 */
+#define CURLPROXY_SOCKS5          5L /* added in 7.10 */
+#define CURLPROXY_SOCKS4A         6L /* added in 7.18.0 */
+#define CURLPROXY_SOCKS5_HOSTNAME 7L /* Use the SOCKS5 protocol but pass along
+                                        the hostname rather than the IP
+                                        address. added in 7.18.0 */
+
 typedef enum {
-  CURLPROXY_HTTP = 0,   /* added in 7.10, new in 7.19.4 default is to use
-                           CONNECT HTTP/1.1 */
-  CURLPROXY_HTTP_1_0 = 1,   /* added in 7.19.4, force to use CONNECT
-                               HTTP/1.0  */
-  CURLPROXY_HTTPS = 2,  /* HTTPS but stick to HTTP/1 added in 7.52.0 */
-  CURLPROXY_HTTPS2 = 3, /* HTTPS and attempt HTTP/2 added in 8.2.0 */
-  CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already
-                           in 7.10 */
-  CURLPROXY_SOCKS5 = 5, /* added in 7.10 */
-  CURLPROXY_SOCKS4A = 6, /* added in 7.18.0 */
-  CURLPROXY_SOCKS5_HOSTNAME = 7 /* Use the SOCKS5 protocol but pass along the
-                                   hostname rather than the IP address. added
-                                   in 7.18.0 */
+  CURLPROXY_LAST = 8 /* never use */
 } curl_proxytype;  /* this enum was added in 7.10 */
 
 /*
@@ -842,19 +846,19 @@ typedef enum {
 #define CURLAUTH_ANY          (~CURLAUTH_DIGEST_IE)
 #define CURLAUTH_ANYSAFE      (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE))
 
-#define CURLSSH_AUTH_ANY       ~0     /* all types supported by the server */
-#define CURLSSH_AUTH_NONE      0      /* none allowed, silly but complete */
-#define CURLSSH_AUTH_PUBLICKEY (1<<0) /* public/private key files */
-#define CURLSSH_AUTH_PASSWORD  (1<<1) /* password */
-#define CURLSSH_AUTH_HOST      (1<<2) /* host key files */
-#define CURLSSH_AUTH_KEYBOARD  (1<<3) /* keyboard interactive */
-#define CURLSSH_AUTH_AGENT     (1<<4) /* agent (ssh-agent, pageant...) */
-#define CURLSSH_AUTH_GSSAPI    (1<<5) /* gssapi (kerberos, ...) */
+#define CURLSSH_AUTH_ANY       ~0L     /* all types supported by the server */
+#define CURLSSH_AUTH_NONE      0L      /* none allowed, silly but complete */
+#define CURLSSH_AUTH_PUBLICKEY (1L<<0) /* public/private key files */
+#define CURLSSH_AUTH_PASSWORD  (1L<<1) /* password */
+#define CURLSSH_AUTH_HOST      (1L<<2) /* host key files */
+#define CURLSSH_AUTH_KEYBOARD  (1L<<3) /* keyboard interactive */
+#define CURLSSH_AUTH_AGENT     (1L<<4) /* agent (ssh-agent, pageant...) */
+#define CURLSSH_AUTH_GSSAPI    (1L<<5) /* gssapi (kerberos, ...) */
 #define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY
 
-#define CURLGSSAPI_DELEGATION_NONE        0      /* no delegation (default) */
-#define CURLGSSAPI_DELEGATION_POLICY_FLAG (1<<0) /* if permitted by policy */
-#define CURLGSSAPI_DELEGATION_FLAG        (1<<1) /* delegate always */
+#define CURLGSSAPI_DELEGATION_NONE        0L      /* no delegation (default) */
+#define CURLGSSAPI_DELEGATION_POLICY_FLAG (1L<<0) /* if permitted by policy */
+#define CURLGSSAPI_DELEGATION_FLAG        (1L<<1) /* delegate always */
 
 #define CURL_ERROR_SIZE 256
 
@@ -979,50 +983,55 @@ typedef enum {
 #endif /* !CURL_NO_OLDIES */
 
 /* parameter for the CURLOPT_FTP_SSL_CCC option */
+#define CURLFTPSSL_CCC_NONE    0L /* do not send CCC */
+#define CURLFTPSSL_CCC_PASSIVE 1L /* Let the server initiate the shutdown */
+#define CURLFTPSSL_CCC_ACTIVE  2L /* Initiate the shutdown */
+
 typedef enum {
-  CURLFTPSSL_CCC_NONE,    /* do not send CCC */
-  CURLFTPSSL_CCC_PASSIVE, /* Let the server initiate the shutdown */
-  CURLFTPSSL_CCC_ACTIVE,  /* Initiate the shutdown */
-  CURLFTPSSL_CCC_LAST     /* not an option, never use */
+  CURLFTPSSL_CCC_LAST = 3 /* not an option, never use */
 } curl_ftpccc;
 
 /* parameter for the CURLOPT_FTPSSLAUTH option */
+#define CURLFTPAUTH_DEFAULT 0L /* let libcurl decide */
+#define CURLFTPAUTH_SSL     1L /* use "AUTH SSL" */
+#define CURLFTPAUTH_TLS     2L /* use "AUTH TLS" */
+
 typedef enum {
-  CURLFTPAUTH_DEFAULT, /* let libcurl decide */
-  CURLFTPAUTH_SSL,     /* use "AUTH SSL" */
-  CURLFTPAUTH_TLS,     /* use "AUTH TLS" */
-  CURLFTPAUTH_LAST /* not an option, never use */
+  CURLFTPAUTH_LAST = 3 /* not an option, never use */
 } curl_ftpauth;
 
 /* parameter for the CURLOPT_FTP_CREATE_MISSING_DIRS option */
+#define CURLFTP_CREATE_DIR_NONE  0L /* do NOT create missing dirs! */
+#define CURLFTP_CREATE_DIR       1L /* (FTP/SFTP) if CWD fails, try MKD and
+                                       then CWD again if MKD succeeded, for
+                                       SFTP this does similar magic */
+#define CURLFTP_CREATE_DIR_RETRY 2L /* (FTP only) if CWD fails, try MKD and
+                                       then CWD again even if MKD failed! */
+
 typedef enum {
-  CURLFTP_CREATE_DIR_NONE,  /* do NOT create missing dirs! */
-  CURLFTP_CREATE_DIR,       /* (FTP/SFTP) if CWD fails, try MKD and then CWD
-                               again if MKD succeeded, for SFTP this does
-                               similar magic */
-  CURLFTP_CREATE_DIR_RETRY, /* (FTP only) if CWD fails, try MKD and then CWD
-                               again even if MKD failed! */
-  CURLFTP_CREATE_DIR_LAST   /* not an option, never use */
+  CURLFTP_CREATE_DIR_LAST = 3 /* not an option, never use */
 } curl_ftpcreatedir;
 
 /* parameter for the CURLOPT_FTP_FILEMETHOD option */
+#define CURLFTPMETHOD_DEFAULT   0L /* let libcurl pick */
+#define CURLFTPMETHOD_MULTICWD  1L /* single CWD operation for each path
+                                      part */
+#define CURLFTPMETHOD_NOCWD     2L /* no CWD at all */
+#define CURLFTPMETHOD_SINGLECWD 3L /* one CWD to full dir, then work on file */
+
 typedef enum {
-  CURLFTPMETHOD_DEFAULT,   /* let libcurl pick */
-  CURLFTPMETHOD_MULTICWD,  /* single CWD operation for each path part */
-  CURLFTPMETHOD_NOCWD,     /* no CWD at all */
-  CURLFTPMETHOD_SINGLECWD, /* one CWD to full dir, then work on file */
-  CURLFTPMETHOD_LAST       /* not an option, never use */
+  CURLFTPMETHOD_LAST = 4 /* not an option, never use */
 } curl_ftpmethod;
 
 /* bitmask defines for CURLOPT_HEADEROPT */
-#define CURLHEADER_UNIFIED  0
-#define CURLHEADER_SEPARATE (1<<0)
+#define CURLHEADER_UNIFIED  0L
+#define CURLHEADER_SEPARATE (1L<<0)
 
 /* CURLALTSVC_* are bits for the CURLOPT_ALTSVC_CTRL option */
-#define CURLALTSVC_READONLYFILE (1<<2)
-#define CURLALTSVC_H1           (1<<3)
-#define CURLALTSVC_H2           (1<<4)
-#define CURLALTSVC_H3           (1<<5)
+#define CURLALTSVC_READONLYFILE (1L<<2)
+#define CURLALTSVC_H1           (1L<<3)
+#define CURLALTSVC_H2           (1L<<4)
+#define CURLALTSVC_H3           (1L<<5)
 
 /* bitmask values for CURLOPT_UPLOAD_FLAGS */
 #define CURLULFLAG_ANSWERED (1L<<0)
@@ -1058,42 +1067,42 @@ typedef CURLSTScode (*curl_hstswrite_callback)(CURL *easy,
                                                void *userp);
 
 /* CURLHSTS_* are bits for the CURLOPT_HSTS option */
-#define CURLHSTS_ENABLE       (long)(1<<0)
-#define CURLHSTS_READONLYFILE (long)(1<<1)
+#define CURLHSTS_ENABLE       (1L<<0)
+#define CURLHSTS_READONLYFILE (1L<<1)
 
 /* The CURLPROTO_ defines below are for the **deprecated** CURLOPT_*PROTOCOLS
    options. Do not use. */
-#define CURLPROTO_HTTP   (1<<0)
-#define CURLPROTO_HTTPS  (1<<1)
-#define CURLPROTO_FTP    (1<<2)
-#define CURLPROTO_FTPS   (1<<3)
-#define CURLPROTO_SCP    (1<<4)
-#define CURLPROTO_SFTP   (1<<5)
-#define CURLPROTO_TELNET (1<<6)
-#define CURLPROTO_LDAP   (1<<7)
-#define CURLPROTO_LDAPS  (1<<8)
-#define CURLPROTO_DICT   (1<<9)
-#define CURLPROTO_FILE   (1<<10)
-#define CURLPROTO_TFTP   (1<<11)
-#define CURLPROTO_IMAP   (1<<12)
-#define CURLPROTO_IMAPS  (1<<13)
-#define CURLPROTO_POP3   (1<<14)
-#define CURLPROTO_POP3S  (1<<15)
-#define CURLPROTO_SMTP   (1<<16)
-#define CURLPROTO_SMTPS  (1<<17)
-#define CURLPROTO_RTSP   (1<<18)
-#define CURLPROTO_RTMP   (1<<19)
-#define CURLPROTO_RTMPT  (1<<20)
-#define CURLPROTO_RTMPE  (1<<21)
-#define CURLPROTO_RTMPTE (1<<22)
-#define CURLPROTO_RTMPS  (1<<23)
-#define CURLPROTO_RTMPTS (1<<24)
-#define CURLPROTO_GOPHER (1<<25)
-#define CURLPROTO_SMB    (1<<26)
-#define CURLPROTO_SMBS   (1<<27)
-#define CURLPROTO_MQTT   (1<<28)
-#define CURLPROTO_GOPHERS (1<<29)
-#define CURLPROTO_ALL    (~0) /* enable everything */
+#define CURLPROTO_HTTP    (1L<<0)
+#define CURLPROTO_HTTPS   (1L<<1)
+#define CURLPROTO_FTP     (1L<<2)
+#define CURLPROTO_FTPS    (1L<<3)
+#define CURLPROTO_SCP     (1L<<4)
+#define CURLPROTO_SFTP    (1L<<5)
+#define CURLPROTO_TELNET  (1L<<6)
+#define CURLPROTO_LDAP    (1L<<7)
+#define CURLPROTO_LDAPS   (1L<<8)
+#define CURLPROTO_DICT    (1L<<9)
+#define CURLPROTO_FILE    (1L<<10)
+#define CURLPROTO_TFTP    (1L<<11)
+#define CURLPROTO_IMAP    (1L<<12)
+#define CURLPROTO_IMAPS   (1L<<13)
+#define CURLPROTO_POP3    (1L<<14)
+#define CURLPROTO_POP3S   (1L<<15)
+#define CURLPROTO_SMTP    (1L<<16)
+#define CURLPROTO_SMTPS   (1L<<17)
+#define CURLPROTO_RTSP    (1L<<18)
+#define CURLPROTO_RTMP    (1L<<19)
+#define CURLPROTO_RTMPT   (1L<<20)
+#define CURLPROTO_RTMPE   (1L<<21)
+#define CURLPROTO_RTMPTE  (1L<<22)
+#define CURLPROTO_RTMPS   (1L<<23)
+#define CURLPROTO_RTMPTS  (1L<<24)
+#define CURLPROTO_GOPHER  (1L<<25)
+#define CURLPROTO_SMB     (1L<<26)
+#define CURLPROTO_SMBS    (1L<<27)
+#define CURLPROTO_MQTT    (1L<<28)
+#define CURLPROTO_GOPHERS (1L<<29)
+#define CURLPROTO_ALL     (~0L) /* enable everything */
 
 /* long may be 32 or 64 bits, but we should never depend on anything else
    but 32 */
@@ -2354,18 +2363,18 @@ enum CURL_NETRC_OPTION {
   CURL_NETRC_LAST = 3
 };
 
-#define CURL_SSLVERSION_DEFAULT 0
-#define CURL_SSLVERSION_TLSv1   1 /* TLS 1.x */
-#define CURL_SSLVERSION_SSLv2   2
-#define CURL_SSLVERSION_SSLv3   3
-#define CURL_SSLVERSION_TLSv1_0 4
-#define CURL_SSLVERSION_TLSv1_1 5
-#define CURL_SSLVERSION_TLSv1_2 6
-#define CURL_SSLVERSION_TLSv1_3 7
+#define CURL_SSLVERSION_DEFAULT 0L
+#define CURL_SSLVERSION_TLSv1   1L /* TLS 1.x */
+#define CURL_SSLVERSION_SSLv2   2L
+#define CURL_SSLVERSION_SSLv3   3L
+#define CURL_SSLVERSION_TLSv1_0 4L
+#define CURL_SSLVERSION_TLSv1_1 5L
+#define CURL_SSLVERSION_TLSv1_2 6L
+#define CURL_SSLVERSION_TLSv1_3 7L
 
-#define CURL_SSLVERSION_LAST 8 /* never use, keep last */
+#define CURL_SSLVERSION_LAST    8L /* never use, keep last */
 
-#define CURL_SSLVERSION_MAX_NONE 0
+#define CURL_SSLVERSION_MAX_NONE 0L
 #define CURL_SSLVERSION_MAX_DEFAULT (CURL_SSLVERSION_TLSv1   << 16)
 #define CURL_SSLVERSION_MAX_TLSv1_0 (CURL_SSLVERSION_TLSv1_0 << 16)
 #define CURL_SSLVERSION_MAX_TLSv1_1 (CURL_SSLVERSION_TLSv1_1 << 16)
@@ -2389,10 +2398,10 @@ enum CURL_TLSAUTH {
    can be bitwise ORed so that CURL_REDIR_POST_301 | CURL_REDIR_POST_302
    | CURL_REDIR_POST_303 == CURL_REDIR_POST_ALL */
 
-#define CURL_REDIR_GET_ALL  0
-#define CURL_REDIR_POST_301 1
-#define CURL_REDIR_POST_302 2
-#define CURL_REDIR_POST_303 4
+#define CURL_REDIR_GET_ALL  0L
+#define CURL_REDIR_POST_301 1L
+#define CURL_REDIR_POST_302 2L
+#define CURL_REDIR_POST_303 4L
 #define CURL_REDIR_POST_ALL \
     (CURL_REDIR_POST_301|CURL_REDIR_POST_302|CURL_REDIR_POST_303)
 
@@ -2421,7 +2430,7 @@ typedef struct curl_mime      curl_mime;      /* Mime context. */
 typedef struct curl_mimepart  curl_mimepart;  /* Mime part context. */
 
 /* CURLMIMEOPT_ defines are for the CURLOPT_MIME_OPTIONS option. */
-#define CURLMIMEOPT_FORMESCAPE  (1<<0) /* Use backslash-escaping for forms. */
+#define CURLMIMEOPT_FORMESCAPE  (1L<<0) /* Use backslash-escaping for forms. */
 
 /*
  * NAME curl_mime_init()
@@ -2750,7 +2759,7 @@ CURL_EXTERN CURLcode curl_global_init(long flags);
  * for each application that uses libcurl. This function can be used to
  * initialize libcurl and set user defined memory management callback
  * functions. Users can implement memory management routines to check for
- * memory leaks, check for mis-use of the curl library etc. User registered
+ * memory leaks, check for misuse of the curl library etc. User registered
  * callback routines will be invoked by this library instead of the system
  * memory management routines like malloc, free etc.
  */
@@ -3302,7 +3311,7 @@ CURL_EXTERN CURLcode curl_easy_ssls_export(CURL *handle,
                                            void *userptr);
 
 
-#ifdef  __cplusplus
+#ifdef __cplusplus
 } /* end of extern "C" */
 #endif
 
@@ -3317,8 +3326,9 @@ CURL_EXTERN CURLcode curl_easy_ssls_export(CURL *handle,
 #include "mprintf.h"
 
 /* the typechecker does not work in C++ (yet) */
-#if defined(__GNUC__) && defined(__GNUC_MINOR__) && \
-    ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) && \
+#if ((defined(__GNUC__) && defined(__GNUC_MINOR__) && \
+      ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) || \
+    (defined(__clang__) && __clang_major__ >= 14)) && \
     !defined(__cplusplus) && !defined(CURL_DISABLE_TYPECHECK)
 #include "typecheck-gcc.h"
 #else

+ 3 - 4
Utilities/cmcurl/include/curl/curlver.h

@@ -32,14 +32,13 @@
 
 /* This is the version number of the libcurl package from which this header
    file origins: */
-#define LIBCURL_VERSION "8.15.0"
+#define LIBCURL_VERSION "8.16.0"
 
 /* The numeric version number is also available "in parts" by using these
    defines: */
 #define LIBCURL_VERSION_MAJOR 8
-#define LIBCURL_VERSION_MINOR 15
+#define LIBCURL_VERSION_MINOR 16
 #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
    always follow this syntax:
@@ -59,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 0x080f00
+#define LIBCURL_VERSION_NUM 0x081000
 
 /*
  * This is the date and time when the full source package was created. The

+ 2 - 2
Utilities/cmcurl/include/curl/easy.h

@@ -23,7 +23,7 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#ifdef  __cplusplus
+#ifdef __cplusplus
 extern "C" {
 #endif
 
@@ -118,7 +118,7 @@ CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer,
  */
 CURL_EXTERN CURLcode curl_easy_upkeep(CURL *curl);
 
-#ifdef  __cplusplus
+#ifdef __cplusplus
 } /* end of extern "C" */
 #endif
 

+ 1 - 1
Utilities/cmcurl/include/curl/header.h

@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 
-#ifdef  __cplusplus
+#ifdef __cplusplus
 extern "C" {
 #endif
 

+ 3 - 3
Utilities/cmcurl/include/curl/mprintf.h

@@ -28,7 +28,7 @@
 #include <stdio.h> /* needed for FILE */
 #include "curl.h"  /* for CURL_EXTERN */
 
-#ifdef  __cplusplus
+#ifdef __cplusplus
 extern "C" {
 #endif
 
@@ -38,7 +38,7 @@ extern "C" {
   defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) &&         \
   !defined(CURL_NO_FMT_CHECKS)
 #if defined(__MINGW32__) && !defined(__clang__)
-#if defined(__MINGW_PRINTF_FORMAT)  /* mingw-w64 3.0.0+. Needs stdio.h. */
+#ifdef __MINGW_PRINTF_FORMAT  /* mingw-w64 3.0.0+. Needs stdio.h. */
 #define CURL_TEMP_PRINTF(fmt, arg) \
   __attribute__((format(__MINGW_PRINTF_FORMAT, fmt, arg)))
 #else
@@ -78,7 +78,7 @@ CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args)
 
 #undef CURL_TEMP_PRINTF
 
-#ifdef  __cplusplus
+#ifdef __cplusplus
 } /* end of extern "C" */
 #endif
 

+ 45 - 1
Utilities/cmcurl/include/curl/multi.h

@@ -50,7 +50,7 @@
  */
 #include "curl.h"
 
-#ifdef  __cplusplus
+#ifdef __cplusplus
 extern "C" {
 #endif
 
@@ -395,9 +395,23 @@ typedef enum {
   /* maximum number of concurrent streams to support on a connection */
   CURLOPT(CURLMOPT_MAX_CONCURRENT_STREAMS, CURLOPTTYPE_LONG, 16),
 
+  /* network has changed, adjust caches/connection reuse */
+  CURLOPT(CURLMOPT_NETWORK_CHANGED, CURLOPTTYPE_LONG, 17),
+
   CURLMOPT_LASTENTRY /* the last unused */
 } CURLMoption;
 
+/* Definition of bits for the CURLMOPT_NETWORK_CHANGED argument: */
+
+/* - CURLMNWC_CLEAR_CONNS tells libcurl to prevent further reuse of existing
+   connections. Connections that are idle will be closed. Ongoing transfers
+   will continue with the connection they have. */
+#define CURLMNWC_CLEAR_CONNS (1L<<0)
+
+/* - CURLMNWC_CLEAR_DNS tells libcurl to prevent further reuse of existing
+   connections. Connections that are idle will be closed. Ongoing transfers
+   will continue with the connection they have. */
+#define CURLMNWC_CLEAR_DNS (1L<<0)
 
 /*
  * Name:    curl_multi_setopt()
@@ -434,6 +448,36 @@ CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle,
  */
 CURL_EXTERN CURL **curl_multi_get_handles(CURLM *multi_handle);
 
+
+typedef enum {
+  CURLMINFO_NONE, /* first, never use this */
+  /* The number of easy handles currently managed by the multi handle,
+   * e.g. have been added but not yet removed. */
+  CURLMINFO_XFERS_CURRENT = 1,
+  /* The number of easy handles running, e.g. not done and not queueing. */
+  CURLMINFO_XFERS_RUNNING = 2,
+  /* The number of easy handles waiting to start, e.g. for a connection
+   * to become available due to limits on parallelism, max connections
+   * or other factors. */
+  CURLMINFO_XFERS_PENDING = 3,
+  /* The number of easy handles finished, waiting for their results to
+   * 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_offt;
+
+/*
+ * Name:    curl_multi_get_offt()
+ *
+ * Desc:    Retrieves a numeric value for the `CURLMINFO_*` enums.
+ *
+ * Returns: CULRM_OK or error when value could not be obtained.
+ */
+CURL_EXTERN CURLMcode curl_multi_get_offt(CURLM *multi_handle,
+                                          CURLMinfo_offt info,
+                                          curl_off_t *pvalue);
+
 /*
  * Name: curl_push_callback
  *

+ 1 - 1
Utilities/cmcurl/include/curl/options.h

@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 
-#ifdef  __cplusplus
+#ifdef __cplusplus
 extern "C" {
 #endif
 

+ 19 - 13
Utilities/cmcurl/include/curl/system.h

@@ -66,7 +66,7 @@
 #  define CURL_TYPEOF_CURL_SOCKLEN_T int
 
 #elif defined(__POCC__)
-#  if defined(_MSC_VER)
+#  ifdef _MSC_VER
 #    define CURL_TYPEOF_CURL_OFF_T     __int64
 #    define CURL_FORMAT_CURL_OFF_T     "I64d"
 #    define CURL_FORMAT_CURL_OFF_TU    "I64u"
@@ -82,7 +82,7 @@
 #  define CURL_TYPEOF_CURL_SOCKLEN_T int
 
 #elif defined(__LCC__)
-#  if defined(__MCST__) /* MCST eLbrus Compiler Collection */
+#  ifdef __MCST__ /* MCST eLbrus Compiler Collection */
 #    define CURL_TYPEOF_CURL_OFF_T     long
 #    define CURL_FORMAT_CURL_OFF_T     "ld"
 #    define CURL_FORMAT_CURL_OFF_TU    "lu"
@@ -118,7 +118,7 @@
 #  define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int
 
 #elif defined(__TANDEM)
-#  if !defined(__LP64)
+#  ifndef __LP64
 #    define CURL_TYPEOF_CURL_OFF_T     long long
 #    define CURL_FORMAT_CURL_OFF_T     "lld"
 #    define CURL_FORMAT_CURL_OFF_TU    "llu"
@@ -135,7 +135,7 @@
 #  endif
 
 #elif defined(UNDER_CE)
-#  if defined(__MINGW32CE__)
+#  ifdef __MINGW32CE__
 #    define CURL_TYPEOF_CURL_OFF_T     long long
 #    define CURL_FORMAT_CURL_OFF_T     "lld"
 #    define CURL_FORMAT_CURL_OFF_TU    "llu"
@@ -162,7 +162,7 @@
 #  define CURL_PULL_SYS_TYPES_H      1
 
 #elif defined(__VMS)
-#  if defined(__VAX)
+#  ifdef __VAX
 #    define CURL_TYPEOF_CURL_OFF_T     long
 #    define CURL_FORMAT_CURL_OFF_T     "ld"
 #    define CURL_FORMAT_CURL_OFF_TU    "lu"
@@ -188,7 +188,7 @@
 #  define CURL_PULL_SYS_SOCKET_H     1
 
 #elif defined(__MVS__)
-#  if defined(_LONG_LONG)
+#  ifdef _LONG_LONG
 #    define CURL_TYPEOF_CURL_OFF_T     long long
 #    define CURL_FORMAT_CURL_OFF_T     "lld"
 #    define CURL_FORMAT_CURL_OFF_TU    "llu"
@@ -207,7 +207,7 @@
 
 #elif defined(__370__)
 #  if defined(__IBMC__) || defined(__IBMCPP__)
-#    if defined(_LONG_LONG)
+#    ifdef _LONG_LONG
 #      define CURL_TYPEOF_CURL_OFF_T     long long
 #      define CURL_FORMAT_CURL_OFF_T     "lld"
 #      define CURL_FORMAT_CURL_OFF_TU    "llu"
@@ -266,7 +266,7 @@
 #  define CURL_PULL_SYS_SOCKET_H     1
 
 #elif defined(__xlc__) /* IBM xlc compiler */
-#  if !defined(_LP64)
+#  ifndef _LP64
 #    define CURL_TYPEOF_CURL_OFF_T     long long
 #    define CURL_FORMAT_CURL_OFF_T     "lld"
 #    define CURL_FORMAT_CURL_OFF_TU    "llu"
@@ -284,7 +284,7 @@
 #  define CURL_PULL_SYS_SOCKET_H     1
 
 #elif defined(__hpux) /* HP aCC compiler */
-#  if !defined(_LP64)
+#  ifndef _LP64
 #    define CURL_TYPEOF_CURL_OFF_T     long long
 #    define CURL_FORMAT_CURL_OFF_T     "lld"
 #    define CURL_FORMAT_CURL_OFF_TU    "llu"
@@ -336,8 +336,11 @@
 #    define CURL_FORMAT_CURL_OFF_TU    "llu"
 #    define CURL_SUFFIX_CURL_OFF_T     LL
 #    define CURL_SUFFIX_CURL_OFF_TU    ULL
-#    define CURL_POPCOUNT64(x)         __builtin_popcountll(x)
-#    define CURL_CTZ64(x)              __builtin_ctzll(x)
+#    if (__GNUC__ >= 4) || \
+       ((__GNUC__ == 3) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 4))
+#      define CURL_POPCOUNT64(x)       __builtin_popcountll(x)
+#      define CURL_CTZ64(x)            __builtin_ctzll(x)
+#    endif
 #  elif defined(__LP64__) || \
         defined(__x86_64__) || defined(__ppc64__) || defined(__sparc64__) || \
         defined(__e2k__) || \
@@ -348,8 +351,11 @@
 #    define CURL_FORMAT_CURL_OFF_TU    "lu"
 #    define CURL_SUFFIX_CURL_OFF_T     L
 #    define CURL_SUFFIX_CURL_OFF_TU    UL
-#    define CURL_POPCOUNT64(x)         __builtin_popcountl(x)
-#    define CURL_CTZ64(x)              __builtin_ctzl(x)
+#    if (__GNUC__ >= 4) || \
+       ((__GNUC__ == 3) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 4))
+#      define CURL_POPCOUNT64(x)       __builtin_popcountl(x)
+#      define CURL_CTZ64(x)            __builtin_ctzl(x)
+#    endif
 #  endif
 #  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
 #  define CURL_PULL_SYS_TYPES_H      1

+ 90 - 10
Utilities/cmcurl/include/curl/typecheck-gcc.h

@@ -135,7 +135,7 @@
               _curl_easy_setopt_err_error_buffer();                     \
           if((option) == CURLOPT_CURLU)                                 \
             if(!curlcheck_ptr((value), CURLU))                          \
-              _curl_easy_setopt_err_curlu();                    \
+              _curl_easy_setopt_err_curlu();                            \
           if((option) == CURLOPT_STDERR)                                \
             if(!curlcheck_FILE(value))                                  \
               _curl_easy_setopt_err_FILE();                             \
@@ -178,7 +178,7 @@
               _curl_easy_getinfo_err_curl_slist();                      \
           if(curlcheck_tlssessioninfo_info(info))                       \
             if(!curlcheck_arr((arg), struct curl_tlssessioninfo *))     \
-              _curl_easy_getinfo_err_curl_tlssessioninfo();            \
+              _curl_easy_getinfo_err_curl_tlssessioninfo();             \
           if(curlcheck_certinfo_info(info))                             \
             if(!curlcheck_arr((arg), struct curl_certinfo *))           \
               _curl_easy_getinfo_err_curl_certinfo();                   \
@@ -193,11 +193,68 @@
       curl_easy_getinfo(handle, info, arg);                             \
     })
 
+#define curl_multi_setopt(handle, option, value)                        \
+  __extension__({                                                       \
+      if(__builtin_constant_p(option)) {                                \
+        if(curlcheck_long_option(option))                               \
+          if(!curlcheck_long(value))                                    \
+            _curl_multi_setopt_err_long();                              \
+        if(curlcheck_off_t_option(option))                              \
+          if(!curlcheck_off_t(value))                                   \
+            _curl_multi_setopt_err_curl_off_t();                        \
+        if(curlcheck_multicb_data_option(option))                       \
+          if(!curlcheck_cb_data(value))                                 \
+            _curl_multi_setopt_err_cb_data();                           \
+        if(curlcheck_charpp_option(option))                             \
+          if(!curlcheck_ptrptr(value, char))                            \
+            _curl_multi_setopt_err_charpp();                            \
+        if((option) == CURLMOPT_PUSHFUNCTION)                           \
+          if(!curlcheck_multipush_cb(value))                            \
+            _curl_multi_setopt_err_pushcb();                            \
+        if((option) == CURLMOPT_SOCKETFUNCTION)                         \
+          if(!curlcheck_multisocket_cb(value))                          \
+            _curl_multi_setopt_err_socketcb();                          \
+        if((option) == CURLMOPT_TIMERFUNCTION)                          \
+          if(!curlcheck_multitimer_cb(value))                           \
+            _curl_multi_setopt_err_timercb();                           \
+      }                                                                 \
+      curl_multi_setopt(handle, option, value);                         \
+    })
+
+/* 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_SOCKETDATA ||                                   \
+   (option) == CURLMOPT_TIMERDATA ||                                    \
+   0)
+
+/* evaluates to true if the option takes a char ** argument */
+#define curlcheck_charpp_option(option)                                 \
+  ((option) == CURLMOPT_PIPELINING_SERVER_BL ||                         \
+   (option) == CURLMOPT_PIPELINING_SITE_BL ||                           \
+   0)
+
+/* evaluates to true if expr is of type curl_multi_timer_callback */
+#define curlcheck_multitimer_cb(expr)                                   \
+  (curlcheck_NULL(expr) ||                                              \
+   curlcheck_cb_compatible((expr), curl_multi_timer_callback))
+
+/* evaluates to true if expr is of type curl_socket_callback */
+#define curlcheck_multisocket_cb(expr)                                  \
+  (curlcheck_NULL(expr) ||                                              \
+   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) ||                                            \
+   curlcheck_cb_compatible((expr), curl_push_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)
-#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param)
+
 
 /* the actual warnings, triggered by calling the _curl_easy_setopt_err*
  * functions */
@@ -208,6 +265,21 @@
   __attribute__((__unused__)) __attribute__((__noinline__))             \
   id(void) { __asm__(""); }
 
+CURLWARNING(_curl_multi_setopt_err_long,
+            "curl_multi_setopt expects a long argument")
+CURLWARNING(_curl_multi_setopt_err_curl_off_t,
+            "curl_multi_setopt expects a curl_off_t argument")
+CURLWARNING(_curl_multi_setopt_err_cb_data,
+            "curl_multi_setopt expects a 'void *' argument")
+CURLWARNING(_curl_multi_setopt_err_charpp,
+            "curl_multi_setopt expects a 'char **' argument")
+CURLWARNING(_curl_multi_setopt_err_pushcb,
+            "curl_multi_setopt expects a curl_push_callback argument")
+CURLWARNING(_curl_multi_setopt_err_socketcb,
+            "curl_multi_setopt expects a curl_socket_callback argument")
+CURLWARNING(_curl_multi_setopt_err_timercb,
+            "curl_multi_setopt expects a curl_multi_timer_callback argument")
+
 CURLWARNING(_curl_easy_setopt_err_long,
             "curl_easy_setopt expects a long argument")
 CURLWARNING(_curl_easy_setopt_err_curl_off_t,
@@ -534,6 +606,14 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
    __builtin_types_compatible_p(__typeof__(expr), type *) ||            \
    __builtin_types_compatible_p(__typeof__(expr), const type *))
 
+/* evaluates to true if expr is type**, const type** or NULL */
+#define curlcheck_ptrptr(expr, type)                                    \
+  (curlcheck_NULL(expr) ||                                              \
+   __builtin_types_compatible_p(__typeof__(expr), type **) ||           \
+   __builtin_types_compatible_p(__typeof__(expr), type *[]) ||          \
+   __builtin_types_compatible_p(__typeof__(expr), const type *[]) ||    \
+   __builtin_types_compatible_p(__typeof__(expr), const type **))
+
 /* evaluates to true if expr is one of type[], type*, NULL or const type* */
 #define curlcheck_arr(expr, type)                                       \
   (curlcheck_ptr((expr), type) ||                                       \
@@ -546,7 +626,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
    curlcheck_arr((expr), unsigned char))
 
 /* evaluates to true if expr is a CURL * */
-#define curlcheck_curl(expr)                                          \
+#define curlcheck_curl(expr)                                            \
   (curlcheck_NULL(expr) ||                                              \
    __builtin_types_compatible_p(__typeof__(expr), CURL *))
 
@@ -840,8 +920,8 @@ typedef long (*_curl_chunk_bgn_callback2)(void *, void *, int);
    curlcheck_cb_compatible((expr), curl_sshhostkeycallback))
 
 /* evaluates to true if expr is of type curl_sshkeycallback */
-#define curlcheck_ssh_key_cb(expr)                                  \
-  (curlcheck_NULL(expr) ||                                          \
+#define curlcheck_ssh_key_cb(expr)                                      \
+  (curlcheck_NULL(expr) ||                                              \
    curlcheck_cb_compatible((expr), curl_sshkeycallback))
 
 /* evaluates to true if expr is of type curl_interleave_callback */
@@ -855,13 +935,13 @@ typedef size_t (*_curl_interleave_callback2)(char *p, size_t s,
                                              size_t n, void *u);
 
 /* evaluates to true if expr is of type curl_prereq_callback */
-#define curlcheck_prereq_cb(expr)                                    \
-  (curlcheck_NULL(expr) ||                                           \
+#define curlcheck_prereq_cb(expr)                                       \
+  (curlcheck_NULL(expr) ||                                              \
    curlcheck_cb_compatible((expr), curl_prereq_callback))
 
 /* evaluates to true if expr is of type curl_trailer_callback */
-#define curlcheck_trailer_cb(expr)                                    \
-  (curlcheck_NULL(expr) ||                                            \
+#define curlcheck_trailer_cb(expr)                                      \
+  (curlcheck_NULL(expr) ||                                              \
    curlcheck_cb_compatible((expr), curl_trailer_callback))
 
 #endif /* CURLINC_TYPECHECK_GCC_H */

+ 1 - 1
Utilities/cmcurl/include/curl/urlapi.h

@@ -26,7 +26,7 @@
 
 #include "curl.h"
 
-#ifdef  __cplusplus
+#ifdef __cplusplus
 extern "C" {
 #endif
 

+ 17 - 4
Utilities/cmcurl/include/curl/websockets.h

@@ -24,7 +24,7 @@
  *
  ***************************************************************************/
 
-#ifdef  __cplusplus
+#ifdef __cplusplus
 extern "C" {
 #endif
 
@@ -72,13 +72,26 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer,
                                   curl_off_t fragsize,
                                   unsigned int flags);
 
+/*
+ * NAME curl_ws_start_frame()
+ *
+ * DESCRIPTION
+ *
+ * Buffers a websocket frame header with the given flags and length.
+ * Errors when a previous frame is not complete, e.g. not all its
+ * payload has been added.
+ */
+CURL_EXTERN CURLcode curl_ws_start_frame(CURL *curl,
+                                         unsigned int flags,
+                                         curl_off_t frame_len);
+
 /* bits for the CURLOPT_WS_OPTIONS bitmask: */
-#define CURLWS_RAW_MODE   (1<<0)
-#define CURLWS_NOAUTOPONG (1<<1)
+#define CURLWS_RAW_MODE   (1L<<0)
+#define CURLWS_NOAUTOPONG (1L<<1)
 
 CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *curl);
 
-#ifdef  __cplusplus
+#ifdef __cplusplus
 }
 #endif
 

+ 6 - 7
Utilities/cmcurl/lib/CMakeLists.txt

@@ -24,8 +24,7 @@
 
 set(LIBCURL_OUTPUT_NAME "${LIB_NAME}" CACHE STRING "Basename of the curl library")
 
-set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "BUILDING_LIBCURL")
-set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "${CURL_DEBUG_MACROS}")
+set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_DEBUG_MACROS} "BUILDING_LIBCURL")
 
 configure_file("curl_config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h")
 
@@ -95,7 +94,7 @@ if(CURL_BUILD_TESTING)
       ${CSOURCES} > "${CMAKE_CURRENT_BINARY_DIR}/unitprotos.h"
     DEPENDS "${PROJECT_SOURCE_DIR}/scripts/extract-unit-protos" ${CSOURCES}
     VERBATIM)
-  add_custom_target(curlu-unitprotos ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/unitprotos.h")
+  add_custom_target(curlu-unitprotos DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/unitprotos.h")
 endif()
 
 ## Library definition
@@ -195,11 +194,11 @@ if(BUILD_STATIC_LIBS)
   endif()
   if(CURL_HAS_LTO)
     if(CMAKE_CONFIGURATION_TYPES)
-      set_target_properties(${LIB_OBJECT} PROPERTIES
+      set_target_properties(${LIB_STATIC} PROPERTIES
         INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
         INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
     else()
-      set_target_properties(${LIB_OBJECT} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
+      set_target_properties(${LIB_STATIC} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
     endif()
   endif()
 
@@ -231,11 +230,11 @@ if(BUILD_SHARED_LIBS)
   endif()
   if(CURL_HAS_LTO)
     if(CMAKE_CONFIGURATION_TYPES)
-      set_target_properties(${LIB_OBJECT} PROPERTIES
+      set_target_properties(${LIB_SHARED} PROPERTIES
         INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
         INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
     else()
-      set_target_properties(${LIB_OBJECT} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
+      set_target_properties(${LIB_SHARED} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
     endif()
   endif()
 

+ 3 - 2
Utilities/cmcurl/lib/Makefile.inc

@@ -110,7 +110,6 @@ LIB_VTLS_HFILES =           \
   vtls/x509asn1.h
 
 LIB_VQUIC_CFILES = \
-  vquic/curl_msh3.c   \
   vquic/curl_ngtcp2.c   \
   vquic/curl_osslq.c   \
   vquic/curl_quiche.c   \
@@ -118,7 +117,6 @@ LIB_VQUIC_CFILES = \
   vquic/vquic-tls.c
 
 LIB_VQUIC_HFILES = \
-  vquic/curl_msh3.h   \
   vquic/curl_ngtcp2.h   \
   vquic/curl_osslq.h   \
   vquic/curl_quiche.h   \
@@ -148,6 +146,7 @@ LIB_CFILES =         \
   cf-h2-proxy.c      \
   cf-haproxy.c       \
   cf-https-connect.c \
+  cf-ip-happy.c      \
   cf-socket.c        \
   cfilters.c         \
   conncache.c        \
@@ -276,6 +275,7 @@ LIB_HFILES =         \
   cf-h2-proxy.h      \
   cf-haproxy.h       \
   cf-https-connect.h \
+  cf-ip-happy.h      \
   cf-socket.h        \
   cfilters.h         \
   conncache.h        \
@@ -296,6 +296,7 @@ LIB_HFILES =         \
   curl_ldap.h        \
   curl_md4.h         \
   curl_md5.h         \
+  curl_mem_undef.h   \
   curl_memory.h      \
   curl_memrchr.h     \
   curl_ntlm_core.h   \

+ 4 - 4
Utilities/cmcurl/lib/altsvc.c

@@ -187,13 +187,13 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, const char *line)
   else {
     struct altsvc *as;
     char dbuf[MAX_ALTSVC_DATELEN + 1];
-    time_t expires;
+    time_t expires = 0;
 
     /* The date parser works on a null-terminated string. The maximum length
        is upheld by curlx_str_quotedword(). */
     memcpy(dbuf, curlx_str(&date), curlx_strlen(&date));
     dbuf[curlx_strlen(&date)] = 0;
-    expires = Curl_getdate_capped(dbuf);
+    Curl_getdate_capped(dbuf, &expires);
     as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn,
                        (size_t)srcport, (size_t)dstport);
     if(as) {
@@ -260,11 +260,11 @@ static CURLcode altsvc_out(struct altsvc *as, FILE *fp)
 #ifdef USE_IPV6
   else {
     char ipv6_unused[16];
-    if(1 == curlx_inet_pton(AF_INET6, as->dst.host, ipv6_unused)) {
+    if(curlx_inet_pton(AF_INET6, as->dst.host, ipv6_unused) == 1) {
       dst6_pre = "[";
       dst6_post = "]";
     }
-    if(1 == curlx_inet_pton(AF_INET6, as->src.host, ipv6_unused)) {
+    if(curlx_inet_pton(AF_INET6, as->src.host, ipv6_unused) == 1) {
       src6_pre = "[";
       src6_post = "]";
     }

+ 62 - 49
Utilities/cmcurl/lib/asyn-ares.c

@@ -237,26 +237,6 @@ CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
   return result;
 }
 
-static void async_ares_cleanup(struct Curl_easy *data);
-
-void Curl_async_ares_shutdown(struct Curl_easy *data)
-{
-  struct async_ares_ctx *ares = &data->state.async.ares;
-  if(ares->channel)
-    ares_cancel(ares->channel);
-  async_ares_cleanup(data);
-}
-
-void Curl_async_ares_destroy(struct Curl_easy *data)
-{
-  struct async_ares_ctx *ares = &data->state.async.ares;
-  Curl_async_ares_shutdown(data);
-  if(ares->channel) {
-    ares_destroy(ares->channel);
-    ares->channel = NULL;
-  }
-}
-
 /*
  * async_ares_cleanup() cleans up async resolver data.
  */
@@ -272,16 +252,37 @@ static void async_ares_cleanup(struct Curl_easy *data)
 #endif
 }
 
+void Curl_async_ares_shutdown(struct Curl_easy *data)
+{
+  /* c-ares has a method to "cancel" operations on a channel, but
+   * as reported in #18216, this does not totally reset the channel
+   * and ares may get stuck.
+   * We need to destroy the channel and on demand create a new
+   * one to avoid that. */
+  Curl_async_ares_destroy(data);
+}
+
+void Curl_async_ares_destroy(struct Curl_easy *data)
+{
+  struct async_ares_ctx *ares = &data->state.async.ares;
+  if(ares->channel) {
+    ares_destroy(ares->channel);
+    ares->channel = NULL;
+  }
+  async_ares_cleanup(data);
+}
+
 /*
- * Curl_async_getsock() is called when someone from the outside world
+ * Curl_async_pollset() is called when someone from the outside world
  * (using curl_multi_fdset()) wants to get our fd_set setup.
  */
 
-int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks)
+CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps)
 {
   struct async_ares_ctx *ares = &data->state.async.ares;
-  DEBUGASSERT(ares->channel);
-  return Curl_ares_getsock(data, ares->channel, socks);
+  if(ares->channel)
+    return Curl_ares_pollset(data, ares->channel, ps);
+  return CURLE_OK;
 }
 
 /*
@@ -337,28 +338,33 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
     Curl_resolv_unlink(data, &data->state.async.dns);
     data->state.async.done = TRUE;
     result = ares->result;
-    if(ares->last_status == CURL_ASYNC_SUCCESS && !result) {
+    if(ares->ares_status == ARES_SUCCESS && !result) {
       data->state.async.dns =
         Curl_dnscache_mk_entry(data, ares->temp_ai,
                                data->state.async.hostname, 0,
                                data->state.async.port, FALSE);
       ares->temp_ai = NULL; /* temp_ai now owned by entry */
 #ifdef HTTPSRR_WORKS
-        if(data->state.async.dns) {
-          struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo);
-          if(!lhrr)
-            result = CURLE_OUT_OF_MEMORY;
-          else
-            data->state.async.dns->hinfo = lhrr;
-        }
+      if(data->state.async.dns) {
+        struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo);
+        if(!lhrr)
+          result = CURLE_OUT_OF_MEMORY;
+        else
+          data->state.async.dns->hinfo = lhrr;
+      }
 #endif
       if(!result && data->state.async.dns)
         result = Curl_dnscache_add(data, data->state.async.dns);
     }
     /* if we have not found anything, report the proper
      * CURLE_COULDNT_RESOLVE_* code */
-    if(!result && !data->state.async.dns)
-      result = Curl_resolver_error(data);
+    if(!result && !data->state.async.dns) {
+      const char *msg = NULL;
+      if(ares->ares_status != ARES_SUCCESS)
+        msg = ares_strerror(ares->ares_status);
+      result = Curl_resolver_error(data, msg);
+    }
+
     if(result)
       Curl_resolv_unlink(data, &data->state.async.dns);
     *dns = data->state.async.dns;
@@ -511,14 +517,14 @@ static void async_ares_hostbyname_cb(void *user_data,
        be valid so only defer it when we know the 'status' says its fine! */
     return;
 
-  if(CURL_ASYNC_SUCCESS == status) {
-    ares->last_status = status; /* one success overrules any error */
+  if(ARES_SUCCESS == status) {
+    ares->ares_status = status; /* one success overrules any error */
     async_addr_concat(&ares->temp_ai,
       Curl_he2ai(hostent, data->state.async.port));
   }
-  else if(ares->last_status != ARES_SUCCESS) {
-    /* no success so far, remember error */
-    ares->last_status = status;
+  else if(ares->ares_status != ARES_SUCCESS) {
+    /* no success so far, remember last error */
+    ares->ares_status = status;
   }
 
   ares->num_pending--;
@@ -666,21 +672,22 @@ async_ares_node2addr(struct ares_addrinfo_node *node)
 }
 
 static void async_ares_addrinfo_cb(void *user_data, int status, int timeouts,
-                                   struct ares_addrinfo *result)
+                                   struct ares_addrinfo *ares_ai)
 {
   struct Curl_easy *data = (struct Curl_easy *)user_data;
   struct async_ares_ctx *ares = &data->state.async.ares;
   (void)timeouts;
-  CURL_TRC_DNS(data, "asyn-ares: addrinfo callback, status=%d", status);
-  if(ARES_SUCCESS == status) {
-    ares->temp_ai = async_ares_node2addr(result->nodes);
-    ares->last_status = CURL_ASYNC_SUCCESS;
-    ares_freeaddrinfo(result);
+  if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */
+    ares->ares_status = status;
+  if(status == ARES_SUCCESS) {
+    ares->temp_ai = async_ares_node2addr(ares_ai->nodes);
+    ares_freeaddrinfo(ares_ai);
   }
   ares->num_pending--;
-  CURL_TRC_DNS(data, "ares: addrinfo done, status=%d, pending=%d, "
-               "addr=%sfound",
-               status, ares->num_pending, ares->temp_ai ? "" : "not ");
+  CURL_TRC_DNS(data, "ares: addrinfo done, query status=%d, "
+               "overall status=%d, pending=%d, addr=%sfound",
+               status, ares->ares_status, ares->num_pending,
+               ares->temp_ai ? "" : "not ");
 }
 
 #endif
@@ -736,7 +743,13 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
     return NULL;
 
   /* initial status - failed */
-  ares->last_status = ARES_ENOTFOUND;
+  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));
+#endif
 
 #ifdef HAVE_CARES_GETADDRINFO
   {

+ 35 - 9
Utilities/cmcurl/lib/asyn-base.c

@@ -70,7 +70,7 @@
 #endif
 
 /*
- * Curl_ares_getsock() is called when the outside world (using
+ * Curl_ares_pollset() is called when the outside world (using
  * curl_multi_fdset()) wants to get our fd_set setup and we are talking with
  * ares. The caller must make sure that this function is only called when we
  * have a working ares channel.
@@ -78,18 +78,44 @@
  * Returns: sockets-in-use-bitmap
  */
 
-int Curl_ares_getsock(struct Curl_easy *data,
-                      ares_channel channel,
-                      curl_socket_t *socks)
+
+CURLcode Curl_ares_pollset(struct Curl_easy *data,
+                           ares_channel channel,
+                           struct easy_pollset *ps)
 {
   struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 };
   struct timeval timebuf;
-  int max = ares_getsock(channel,
-                         (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
-  struct timeval *timeout = ares_timeout(channel, &maxtime, &timebuf);
-  timediff_t milli = curlx_tvtoms(timeout);
+  curl_socket_t sockets[16];  /* ARES documented limit */
+  unsigned int bitmap, i;
+  struct timeval *timeout;
+  timediff_t milli;
+  CURLcode result = CURLE_OK;
+
+  DEBUGASSERT(channel);
+  if(!channel)
+    return CURLE_FAILED_INIT;
+
+  bitmap = ares_getsock(channel, (ares_socket_t *)sockets,
+                        CURL_ARRAYSIZE(sockets));
+  for(i = 0; i < CURL_ARRAYSIZE(sockets); ++i) {
+    int flags = 0;
+    if(ARES_GETSOCK_READABLE(bitmap, i))
+      flags |= CURL_POLL_IN;
+    if(ARES_GETSOCK_WRITABLE(bitmap, i))
+      flags |= CURL_POLL_OUT;
+    if(!flags)
+      break;
+    result = Curl_pollset_change(data, ps, sockets[i], flags, 0);
+    if(result)
+      return result;
+  }
+
+  timeout = ares_timeout(channel, &maxtime, &timebuf);
+  if(!timeout)
+    timeout = &maxtime;
+  milli = curlx_tvtoms(timeout);
   Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
-  return max;
+  return result;
 }
 
 /*

+ 230 - 162
Utilities/cmcurl/lib/asyn-thrdd.c

@@ -63,6 +63,7 @@
 #include "url.h"
 #include "multiif.h"
 #include "curl_threads.h"
+#include "select.h"
 #include "strdup.h"
 
 #ifdef USE_ARES
@@ -106,6 +107,7 @@ void Curl_async_global_cleanup(void)
 }
 
 static void async_thrdd_destroy(struct Curl_easy *);
+static void async_thrdd_shutdown(struct Curl_easy *);
 
 CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
 {
@@ -114,33 +116,46 @@ CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
   return CURLE_OK;
 }
 
-/* Destroy context of threaded resolver */
-static void addr_ctx_destroy(struct async_thrdd_addr_ctx *addr_ctx)
+/* Give up reference to add_ctx */
+static void addr_ctx_unlink(struct async_thrdd_addr_ctx **paddr_ctx,
+                            struct Curl_easy *data)
 {
-  if(addr_ctx) {
-    DEBUGASSERT(!addr_ctx->ref_count);
+  struct async_thrdd_addr_ctx *addr_ctx = *paddr_ctx;
+  bool destroy;
+
+  (void)data;
+  if(!addr_ctx)
+    return;
+
+  Curl_mutex_acquire(&addr_ctx->mutx);
+  if(!data)  /* called by resolving thread */
+    addr_ctx->thrd_done = TRUE;
+
+  DEBUGASSERT(addr_ctx->ref_count);
+  --addr_ctx->ref_count;
+  destroy = !addr_ctx->ref_count;
+  Curl_mutex_release(&addr_ctx->mutx);
+
+  if(destroy) {
     Curl_mutex_destroy(&addr_ctx->mutx);
     free(addr_ctx->hostname);
     if(addr_ctx->res)
       Curl_freeaddrinfo(addr_ctx->res);
 #ifndef CURL_DISABLE_SOCKETPAIR
-  /*
-   * close one end of the socket pair (may be done in resolver thread);
-   * the other end (for reading) is always closed in the parent thread.
-   */
 #ifndef USE_EVENTFD
-  if(addr_ctx->sock_pair[1] != CURL_SOCKET_BAD) {
     wakeup_close(addr_ctx->sock_pair[1]);
-  }
 #endif
+    wakeup_close(addr_ctx->sock_pair[0]);
 #endif
     free(addr_ctx);
   }
+  *paddr_ctx = NULL;
 }
 
 /* Initialize context for threaded resolver */
 static struct async_thrdd_addr_ctx *
-addr_ctx_create(const char *hostname, int port,
+addr_ctx_create(struct Curl_easy *data,
+                const char *hostname, int port,
                 const struct addrinfo *hints)
 {
   struct async_thrdd_addr_ctx *addr_ctx = calloc(1, sizeof(*addr_ctx));
@@ -149,17 +164,13 @@ addr_ctx_create(const char *hostname, int port,
 
   addr_ctx->thread_hnd = curl_thread_t_null;
   addr_ctx->port = port;
-#ifndef CURL_DISABLE_SOCKETPAIR
-  addr_ctx->sock_pair[0] = CURL_SOCKET_BAD;
-  addr_ctx->sock_pair[1] = CURL_SOCKET_BAD;
-#endif
-  addr_ctx->ref_count = 0;
+  addr_ctx->ref_count = 1;
 
 #ifdef HAVE_GETADDRINFO
   DEBUGASSERT(hints);
   addr_ctx->hints = *hints;
 #else
-  (void) hints;
+  (void)hints;
 #endif
 
   Curl_mutex_init(&addr_ctx->mutx);
@@ -172,7 +183,7 @@ addr_ctx_create(const char *hostname, int port,
     goto err_exit;
   }
 #endif
-  addr_ctx->sock_error = CURL_ASYNC_SUCCESS;
+  addr_ctx->sock_error = 0;
 
   /* Copying hostname string because original can be destroyed by parent
    * thread during gethostbyname execution.
@@ -181,20 +192,21 @@ addr_ctx_create(const char *hostname, int port,
   if(!addr_ctx->hostname)
     goto err_exit;
 
-  addr_ctx->ref_count = 1;
   return addr_ctx;
 
 err_exit:
-#ifndef CURL_DISABLE_SOCKETPAIR
-  if(addr_ctx->sock_pair[0] != CURL_SOCKET_BAD) {
-    wakeup_close(addr_ctx->sock_pair[0]);
-    addr_ctx->sock_pair[0] = CURL_SOCKET_BAD;
-  }
-#endif
-  addr_ctx_destroy(addr_ctx);
+  addr_ctx_unlink(&addr_ctx, data);
   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
 
 /*
@@ -203,58 +215,71 @@ err_exit:
  * For builds without ARES, but with USE_IPV6, create a resolver thread
  * and wait on it.
  */
-static
-#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
-DWORD
-#else
-unsigned int
-#endif
-CURL_STDCALL getaddrinfo_thread(void *arg)
+static CURL_THREAD_RETURN_T CURL_STDCALL getaddrinfo_thread(void *arg)
 {
   struct async_thrdd_addr_ctx *addr_ctx = arg;
-  char service[12];
-  int rc;
-  bool all_gone;
+  bool do_abort;
 
-  msnprintf(service, sizeof(service), "%d", addr_ctx->port);
+/* 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
 
-  rc = Curl_getaddrinfo_ex(addr_ctx->hostname, service,
-                           &addr_ctx->hints, &addr_ctx->res);
-
-  if(rc) {
-    addr_ctx->sock_error = SOCKERRNO ? SOCKERRNO : rc;
-    if(addr_ctx->sock_error == 0)
-      addr_ctx->sock_error = RESOLVER_ENOMEM;
-  }
-  else {
-    Curl_addrinfo_set_port(addr_ctx->res, addr_ctx->port);
-  }
+  Curl_thread_push_cleanup(async_thrd_cleanup, addr_ctx);
 
   Curl_mutex_acquire(&addr_ctx->mutx);
-  if(addr_ctx->ref_count > 1) {
-    /* Someone still waiting on our results. */
+  do_abort = addr_ctx->do_abort;
+  Curl_mutex_release(&addr_ctx->mutx);
+
+  if(!do_abort) {
+    char service[12];
+    int rc;
+
+#ifdef DEBUGBUILD
+    Curl_resolve_test_delay();
+#endif
+    msnprintf(service, sizeof(service), "%d", addr_ctx->port);
+
+    rc = Curl_getaddrinfo_ex(addr_ctx->hostname, service,
+                             &addr_ctx->hints, &addr_ctx->res);
+
+    if(rc) {
+      addr_ctx->sock_error = SOCKERRNO ? SOCKERRNO : rc;
+      if(addr_ctx->sock_error == 0)
+        addr_ctx->sock_error = RESOLVER_ENOMEM;
+    }
+    else {
+      Curl_addrinfo_set_port(addr_ctx->res, addr_ctx->port);
+    }
+
+    Curl_mutex_acquire(&addr_ctx->mutx);
+    do_abort = addr_ctx->do_abort;
+    Curl_mutex_release(&addr_ctx->mutx);
 #ifndef CURL_DISABLE_SOCKETPAIR
-    if(addr_ctx->sock_pair[1] != CURL_SOCKET_BAD) {
+    if(!do_abort) {
 #ifdef USE_EVENTFD
       const uint64_t buf[1] = { 1 };
 #else
       const char buf[1] = { 1 };
 #endif
-      /* DNS has been resolved, signal client task */
+      /* Thread is done, notify transfer */
       if(wakeup_write(addr_ctx->sock_pair[1], buf, sizeof(buf)) < 0) {
-        /* update sock_erro to errno */
+        /* update sock_error to errno */
         addr_ctx->sock_error = SOCKERRNO;
       }
     }
 #endif
+
   }
-  /* thread gives up its reference to the shared data now. */
-  --addr_ctx->ref_count;
-  all_gone = !addr_ctx->ref_count;
-  Curl_mutex_release(&addr_ctx->mutx);
-  if(all_gone)
-    addr_ctx_destroy(addr_ctx);
 
+  Curl_thread_pop_cleanup();
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+  addr_ctx_unlink(&addr_ctx, NULL);
   return 0;
 }
 
@@ -263,33 +288,61 @@ CURL_STDCALL getaddrinfo_thread(void *arg)
 /*
  * gethostbyname_thread() resolves a name and then exits.
  */
-static
-#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
-DWORD
-#else
-unsigned int
-#endif
-CURL_STDCALL gethostbyname_thread(void *arg)
+static CURL_THREAD_RETURN_T CURL_STDCALL gethostbyname_thread(void *arg)
 {
   struct async_thrdd_addr_ctx *addr_ctx = arg;
-  bool all_gone;
+  bool do_abort;
 
-  addr_ctx->res = Curl_ipv4_resolve_r(addr_ctx->hostname, addr_ctx->port);
+/* 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
 
-  if(!addr_ctx->res) {
-    addr_ctx->sock_error = SOCKERRNO;
-    if(addr_ctx->sock_error == 0)
-      addr_ctx->sock_error = RESOLVER_ENOMEM;
-  }
+  Curl_thread_push_cleanup(async_thrd_cleanup, addr_ctx);
 
   Curl_mutex_acquire(&addr_ctx->mutx);
-  /* thread gives up its reference to the shared data now. */
-  --addr_ctx->ref_count;
-  all_gone = !addr_ctx->ref_count;;
+  do_abort = addr_ctx->do_abort;
   Curl_mutex_release(&addr_ctx->mutx);
-  if(all_gone)
-    addr_ctx_destroy(addr_ctx);
 
+  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;
+      if(addr_ctx->sock_error == 0)
+        addr_ctx->sock_error = RESOLVER_ENOMEM;
+    }
+
+    Curl_mutex_acquire(&addr_ctx->mutx);
+    do_abort = addr_ctx->do_abort;
+    Curl_mutex_release(&addr_ctx->mutx);
+#ifndef CURL_DISABLE_SOCKETPAIR
+    if(!do_abort) {
+#ifdef USE_EVENTFD
+      const uint64_t buf[1] = { 1 };
+#else
+      const char buf[1] = { 1 };
+#endif
+      /* Thread is done, notify transfer */
+      if(wakeup_write(addr_ctx->sock_pair[1], buf, sizeof(buf)) < 0) {
+        /* update sock_error to errno */
+        addr_ctx->sock_error = SOCKERRNO;
+      }
+    }
+#endif
+  }
+
+  Curl_thread_pop_cleanup();
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#endif
+
+  async_thrd_cleanup(addr_ctx);
   return 0;
 }
 
@@ -302,6 +355,7 @@ static void async_thrdd_destroy(struct Curl_easy *data)
 {
   struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
   struct async_thrdd_addr_ctx *addr = thrdd->addr;
+
 #ifdef USE_HTTPSRR_ARES
   if(thrdd->rr.channel) {
     ares_destroy(thrdd->rr.channel);
@@ -310,46 +364,30 @@ static void async_thrdd_destroy(struct Curl_easy *data)
   Curl_httpsrr_cleanup(&thrdd->rr.hinfo);
 #endif
 
-  if(addr) {
-#ifndef CURL_DISABLE_SOCKETPAIR
-    curl_socket_t sock_rd = addr->sock_pair[0];
-#endif
+  if(thrdd->addr && (thrdd->addr->thread_hnd != curl_thread_t_null)) {
     bool done;
 
-    /* Release our reference to the data shared with the thread. */
     Curl_mutex_acquire(&addr->mutx);
-    --addr->ref_count;
-    CURL_TRC_DNS(data, "resolve, destroy async data, shared ref=%d",
-                 addr->ref_count);
-    done = !addr->ref_count;
-    /* we give up our reference to `addr`, so NULL our pointer.
-     * coverity analyses this as being a potential unsynched write,
-     * assuming two calls to this function could be invoked concurrently.
-     * Which they never are, as the transfer's side runs single-threaded. */
-    thrdd->addr = NULL;
-    if(!done) {
-      /* thread is still running. Detach the thread while mutexed, it will
-       * trigger the cleanup when it releases its reference. */
-      Curl_thread_destroy(&addr->thread_hnd);
-    }
+#ifndef CURL_DISABLE_SOCKETPAIR
+    if(!addr->do_abort)
+      Curl_multi_will_close(data, addr->sock_pair[0]);
+#endif
+    addr->do_abort = TRUE;
+    done = addr->thrd_done;
     Curl_mutex_release(&addr->mutx);
 
     if(done) {
-      /* thread has released its reference, join it and
-       * release the memory we shared with it. */
-      if(addr->thread_hnd != curl_thread_t_null)
-        Curl_thread_join(&addr->thread_hnd);
-      addr_ctx_destroy(addr);
+      Curl_thread_join(&addr->thread_hnd);
+      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. */
+      Curl_thread_destroy(&addr->thread_hnd);
+      CURL_TRC_DNS(data, "async_thrdd_destroy, thread detached");
     }
-#ifndef CURL_DISABLE_SOCKETPAIR
-    /*
-     * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
-     * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
-     */
-    Curl_multi_will_close(data, sock_rd);
-    wakeup_close(sock_rd);
-#endif
   }
+  addr_ctx_unlink(&thrdd->addr, data);
 }
 
 #ifdef USE_HTTPSRR_ARES
@@ -379,6 +417,14 @@ static CURLcode async_rr_start(struct Curl_easy *data)
     thrdd->rr.channel = NULL;
     return CURLE_FAILED_INIT;
   }
+#ifdef CURLDEBUG
+  if(getenv("CURL_DNS_SERVER")) {
+    const char *servers = getenv("CURL_DNS_SERVER");
+    status = ares_set_servers_ports_csv(thrdd->rr.channel, servers);
+    if(status)
+      return CURLE_FAILED_INIT;
+  }
+#endif
 
   memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo));
   thrdd->rr.hinfo.port = -1;
@@ -386,6 +432,7 @@ static CURLcode async_rr_start(struct Curl_easy *data)
                     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);
   return CURLE_OK;
 }
 #endif
@@ -428,29 +475,28 @@ static bool async_thrdd_init(struct Curl_easy *data,
   if(!data->state.async.hostname)
     goto err_exit;
 
-  addr_ctx = addr_ctx_create(hostname, port, hints);
+  addr_ctx = addr_ctx_create(data, hostname, port, hints);
   if(!addr_ctx)
     goto err_exit;
   thrdd->addr = addr_ctx;
 
-  Curl_mutex_acquire(&addr_ctx->mutx);
-  DEBUGASSERT(addr_ctx->ref_count == 1);
   /* passing addr_ctx to the thread adds a reference */
+  addr_ctx->ref_count = 2;
   addr_ctx->start = curlx_now();
-  ++addr_ctx->ref_count;
+
 #ifdef HAVE_GETADDRINFO
   addr_ctx->thread_hnd = Curl_thread_create(getaddrinfo_thread, addr_ctx);
 #else
   addr_ctx->thread_hnd = Curl_thread_create(gethostbyname_thread, addr_ctx);
 #endif
+
   if(addr_ctx->thread_hnd == curl_thread_t_null) {
-    /* The thread never started, remove its reference that never happened. */
-    --addr_ctx->ref_count;
+    /* The thread never started */
+    addr_ctx->ref_count = 1;
+    addr_ctx->thrd_done = TRUE;
     err = errno;
-    Curl_mutex_release(&addr_ctx->mutx);
     goto err_exit;
   }
-  Curl_mutex_release(&addr_ctx->mutx);
 
 #ifdef USE_HTTPSRR_ARES
   if(async_rr_start(data))
@@ -466,6 +512,33 @@ err_exit:
   return FALSE;
 }
 
+static void async_thrdd_shutdown(struct Curl_easy *data)
+{
+  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+  struct async_thrdd_addr_ctx *addr_ctx = thrdd->addr;
+  bool done;
+
+  if(!addr_ctx)
+    return;
+  if(addr_ctx->thread_hnd == curl_thread_t_null)
+    return;
+
+  Curl_mutex_acquire(&addr_ctx->mutx);
+#ifndef CURL_DISABLE_SOCKETPAIR
+    if(!addr_ctx->do_abort)
+      Curl_multi_will_close(data, addr_ctx->sock_pair[0]);
+#endif
+  addr_ctx->do_abort = TRUE;
+  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);
+  }
+}
+
 /*
  * 'entry' may be NULL and then no data is returned
  */
@@ -475,47 +548,44 @@ static CURLcode asyn_thrdd_await(struct Curl_easy *data,
 {
   CURLcode result = CURLE_OK;
 
-  DEBUGASSERT(addr_ctx->thread_hnd != curl_thread_t_null);
+  if(addr_ctx->thread_hnd != curl_thread_t_null) {
+    /* not interested in result? cancel, if still running... */
+    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);
+    }
 
-  CURL_TRC_DNS(data, "resolve, wait for thread to finish");
-  /* wait for the thread to resolve the name */
-  if(Curl_thread_join(&addr_ctx->thread_hnd)) {
     if(entry)
       result = Curl_async_is_resolved(data, entry);
   }
-  else
-    DEBUGASSERT(0);
 
   data->state.async.done = TRUE;
   if(entry)
     *entry = data->state.async.dns;
 
-  async_thrdd_destroy(data);
   return result;
 }
 
-
 /*
  * Until we gain a way to signal the resolver threads to stop early, we must
  * simply wait for them and ignore their results.
  */
 void Curl_async_thrdd_shutdown(struct Curl_easy *data)
 {
-  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
-
-  /* If we are still resolving, we must wait for the threads to fully clean up,
-     unfortunately. Otherwise, we can simply cancel to clean up any resolver
-     data. */
-  if(thrdd->addr && (thrdd->addr->thread_hnd != curl_thread_t_null) &&
-     !data->set.quick_exit)
-    (void)asyn_thrdd_await(data, thrdd->addr, NULL);
-  else
-    async_thrdd_destroy(data);
+  async_thrdd_shutdown(data);
 }
 
 void Curl_async_thrdd_destroy(struct Curl_easy *data)
 {
-  Curl_async_thrdd_shutdown(data);
+  struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+
+  if(thrdd->addr && !data->set.quick_exit) {
+    (void)asyn_thrdd_await(data, thrdd->addr, NULL);
+  }
+  async_thrdd_destroy(data);
 }
 
 /*
@@ -572,7 +642,7 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
     return CURLE_FAILED_INIT;
 
   Curl_mutex_acquire(&thrdd->addr->mutx);
-  done = (thrdd->addr->ref_count == 1);
+  done = thrdd->addr->thrd_done;
   Curl_mutex_release(&thrdd->addr->mutx);
 
   if(done) {
@@ -608,13 +678,13 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
     }
 
     if(!result && !data->state.async.dns)
-      result = Curl_resolver_error(data);
+      result = Curl_resolver_error(data, NULL);
     if(result)
       Curl_resolv_unlink(data, &data->state.async.dns);
     *dns = data->state.async.dns;
     CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
                  result, *dns ? "" : "not ");
-    async_thrdd_destroy(data);
+    async_thrdd_shutdown(data);
     return result;
   }
   else {
@@ -641,37 +711,35 @@ CURLcode Curl_async_is_resolved(struct Curl_easy *data,
   }
 }
 
-int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks)
+CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps)
 {
   struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
-  int ret_val = 0;
-#if !defined(CURL_DISABLE_SOCKETPAIR) || defined(USE_HTTPSRR_ARES)
-  int socketi = 0;
-#else
-  (void)socks;
+  CURLcode result = CURLE_OK;
+  bool thrd_done;
+
+#if !defined(USE_HTTPSRR_ARES) && defined(CURL_DISABLE_SOCKETPAIR)
+  (void)ps;
 #endif
 
 #ifdef USE_HTTPSRR_ARES
   if(thrdd->rr.channel) {
-    ret_val = Curl_ares_getsock(data, thrdd->rr.channel, socks);
-    for(socketi = 0; socketi < (MAX_SOCKSPEREASYHANDLE - 1); socketi++)
-      if(!ARES_GETSOCK_READABLE(ret_val, socketi) &&
-         !ARES_GETSOCK_WRITABLE(ret_val, socketi))
-        break;
+    result = Curl_ares_pollset(data, thrdd->rr.channel, ps);
+    if(result)
+      return result;
   }
 #endif
   if(!thrdd->addr)
-    return ret_val;
+    return result;
 
+  Curl_mutex_acquire(&thrdd->addr->mutx);
+  thrd_done = thrdd->addr->thrd_done;
+  Curl_mutex_release(&thrdd->addr->mutx);
+
+  if(!thrd_done) {
 #ifndef CURL_DISABLE_SOCKETPAIR
-  if(thrdd->addr) {
-    /* return read fd to client for polling the DNS resolution status */
-    socks[socketi] = thrdd->addr->sock_pair[0];
-    ret_val |= GETSOCK_READSOCK(socketi);
-  }
-  else
-#endif
-  {
+  /* return read fd to client for polling the DNS resolution status */
+    result = Curl_pollset_add_in(data, ps, thrdd->addr->sock_pair[0]);
+#else
     timediff_t milli;
     timediff_t ms = curlx_timediff(curlx_now(), thrdd->addr->start);
     if(ms < 3)
@@ -683,9 +751,9 @@ int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks)
     else
       milli = 200;
     Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
+#endif
   }
-
-  return ret_val;
+  return result;
 }
 
 #ifndef HAVE_GETADDRINFO

+ 12 - 8
Utilities/cmcurl/lib/asyn.h

@@ -37,6 +37,7 @@ struct Curl_dns_entry;
 struct addrinfo;
 struct hostent;
 struct connectdata;
+struct easy_pollset;
 
 #if defined(CURLRES_ARES) && defined(CURLRES_THREADED)
 #error cannot have both CURLRES_ARES and CURLRES_THREADED defined
@@ -70,15 +71,15 @@ void Curl_async_global_cleanup(void);
  */
 CURLcode Curl_async_get_impl(struct Curl_easy *easy, void **impl);
 
-/* Curl_async_getsock()
+/* Curl_async_pollset()
  *
- * This function is called from the Curl_multi_getsock() function.  'sock' is a
+ * This function is called from the Curl_multi_pollset() function.  'sock' is a
  * pointer to an array to hold the file descriptors, with 'numsock' being the
  * size of that array (in number of entries). This function is supposed to
  * return bitmask indicating what file descriptors (referring to array indexes
  * in the 'sock' array) to wait for, read/write.
  */
-int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *sock);
+CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps);
 
 /*
  * Curl_async_is_resolved()
@@ -127,9 +128,10 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
 /* common functions for c-ares and threaded resolver with HTTPSRR */
 #include <ares.h>
 
-int Curl_ares_getsock(struct Curl_easy *data,
-                      ares_channel channel,
-                      curl_socket_t *socks);
+CURLcode Curl_ares_pollset(struct Curl_easy *data,
+                           ares_channel channel,
+                           struct easy_pollset *ps);
+
 int Curl_ares_perform(ares_channel channel,
                       timediff_t timeout_ms);
 #endif
@@ -141,7 +143,7 @@ struct async_ares_ctx {
   int num_pending; /* number of outstanding c-ares requests */
   struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
                                     parts */
-  int last_status;
+  int ares_status; /* ARES_SUCCESS, ARES_ENOTFOUND, etc. */
   CURLcode result; /* CURLE_OK or error handling response */
 #ifndef HAVE_CARES_GETADDRINFO
   struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
@@ -191,6 +193,8 @@ struct async_thrdd_addr_ctx {
   int port;
   int sock_error;
   int ref_count;
+  BIT(thrd_done);
+  BIT(do_abort);
 };
 
 /* Context for threaded resolver */
@@ -236,7 +240,7 @@ struct doh_probes;
 
 #ifdef USE_CURL_ASYNC
 struct Curl_async {
-#ifdef CURLRES_ARES /*  */
+#ifdef CURLRES_ARES
   struct async_ares_ctx ares;
 #elif defined(CURLRES_THREADED)
   struct async_thrdd_ctx thrdd;

+ 14 - 4
Utilities/cmcurl/lib/bufq.c

@@ -149,7 +149,6 @@ static void chunk_list_free(struct buf_chunk **anchor)
 }
 
 
-
 void Curl_bufcp_init(struct bufc_pool *pool,
                      size_t chunk_size, size_t spare_max)
 {
@@ -174,6 +173,12 @@ static CURLcode bufcp_take(struct bufc_pool *pool,
     return CURLE_OK;
   }
 
+  /* Check for integer overflow before allocation */
+  if(pool->chunk_size > SIZE_MAX - sizeof(*chunk)) {
+    *pchunk = NULL;
+    return CURLE_OUT_OF_MEMORY;
+  }
+
   chunk = calloc(1, sizeof(*chunk) + pool->chunk_size);
   if(!chunk) {
     *pchunk = NULL;
@@ -302,6 +307,11 @@ static struct buf_chunk *get_spare(struct bufq *q)
     return chunk;
   }
   else {
+    /* Check for integer overflow before allocation */
+    if(q->chunk_size > SIZE_MAX - sizeof(*chunk)) {
+      return NULL;
+    }
+
     chunk = calloc(1, sizeof(*chunk) + q->chunk_size);
     if(!chunk)
       return NULL;
@@ -526,7 +536,8 @@ CURLcode Curl_bufq_write_pass(struct bufq *q,
       if(result != CURLE_AGAIN)
         /* real error, fail */
         return result;
-      if((result == CURLE_AGAIN) && *pwritten)
+      /* result == CURLE_AGAIN */
+      if(*pwritten)
         /* we did write successfully before */
         result = CURLE_OK;
       return result;
@@ -590,8 +601,7 @@ static CURLcode bufq_slurpn(struct bufq *q, size_t max_len,
       break;
     }
     else if(n == 0) {
-      /* eof */
-      result = CURLE_OK;
+      /* eof, result remains CURLE_OK */
       break;
     }
     *pnread += n;

+ 8 - 6
Utilities/cmcurl/lib/cf-h1-proxy.c

@@ -682,11 +682,12 @@ out:
   return result;
 }
 
-static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
-                                       struct Curl_easy *data,
-                                       struct easy_pollset *ps)
+static CURLcode cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
+                                           struct Curl_easy *data,
+                                           struct easy_pollset *ps)
 {
   struct h1_tunnel_state *ts = cf->ctx;
+  CURLcode result = CURLE_OK;
 
   if(!cf->connected) {
     /* If we are not connected, but the filter "below" is
@@ -698,13 +699,14 @@ static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
          response headers or if we are still sending the request, wait
          for write. */
       if(tunnel_want_send(ts))
-        Curl_pollset_set_out_only(data, ps, sock);
+        result = Curl_pollset_set_out_only(data, ps, sock);
       else
-        Curl_pollset_set_in_only(data, ps, sock);
+        result = Curl_pollset_set_in_only(data, ps, sock);
     }
     else
-      Curl_pollset_set_out_only(data, ps, sock);
+      result = Curl_pollset_set_out_only(data, ps, sock);
   }
+  return result;
 }
 
 static void cf_h1_proxy_destroy(struct Curl_cfilter *cf,

+ 24 - 14
Utilities/cmcurl/lib/cf-h2-proxy.c

@@ -24,7 +24,8 @@
 
 #include "curl_setup.h"
 
-#if defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY)
+#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY) && \
+  defined(USE_NGHTTP2)
 
 #include <nghttp2/nghttp2.h>
 #include "urldata.h"
@@ -40,6 +41,7 @@
 #include "http_proxy.h"
 #include "multiif.h"
 #include "sendf.h"
+#include "select.h"
 #include "cf-h2-proxy.h"
 
 /* The last 3 #include files should be in this order */
@@ -472,7 +474,7 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf,
                 Curl_bufq_len(&ctx->inbufq), result, nread);
     if(result) {
       if(result != CURLE_AGAIN) {
-        failf(data, "Failed receiving HTTP2 data");
+        failf(data, "Failed receiving HTTP2 proxy data");
         return result;
       }
       break;
@@ -539,7 +541,7 @@ static ssize_t on_session_send(nghttp2_session *h2,
   if(!nwritten)
     return NGHTTP2_ERR_WOULDBLOCK;
 
-  return (nwritten  > SSIZE_T_MAX) ?
+  return (nwritten  > SSIZE_MAX) ?
     NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nwritten;
 }
 
@@ -815,7 +817,7 @@ static ssize_t tunnel_send_callback(nghttp2_session *session,
 
   CURL_TRC_CF(data, cf, "[%d] tunnel_send_callback -> %zd",
               ts->stream_id, nread);
-  return (nread  > SSIZE_T_MAX) ?
+  return (nread  > SSIZE_MAX) ?
     NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nread;
 }
 
@@ -1201,14 +1203,15 @@ static bool cf_h2_proxy_data_pending(struct Curl_cfilter *cf,
   return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
 }
 
-static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf,
-                                       struct Curl_easy *data,
-                                       struct easy_pollset *ps)
+static CURLcode cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf,
+                                           struct Curl_easy *data,
+                                           struct easy_pollset *ps)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   struct cf_call_data save;
   curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
   bool want_recv, want_send;
+  CURLcode result = CURLE_OK;
 
   if(!cf->connected && ctx->h2) {
     want_send = nghttp2_session_want_write(ctx->h2) ||
@@ -1233,9 +1236,9 @@ static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf,
                 !Curl_bufq_is_empty(&ctx->outbufq) ||
                 !Curl_bufq_is_empty(&ctx->tunnel.sendbuf);
 
-    Curl_pollset_set(data, ps, sock, want_recv, want_send);
-    CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d",
-                want_recv, want_send);
+    result = Curl_pollset_set(data, ps, sock, want_recv, want_send);
+    CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d -> %d",
+                want_recv, want_send, result);
     CF_DATA_RESTORE(cf, save);
   }
   else if(ctx->sent_goaway && !cf->shutdown) {
@@ -1245,11 +1248,12 @@ static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf,
                 !Curl_bufq_is_empty(&ctx->outbufq) ||
                 !Curl_bufq_is_empty(&ctx->tunnel.sendbuf);
     want_recv = nghttp2_session_want_read(ctx->h2);
-    Curl_pollset_set(data, ps, sock, want_recv, want_send);
-    CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d",
-                want_recv, want_send);
+    result = Curl_pollset_set(data, ps, sock, want_recv, want_send);
+    CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d -> %d",
+                want_recv, want_send, result);
     CF_DATA_RESTORE(cf, save);
   }
+  return result;
 }
 
 static CURLcode h2_handle_tunnel_close(struct Curl_cfilter *cf,
@@ -1533,6 +1537,12 @@ static CURLcode cf_h2_proxy_query(struct Curl_cfilter *cf,
     }
     break;
   }
+  case CF_QUERY_ALPN_NEGOTIATED: {
+    const char **palpn = pres2;
+    DEBUGASSERT(palpn);
+    *palpn = NULL;
+    return CURLE_OK;
+  }
   default:
     break;
   }
@@ -1606,4 +1616,4 @@ out:
   return result;
 }
 
-#endif /* defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) */
+#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_PROXY && USE_NGHTTP2 */

+ 1 - 2
Utilities/cmcurl/lib/cf-h2-proxy.h

@@ -33,7 +33,6 @@ CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf,
 
 extern struct Curl_cftype Curl_cft_h2_proxy;
 
-
-#endif /* defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY) */
+#endif /* USE_NGHTTP2 && !CURL_DISABLE_PROXY */
 
 #endif /* HEADER_CURL_H2_PROXY_H */

+ 9 - 6
Utilities/cmcurl/lib/cf-haproxy.c

@@ -24,7 +24,7 @@
 
 #include "curl_setup.h"
 
-#if !defined(CURL_DISABLE_PROXY)
+#ifndef CURL_DISABLE_PROXY
 
 #include <curl/curl.h>
 #include "urldata.h"
@@ -32,6 +32,7 @@
 #include "cf-haproxy.h"
 #include "curl_trc.h"
 #include "multiif.h"
+#include "select.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -72,7 +73,7 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
   CURLcode result;
   const char *client_ip;
   struct ip_quadruple ipquad;
-  int is_ipv6;
+  bool is_ipv6;
 
   DEBUGASSERT(ctx);
   DEBUGASSERT(ctx->state == HAPROXY_INIT);
@@ -178,15 +179,17 @@ static void cf_haproxy_close(struct Curl_cfilter *cf,
     cf->next->cft->do_close(cf->next, data);
 }
 
-static void cf_haproxy_adjust_pollset(struct Curl_cfilter *cf,
-                                      struct Curl_easy *data,
-                                      struct easy_pollset *ps)
+static CURLcode cf_haproxy_adjust_pollset(struct Curl_cfilter *cf,
+                                          struct Curl_easy *data,
+                                          struct easy_pollset *ps)
 {
   if(cf->next->connected && !cf->connected) {
     /* If we are not connected, but the filter "below" is
      * and not waiting on something, we are sending. */
-    Curl_pollset_set_out_only(data, ps, Curl_conn_cf_get_socket(cf, data));
+    return Curl_pollset_set_out_only(
+      data, ps, Curl_conn_cf_get_socket(cf, data));
   }
+  return CURLE_OK;
 }
 
 struct Curl_cftype Curl_cft_haproxy = {

+ 1 - 1
Utilities/cmcurl/lib/cf-haproxy.h

@@ -27,7 +27,7 @@
 #include "curl_setup.h"
 #include "urldata.h"
 
-#if !defined(CURL_DISABLE_PROXY)
+#ifndef CURL_DISABLE_PROXY
 
 CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at,
                                       struct Curl_easy *data);

+ 22 - 21
Utilities/cmcurl/lib/cf-https-connect.c

@@ -24,7 +24,7 @@
 
 #include "curl_setup.h"
 
-#if !defined(CURL_DISABLE_HTTP)
+#ifndef CURL_DISABLE_HTTP
 
 #include "urldata.h"
 #include <curl/curl.h>
@@ -35,6 +35,7 @@
 #include "multiif.h"
 #include "cf-https-connect.h"
 #include "http2.h"
+#include "select.h"
 #include "vquic/vquic.h"
 
 /* The last 3 #include files should be in this order */
@@ -226,24 +227,22 @@ static CURLcode baller_connected(struct Curl_cfilter *cf,
   cf->next = winner->cf;
   winner->cf = NULL;
 
-  switch(cf->conn->alpn) {
-  case CURL_HTTP_VERSION_3:
-    break;
-  case CURL_HTTP_VERSION_2:
 #ifdef USE_NGHTTP2
+  {
     /* Using nghttp2, we add the filter "below" us, so when the conn
      * closes, we tear it down for a fresh reconnect */
-    result = Curl_http2_switch_at(cf, data);
-    if(result) {
-      ctx->state = CF_HC_FAILURE;
-      ctx->result = result;
-      return result;
+    const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data);
+    if(alpn && !strcmp("h2", alpn)) {
+      result = Curl_http2_switch_at(cf, data);
+      if(result) {
+        ctx->state = CF_HC_FAILURE;
+        ctx->result = result;
+        return result;
+      }
     }
-#endif
-    break;
-  default:
-    break;
   }
+#endif
+
   ctx->state = CF_HC_SUCCESS;
   cf->connected = TRUE;
   return result;
@@ -428,22 +427,24 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf,
   return result;
 }
 
-static void cf_hc_adjust_pollset(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data,
-                                 struct easy_pollset *ps)
+static CURLcode cf_hc_adjust_pollset(struct Curl_cfilter *cf,
+                                     struct Curl_easy *data,
+                                     struct easy_pollset *ps)
 {
+  CURLcode result = CURLE_OK;
   if(!cf->connected) {
     struct cf_hc_ctx *ctx = cf->ctx;
     size_t i;
 
-    for(i = 0; i < ctx->baller_count; i++) {
+    for(i = 0; (i < ctx->baller_count) && !result; i++) {
       struct cf_hc_baller *b = &ctx->ballers[i];
       if(!cf_hc_baller_is_active(b))
         continue;
-      Curl_conn_cf_adjust_pollset(b->cf, data, ps);
+      result = Curl_conn_cf_adjust_pollset(b->cf, data, ps);
     }
-    CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num);
+    CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %d socks", result, ps->n);
   }
+  return result;
 }
 
 static bool cf_hc_data_pending(struct Curl_cfilter *cf,
@@ -749,4 +750,4 @@ out:
   return result;
 }
 
-#endif /* !defined(CURL_DISABLE_HTTP) */
+#endif /* !CURL_DISABLE_HTTP */

+ 2 - 8
Utilities/cmcurl/lib/cf-https-connect.h

@@ -25,7 +25,7 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
-#if !defined(CURL_DISABLE_HTTP)
+#ifndef CURL_DISABLE_HTTP
 
 struct Curl_cfilter;
 struct Curl_easy;
@@ -40,16 +40,10 @@ CURLcode Curl_cf_http_connect_add(struct Curl_easy *data,
                                   int sockindex,
                                   bool try_h3, bool try_h21);
 
-CURLcode
-Curl_cf_http_connect_insert_after(struct Curl_cfilter *cf_at,
-                                  struct Curl_easy *data,
-                                  bool try_h3, bool try_h21);
-
-
 CURLcode Curl_cf_https_setup(struct Curl_easy *data,
                              struct connectdata *conn,
                              int sockindex);
 
 
-#endif /* !defined(CURL_DISABLE_HTTP) */
+#endif /* !CURL_DISABLE_HTTP */
 #endif /* HEADER_CURL_CF_HTTP_H */

+ 951 - 0
Utilities/cmcurl/lib/cf-ip-happy.c

@@ -0,0 +1,951 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  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_NETINET_IN_H
+#include <netinet/in.h> /* <netinet/tcp.h> may need it */
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h> /* for sockaddr_un */
+#endif
+#ifdef HAVE_LINUX_TCP_H
+#include <linux/tcp.h>
+#elif defined(HAVE_NETINET_TCP_H)
+#include <netinet/tcp.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+#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
+
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#include "urldata.h"
+#include "connect.h"
+#include "cfilters.h"
+#include "cf-ip-happy.h"
+#include "curl_trc.h"
+#include "multiif.h"
+#include "progress.h"
+#include "select.h"
+#include "vquic/vquic.h" /* for quic cfilters */
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+struct transport_provider {
+  int transport;
+  cf_ip_connect_create *cf_create;
+};
+
+static
+#ifndef UNITTESTS
+const
+#endif
+struct transport_provider transport_providers[] = {
+  { TRNSPRT_TCP, Curl_cf_tcp_create },
+#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3)
+  { TRNSPRT_QUIC, Curl_cf_quic_create },
+#endif
+#ifndef CURL_DISABLE_TFTP
+  { TRNSPRT_UDP, Curl_cf_udp_create },
+#endif
+#ifdef USE_UNIX_SOCKETS
+  { TRNSPRT_UNIX, Curl_cf_unix_create },
+#endif
+};
+
+static cf_ip_connect_create *get_cf_create(int transport)
+{
+  size_t i;
+  for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
+    if(transport == transport_providers[i].transport)
+      return transport_providers[i].cf_create;
+  }
+  return NULL;
+}
+
+#ifdef UNITTESTS
+/* used by unit2600.c */
+void Curl_debug_set_transport_provider(int transport,
+                                       cf_ip_connect_create *cf_create)
+{
+  size_t i;
+  for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
+    if(transport == transport_providers[i].transport) {
+      transport_providers[i].cf_create = cf_create;
+      return;
+    }
+  }
+}
+#endif /* UNITTESTS */
+
+
+struct cf_ai_iter {
+  const struct Curl_addrinfo *head;
+  const struct Curl_addrinfo *last;
+  int ai_family;
+  int n;
+};
+
+static void cf_ai_iter_init(struct cf_ai_iter *iter,
+                            const struct Curl_addrinfo *list,
+                            int ai_family)
+{
+  iter->head = list;
+  iter->ai_family = ai_family;
+  iter->last = NULL;
+  iter->n = -1;
+}
+
+static const struct Curl_addrinfo *cf_ai_iter_next(struct cf_ai_iter *iter)
+{
+  const struct Curl_addrinfo *addr;
+  if(iter->n < 0) {
+    iter->n++;
+    for(addr = iter->head; addr; addr = addr->ai_next) {
+      if(addr->ai_family == iter->ai_family)
+        break;
+    }
+    iter->last = addr;
+  }
+  else if(iter->last) {
+    iter->n++;
+    for(addr = iter->last->ai_next; addr; addr = addr->ai_next) {
+      if(addr->ai_family == iter->ai_family)
+        break;
+    }
+    iter->last = addr;
+  }
+  return iter->last;
+}
+
+#ifdef USE_IPV6
+static bool cf_ai_iter_done(struct cf_ai_iter *iter)
+{
+  return (iter->n >= 0) && !iter->last;
+}
+#endif
+
+struct cf_ip_attempt {
+  struct cf_ip_attempt *next;
+  const struct Curl_addrinfo *addr;  /* List of addresses to try, not owned */
+  struct Curl_cfilter *cf;           /* current sub-cfilter connecting */
+  cf_ip_connect_create *cf_create;
+  struct curltime started;           /* start of current attempt */
+  CURLcode result;
+  int ai_family;
+  int transport;
+  int error;
+  BIT(connected);                    /* cf has connected */
+  BIT(shutdown);                     /* cf has shutdown */
+  BIT(inconclusive);                 /* connect was not a hard failure, we
+                                      * might talk to a restarting server */
+};
+
+static void cf_ip_attempt_free(struct cf_ip_attempt *a,
+                               struct Curl_easy *data)
+{
+  if(a) {
+    if(a->cf)
+      Curl_conn_cf_discard_chain(&a->cf, data);
+    free(a);
+  }
+}
+
+static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa,
+                                  struct Curl_cfilter *cf,
+                                  struct Curl_easy *data,
+                                  const struct Curl_addrinfo *addr,
+                                  int ai_family,
+                                  int transport,
+                                  cf_ip_connect_create *cf_create)
+{
+  struct Curl_cfilter *wcf;
+  struct cf_ip_attempt *a;
+  CURLcode result = CURLE_OK;
+
+  *pa = NULL;
+  a = calloc(1, sizeof(*a));
+  if(!a)
+    return CURLE_OUT_OF_MEMORY;
+
+  a->addr = addr;
+  a->ai_family = ai_family;
+  a->transport = transport;
+  a->result = CURLE_OK;
+  a->cf_create = cf_create;
+  *pa = a;
+
+  result = a->cf_create(&a->cf, data, cf->conn, a->addr, transport);
+  if(result)
+    goto out;
+
+  /* the new filter might have sub-filters */
+  for(wcf = a->cf; wcf; wcf = wcf->next) {
+    wcf->conn = cf->conn;
+    wcf->sockindex = cf->sockindex;
+  }
+
+out:
+  if(result) {
+    cf_ip_attempt_free(a, data);
+    *pa = NULL;
+  }
+  return result;
+}
+
+static CURLcode cf_ip_attempt_connect(struct cf_ip_attempt *a,
+                                      struct Curl_easy *data,
+                                      bool *connected)
+{
+  *connected = a->connected;
+  if(!a->result &&  !*connected) {
+    /* evaluate again */
+    a->result = Curl_conn_cf_connect(a->cf, data, connected);
+
+    if(!a->result) {
+      if(*connected) {
+        a->connected = TRUE;
+      }
+    }
+    else if(a->result == CURLE_WEIRD_SERVER_REPLY)
+      a->inconclusive = TRUE;
+  }
+  return a->result;
+}
+
+struct cf_ip_ballers {
+  struct cf_ip_attempt *running;
+  struct cf_ip_attempt *winner;
+  struct cf_ai_iter addr_iter;
+#ifdef USE_IPV6
+  struct cf_ai_iter ipv6_iter;
+#endif
+  cf_ip_connect_create *cf_create;   /* for creating cf */
+  struct curltime started;
+  struct curltime last_attempt_started;
+  timediff_t attempt_delay_ms;
+  int last_attempt_ai_family;
+  int transport;
+};
+
+static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a,
+                                      struct Curl_cfilter *cf,
+                                      struct Curl_easy *data)
+{
+  struct Curl_cfilter *cf_prev = a->cf;
+  struct Curl_cfilter *wcf;
+  CURLcode result;
+
+  /* When restarting, we tear down and existing filter *after* we
+   * started up the new one. This gives us a new socket number and
+   * probably a new local port. Which may prevent confusion. */
+  a->result = CURLE_OK;
+  a->connected = FALSE;
+  a->inconclusive = FALSE;
+  a->cf = NULL;
+
+  result = a->cf_create(&a->cf, data, cf->conn, a->addr, a->transport);
+  if(!result) {
+    bool dummy;
+    /* the new filter might have sub-filters */
+    for(wcf = a->cf; wcf; wcf = wcf->next) {
+      wcf->conn = cf->conn;
+      wcf->sockindex = cf->sockindex;
+    }
+    a->result = cf_ip_attempt_connect(a, data, &dummy);
+  }
+  if(cf_prev)
+    Curl_conn_cf_discard_chain(&cf_prev, data);
+  return result;
+}
+
+static void cf_ip_ballers_clear(struct Curl_cfilter *cf,
+                                struct Curl_easy *data,
+                                struct cf_ip_ballers *bs)
+{
+  (void)cf;
+  while(bs->running) {
+    struct cf_ip_attempt *a = bs->running;
+    bs->running = a->next;
+    cf_ip_attempt_free(a, data);
+  }
+  cf_ip_attempt_free(bs->winner, data);
+  bs->winner = NULL;
+}
+
+static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, int ip_version,
+                                   const struct Curl_addrinfo *addr_list,
+                                   cf_ip_connect_create *cf_create,
+                                   int transport,
+                                   timediff_t attempt_delay_ms)
+{
+  memset(bs, 0, sizeof(*bs));
+  bs->cf_create = cf_create;
+  bs->transport = transport;
+  bs->attempt_delay_ms = attempt_delay_ms;
+  bs->last_attempt_ai_family = AF_INET; /* so AF_INET6 is next */
+
+  if(transport == TRNSPRT_UNIX) {
+#ifdef USE_UNIX_SOCKETS
+    cf_ai_iter_init(&bs->addr_iter, addr_list, AF_UNIX);
+#else
+    return CURLE_UNSUPPORTED_PROTOCOL;
+#endif
+  }
+  else { /* TCP/UDP/QUIC */
+#ifdef USE_IPV6
+    if(ip_version == CURL_IPRESOLVE_V6)
+      cf_ai_iter_init(&bs->addr_iter, NULL, AF_INET);
+    else
+      cf_ai_iter_init(&bs->addr_iter, addr_list, AF_INET);
+
+    if(ip_version == CURL_IPRESOLVE_V4)
+      cf_ai_iter_init(&bs->ipv6_iter, NULL, AF_INET6);
+    else
+      cf_ai_iter_init(&bs->ipv6_iter, addr_list, AF_INET6);
+#else
+    (void)ip_version;
+    cf_ai_iter_init(&bs->addr_iter, addr_list, AF_INET);
+#endif
+  }
+  return CURLE_OK;
+}
+
+static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs,
+                                  struct Curl_cfilter *cf,
+                                  struct Curl_easy *data,
+                                  bool *connected)
+{
+  CURLcode result = CURLE_OK;
+  struct cf_ip_attempt *a = NULL, **panchor;
+  bool do_more, more_possible;
+  struct curltime now;
+  timediff_t next_expire_ms;
+  int i, inconclusive, ongoing;
+
+  if(bs->winner)
+    return CURLE_OK;
+
+evaluate:
+  now = curlx_now();
+  ongoing = inconclusive = 0;
+  more_possible = TRUE;
+
+  /* check if a running baller connects now */
+  i = -1;
+  for(panchor = &bs->running; *panchor; panchor = &((*panchor)->next)) {
+    ++i;
+    a = *panchor;
+    a->result = cf_ip_attempt_connect(a, data, connected);
+    if(!a->result) {
+      if(*connected) {
+        /* connected, declare the winner, remove from running,
+         * clear remaining running list. */
+        CURL_TRC_CF(data, cf, "connect attempt #%d successful", i);
+        bs->winner = a;
+        *panchor = a->next;
+        a->next = NULL;
+        while(bs->running) {
+          a = bs->running;
+          bs->running = a->next;
+          cf_ip_attempt_free(a, data);
+        }
+        return CURLE_OK;
+      }
+      /* still running */
+      ++ongoing;
+    }
+    else if(a->inconclusive) /* failed, but inconclusive */
+      ++inconclusive;
+  }
+  if(bs->running)
+    CURL_TRC_CF(data, cf, "checked connect attempts: "
+                "%d ongoing, %d inconclusive", ongoing, inconclusive);
+
+  /* no attempt connected yet, start another one? */
+  if(!ongoing) {
+    if(!bs->started.tv_sec && !bs->started.tv_usec)
+      bs->started = now;
+    do_more = TRUE;
+  }
+  else {
+    do_more = (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");
+  }
+
+  if(do_more) {
+    /* start the next attempt if there is another ip address to try.
+     * Alternate between address families when possible. */
+    const struct Curl_addrinfo *addr = NULL;
+    int ai_family = 0;
+#ifdef USE_IPV6
+    if((bs->last_attempt_ai_family == AF_INET) ||
+        cf_ai_iter_done(&bs->addr_iter)) {
+       addr = cf_ai_iter_next(&bs->ipv6_iter);
+       ai_family = bs->ipv6_iter.ai_family;
+    }
+#endif
+    if(!addr) {
+      addr = cf_ai_iter_next(&bs->addr_iter);
+      ai_family = bs->addr_iter.ai_family;
+    }
+
+    if(addr) {  /* try another address */
+      result = cf_ip_attempt_new(&a, cf, data, addr, ai_family,
+                                bs->transport, bs->cf_create);
+      CURL_TRC_CF(data, cf, "starting %s attempt for ipv%s -> %d",
+                  bs->running ? "next" : "first",
+                  (ai_family == AF_INET) ? "4" : "6", result);
+      if(result)
+        goto out;
+      DEBUGASSERT(a);
+
+      /* append to running list */
+      panchor = &bs->running;
+      while(*panchor)
+        panchor = &((*panchor)->next);
+      *panchor = a;
+      bs->last_attempt_started = now;
+      bs->last_attempt_ai_family = ai_family;
+      /* and run everything again */
+      goto 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");
+        i = -1;
+        for(a = bs->running; a; a = a->next) {
+          ++i;
+          if(!a->inconclusive)
+            continue;
+          result = cf_ip_attempt_restart(a, cf, data);
+          CURL_TRC_CF(data, cf, "restarted baller %d -> %d", i, result);
+          if(result) /* serious failure */
+            goto out;
+          bs->last_attempt_started = now;
+          goto evaluate;
+        }
+        DEBUGASSERT(0); /* should not come here */
+      }
+      /* attempt timeout for restart has not expired yet */
+      goto out;
+    }
+    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;
+      for(a = bs->running; a; a = a->next) {
+        CURL_TRC_CF(data, cf, "baller %d: result=%d", i, a->result);
+        if(a->result)
+          result = a->result;
+      }
+    }
+  }
+
+out:
+  if(!result) {
+    /* when do we need to be called again? */
+    next_expire_ms = Curl_timeleft(data, &now, TRUE);
+    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) {
+      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;
+}
+
+static CURLcode cf_ip_ballers_shutdown(struct cf_ip_ballers *bs,
+                                       struct Curl_easy *data,
+                                       bool *done)
+{
+  struct cf_ip_attempt *a;
+
+  /* shutdown all ballers that have not done so already. If one fails,
+   * continue shutting down others until all are shutdown. */
+  *done = TRUE;
+  for(a = bs->running; a; a = a->next) {
+    bool bdone = FALSE;
+    if(a->shutdown)
+      continue;
+    a->result = a->cf->cft->do_shutdown(a->cf, data, &bdone);
+    if(a->result || bdone)
+      a->shutdown = TRUE; /* treat a failed shutdown as done */
+    else
+      *done = FALSE;
+  }
+  return CURLE_OK;
+}
+
+static CURLcode cf_ip_ballers_pollset(struct cf_ip_ballers *bs,
+                                      struct Curl_easy *data,
+                                      struct easy_pollset *ps)
+{
+  struct cf_ip_attempt *a;
+  CURLcode result = CURLE_OK;
+  for(a = bs->running; a && !result; a = a->next) {
+    if(a->result)
+      continue;
+    result = Curl_conn_cf_adjust_pollset(a->cf, data, ps);
+  }
+  return result;
+}
+
+static bool cf_ip_ballers_pending(struct cf_ip_ballers *bs,
+                                  const struct Curl_easy *data)
+{
+  struct cf_ip_attempt *a;
+
+  for(a = bs->running; a; a = a->next) {
+    if(a->result)
+      continue;
+    if(a->cf->cft->has_data_pending(a->cf, data))
+      return TRUE;
+  }
+  return FALSE;
+}
+
+static struct curltime cf_ip_ballers_max_time(struct cf_ip_ballers *bs,
+                                              struct Curl_easy *data,
+                                              int query)
+{
+  struct curltime t, tmax;
+  struct cf_ip_attempt *a;
+
+  memset(&tmax, 0, sizeof(tmax));
+  for(a = bs->running; a; a = a->next) {
+    memset(&t, 0, sizeof(t));
+    if(!a->cf->cft->query(a->cf, data, query, NULL, &t)) {
+      if((t.tv_sec || t.tv_usec) && curlx_timediff_us(t, tmax) > 0)
+        tmax = t;
+    }
+  }
+  return tmax;
+}
+
+static int cf_ip_ballers_min_reply_ms(struct cf_ip_ballers *bs,
+                                      struct Curl_easy *data)
+{
+  int reply_ms = -1, breply_ms;
+  struct cf_ip_attempt *a;
+
+  for(a = bs->running; a; a = a->next) {
+    if(!a->cf->cft->query(a->cf, data, CF_QUERY_CONNECT_REPLY_MS,
+                          &breply_ms, NULL)) {
+      if(breply_ms >= 0 && (reply_ms < 0 || breply_ms < reply_ms))
+        reply_ms = breply_ms;
+    }
+  }
+  return reply_ms;
+}
+
+
+typedef enum {
+  SCFST_INIT,
+  SCFST_WAITING,
+  SCFST_DONE
+} cf_connect_state;
+
+struct cf_ip_happy_ctx {
+  int transport;
+  cf_ip_connect_create *cf_create;
+  cf_connect_state state;
+  struct cf_ip_ballers ballers;
+  struct curltime started;
+};
+
+
+static CURLcode is_connected(struct Curl_cfilter *cf,
+                             struct Curl_easy *data,
+                             bool *connected)
+{
+  struct cf_ip_happy_ctx *ctx = cf->ctx;
+  struct connectdata *conn = cf->conn;
+  CURLcode result;
+
+  result = cf_ip_ballers_run(&ctx->ballers, cf, data, connected);
+
+  if(!result)
+    return CURLE_OK;
+
+  {
+    const char *hostname, *proxy_name = NULL;
+    int port;
+#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;
+
+    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;
+
+    failf(data, "Failed to connect to %s port %u %s%s%safter "
+          "%" FMT_TIMEDIFF_T " ms: %s",
+          hostname, port,
+          proxy_name ? "via " : "",
+          proxy_name ? proxy_name : "",
+          proxy_name ? " " : "",
+          curlx_timediff(curlx_now(), data->progress.t_startsingle),
+          curl_easy_strerror(result));
+  }
+
+#ifdef SOCKETIMEDOUT
+  if(SOCKETIMEDOUT == data->state.os_errno)
+    result = CURLE_OPERATION_TIMEDOUT;
+#endif
+
+  return result;
+}
+
+/*
+ * Connect to the given host with timeout, proxy or remote does not matter.
+ * There might be more than one IP address to try out.
+ */
+static CURLcode start_connect(struct Curl_cfilter *cf,
+                              struct Curl_easy *data)
+{
+  struct cf_ip_happy_ctx *ctx = cf->ctx;
+  struct Curl_dns_entry *dns = data->state.dns[cf->sockindex];
+
+  if(!dns)
+    return CURLE_FAILED_INIT;
+
+  if(Curl_timeleft(data, NULL, TRUE) < 0) {
+    /* a precaution, no need to continue if time already is up */
+    failf(data, "Connection time-out");
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+
+  CURL_TRC_CF(data, cf, "init ip ballers for transport %d", ctx->transport);
+  ctx->started = curlx_now();
+  return cf_ip_ballers_init(&ctx->ballers, cf->conn->ip_version,
+                            dns->addr, ctx->cf_create, ctx->transport,
+                            data->set.happy_eyeballs_timeout);
+}
+
+static void cf_ip_happy_ctx_clear(struct Curl_cfilter *cf,
+                                  struct Curl_easy *data)
+{
+  struct cf_ip_happy_ctx *ctx = cf->ctx;
+
+  DEBUGASSERT(ctx);
+  DEBUGASSERT(data);
+  cf_ip_ballers_clear(cf, data, &ctx->ballers);
+}
+
+static CURLcode cf_ip_happy_shutdown(struct Curl_cfilter *cf,
+                                     struct Curl_easy *data,
+                                     bool *done)
+{
+  struct cf_ip_happy_ctx *ctx = cf->ctx;
+  CURLcode result = CURLE_OK;
+
+  DEBUGASSERT(data);
+  if(cf->connected) {
+    *done = TRUE;
+    return CURLE_OK;
+  }
+
+  result = cf_ip_ballers_shutdown(&ctx->ballers, data, done);
+  CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done);
+  return result;
+}
+
+static CURLcode cf_ip_happy_adjust_pollset(struct Curl_cfilter *cf,
+                                           struct Curl_easy *data,
+                                           struct easy_pollset *ps)
+{
+  struct cf_ip_happy_ctx *ctx = cf->ctx;
+  CURLcode result = CURLE_OK;
+
+  if(!cf->connected) {
+    result = cf_ip_ballers_pollset(&ctx->ballers, data, ps);
+    CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %d socks", result, ps->n);
+  }
+  return result;
+}
+
+static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf,
+                                    struct Curl_easy *data,
+                                    bool *done)
+{
+  struct cf_ip_happy_ctx *ctx = cf->ctx;
+  CURLcode result = CURLE_OK;
+
+  if(cf->connected) {
+    *done = TRUE;
+    return CURLE_OK;
+  }
+
+  DEBUGASSERT(ctx);
+  *done = FALSE;
+
+  switch(ctx->state) {
+    case SCFST_INIT:
+      DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data));
+      DEBUGASSERT(!cf->connected);
+      result = start_connect(cf, data);
+      if(result)
+        return result;
+      ctx->state = SCFST_WAITING;
+      FALLTHROUGH();
+    case SCFST_WAITING:
+      result = is_connected(cf, data, done);
+      if(!result && *done) {
+        DEBUGASSERT(ctx->ballers.winner);
+        DEBUGASSERT(ctx->ballers.winner->cf);
+        DEBUGASSERT(ctx->ballers.winner->cf->connected);
+        /* we have a winner. Install and activate it.
+         * close/free all others. */
+        ctx->state = SCFST_DONE;
+        cf->connected = TRUE;
+        cf->next = ctx->ballers.winner->cf;
+        ctx->ballers.winner->cf = NULL;
+        cf_ip_happy_ctx_clear(cf, data);
+        Curl_expire_done(data, EXPIRE_HAPPY_EYEBALLS);
+
+        if(cf->conn->handler->protocol & PROTO_FAMILY_SSH)
+          Curl_pgrsTime(data, TIMER_APPCONNECT); /* we are connected already */
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+        if(Curl_trc_cf_is_verbose(cf, data)) {
+          struct ip_quadruple ipquad;
+          bool is_ipv6;
+          if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) {
+            const char *host;
+            int port;
+            Curl_conn_get_current_host(data, cf->sockindex, &host, &port);
+            CURL_TRC_CF(data, cf, "Connected to %s (%s) port %u",
+                        host, ipquad.remote_ip, ipquad.remote_port);
+          }
+        }
+#endif
+        data->info.numconnects++; /* to track the # of connections made */
+      }
+      break;
+    case SCFST_DONE:
+      *done = TRUE;
+      break;
+  }
+  return result;
+}
+
+static void cf_ip_happy_close(struct Curl_cfilter *cf,
+                              struct Curl_easy *data)
+{
+  struct cf_ip_happy_ctx *ctx = cf->ctx;
+
+  CURL_TRC_CF(data, cf, "close");
+  cf_ip_happy_ctx_clear(cf, data);
+  cf->connected = FALSE;
+  ctx->state = SCFST_INIT;
+
+  if(cf->next) {
+    cf->next->cft->do_close(cf->next, data);
+    Curl_conn_cf_discard_chain(&cf->next, data);
+  }
+}
+
+static bool cf_ip_happy_data_pending(struct Curl_cfilter *cf,
+                                     const struct Curl_easy *data)
+{
+  struct cf_ip_happy_ctx *ctx = cf->ctx;
+
+  if(!cf->connected) {
+    return cf_ip_ballers_pending(&ctx->ballers, data);
+  }
+  return cf->next->cft->has_data_pending(cf->next, data);
+}
+
+static CURLcode cf_ip_happy_query(struct Curl_cfilter *cf,
+                                  struct Curl_easy *data,
+                                  int query, int *pres1, void *pres2)
+{
+  struct cf_ip_happy_ctx *ctx = cf->ctx;
+
+  if(!cf->connected) {
+    switch(query) {
+    case CF_QUERY_CONNECT_REPLY_MS: {
+      *pres1 = cf_ip_ballers_min_reply_ms(&ctx->ballers, data);
+      CURL_TRC_CF(data, cf, "query connect reply: %dms", *pres1);
+      return CURLE_OK;
+    }
+    case CF_QUERY_TIMER_CONNECT: {
+      struct curltime *when = pres2;
+      *when = cf_ip_ballers_max_time(&ctx->ballers, data,
+                                     CF_QUERY_TIMER_CONNECT);
+      return CURLE_OK;
+    }
+    case CF_QUERY_TIMER_APPCONNECT: {
+      struct curltime *when = pres2;
+      *when = cf_ip_ballers_max_time(&ctx->ballers, data,
+                                     CF_QUERY_TIMER_APPCONNECT);
+      return CURLE_OK;
+    }
+    default:
+      break;
+    }
+  }
+
+  return cf->next ?
+    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+    CURLE_UNKNOWN_OPTION;
+}
+
+static void cf_ip_happy_destroy(struct Curl_cfilter *cf,
+                                struct Curl_easy *data)
+{
+  struct cf_ip_happy_ctx *ctx = cf->ctx;
+
+  CURL_TRC_CF(data, cf, "destroy");
+  if(ctx) {
+    cf_ip_happy_ctx_clear(cf, data);
+  }
+  /* release any resources held in state */
+  Curl_safefree(ctx);
+}
+
+struct Curl_cftype Curl_cft_ip_happy = {
+  "HAPPY-EYEBALLS",
+  0,
+  CURL_LOG_LVL_NONE,
+  cf_ip_happy_destroy,
+  cf_ip_happy_connect,
+  cf_ip_happy_close,
+  cf_ip_happy_shutdown,
+  cf_ip_happy_adjust_pollset,
+  cf_ip_happy_data_pending,
+  Curl_cf_def_send,
+  Curl_cf_def_recv,
+  Curl_cf_def_cntrl,
+  Curl_cf_def_conn_is_alive,
+  Curl_cf_def_conn_keep_alive,
+  cf_ip_happy_query,
+};
+
+/**
+ * Create an IP happy eyeball connection filter that uses the, once resolved,
+ * address information to connect on ip families based on connection
+ * configuration.
+ * @param pcf        output, the created cfilter
+ * @param data       easy handle used in creation
+ * @param conn       connection the filter is created for
+ * @param cf_create  method to create the sub-filters performing the
+ *                   actual connects.
+ */
+static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf,
+                                   struct Curl_easy *data,
+                                   struct connectdata *conn,
+                                   cf_ip_connect_create *cf_create,
+                                   int transport)
+{
+  struct cf_ip_happy_ctx *ctx = NULL;
+  CURLcode result;
+
+  (void)data;
+  (void)conn;
+  *pcf = NULL;
+  ctx = calloc(1, sizeof(*ctx));
+  if(!ctx) {
+    result = CURLE_OUT_OF_MEMORY;
+    goto out;
+  }
+  ctx->transport = transport;
+  ctx->cf_create = cf_create;
+
+  result = Curl_cf_create(pcf, &Curl_cft_ip_happy, ctx);
+
+out:
+  if(result) {
+    Curl_safefree(*pcf);
+    free(ctx);
+  }
+  return result;
+}
+
+CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at,
+                                  struct Curl_easy *data,
+                                  int transport)
+{
+  cf_ip_connect_create *cf_create;
+  struct Curl_cfilter *cf;
+  CURLcode result;
+
+  /* Need to be first */
+  DEBUGASSERT(cf_at);
+  cf_create = get_cf_create(transport);
+  if(!cf_create) {
+    CURL_TRC_CF(data, cf_at, "unsupported transport type %d", transport);
+    return CURLE_UNSUPPORTED_PROTOCOL;
+  }
+  result = cf_ip_happy_create(&cf, data, cf_at->conn, cf_create, transport);
+  if(result)
+    return result;
+
+  Curl_conn_cf_insert_after(cf_at, cf);
+  return CURLE_OK;
+}

+ 59 - 0
Utilities/cmcurl/lib/cf-ip-happy.h

@@ -0,0 +1,59 @@
+#ifndef HEADER_CURL_IP_HAPPY_H
+#define HEADER_CURL_IP_HAPPY_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 "curlx/nonblock.h" /* for curlx_nonblock() */
+#include "sockaddr.h"
+
+/**
+ * Create a cfilter for making an "ip" connection to the
+ * given address, using parameters from `conn`. The "ip" connection
+ * can be a TCP socket, a UDP socket or even a QUIC connection.
+ *
+ * It MUST use only the supplied `ai` for its connection attempt.
+ *
+ * Such a filter may be used in "happy eyeball" scenarios, and its
+ * `connect` implementation needs to support non-blocking. Once connected,
+ * it MAY be installed in the connection filter chain to serve transfers.
+ */
+typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf,
+                                      struct Curl_easy *data,
+                                      struct connectdata *conn,
+                                      const struct Curl_addrinfo *ai,
+                                      int transport);
+
+CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at,
+                                  struct Curl_easy *data,
+                                  int transport);
+
+extern struct Curl_cftype Curl_cft_ip_happy;
+
+#ifdef UNITTESTS
+void Curl_debug_set_transport_provider(int transport,
+                                       cf_ip_connect_create *cf_create);
+#endif
+
+#endif /* HEADER_CURL_IP_HAPPY_H */

+ 31 - 32
Utilities/cmcurl/lib/cf-socket.c

@@ -109,7 +109,7 @@ static void set_ipv6_v6only(curl_socket_t sockfd, int on)
 
 static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
 {
-#if defined(TCP_NODELAY)
+#if defined(TCP_NODELAY) && defined(CURL_TCP_NODELAY_SUPPORTED)
   curl_socklen_t onoff = (curl_socklen_t) 1;
   int level = IPPROTO_TCP;
   char buffer[STRERROR_LEN];
@@ -136,7 +136,7 @@ static void nosigpipe(struct Curl_easy *data,
   (void)data;
   if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE,
                 (void *)&onoff, sizeof(onoff)) < 0) {
-#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
     char buffer[STRERROR_LEN];
     infof(data, "Could not set SO_NOSIGPIPE: %s",
           Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
@@ -164,6 +164,7 @@ static void nosigpipe(struct Curl_easy *data,
 #define KEEPALIVE_FACTOR(x)
 #endif
 
+/* Offered by mingw-w64 and MS SDK. Latter only when targeting Win7+. */
 #if defined(USE_WINSOCK) && !defined(SIO_KEEPALIVE_VALS)
 #define SIO_KEEPALIVE_VALS    _WSAIOW(IOC_VENDOR,4)
 
@@ -188,9 +189,9 @@ tcpkeepalive(struct Curl_easy *data,
           sockfd, SOCKERRNO);
   }
   else {
-#if defined(SIO_KEEPALIVE_VALS) /* Windows */
+#ifdef SIO_KEEPALIVE_VALS /* Windows */
 /* Windows 10, version 1709 (10.0.16299) and later versions */
-#if defined(CURL_WINSOCK_KEEP_SSO)
+#ifdef CURL_WINSOCK_KEEP_SSO
     optval = curlx_sltosi(data->set.tcp_keepidle);
     KEEPALIVE_FACTOR(optval);
     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
@@ -307,9 +308,9 @@ tcpkeepalive(struct Curl_easy *data,
  * Assign the address `ai` to the Curl_sockaddr_ex `dest` and
  * set the transport used.
  */
-CURLcode Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
-                               const struct Curl_addrinfo *ai,
-                               int transport)
+static CURLcode sock_assign_addr(struct Curl_sockaddr_ex *dest,
+                                 const struct Curl_addrinfo *ai,
+                                 int transport)
 {
   /*
    * The Curl_sockaddr_ex structure is basically libcurl's external API
@@ -406,7 +407,7 @@ CURLcode Curl_socket_open(struct Curl_easy *data,
     /* if the caller does not want info back, use a local temp copy */
     addr = &dummy;
 
-  result = Curl_sock_assign_addr(addr, ai, transport);
+  result = sock_assign_addr(addr, ai, transport);
   if(result)
     return result;
 
@@ -844,7 +845,7 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
 
 #endif
 
-  if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
+  if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
     err = SOCKERRNO;
 #ifdef UNDER_CE
   /* Old Windows CE versions do not support SO_ERROR */
@@ -860,7 +861,7 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
     err = 0;
   }
 #endif
-  if((0 == err) || (SOCKEISCONN == err))
+  if((err == 0) || (SOCKEISCONN == err))
     /* we are connected, awesome! */
     rc = TRUE;
   else
@@ -885,7 +886,7 @@ static CURLcode socket_connect_result(struct Curl_easy *data,
   switch(error) {
   case SOCKEINPROGRESS:
   case SOCKEWOULDBLOCK:
-#if defined(EAGAIN)
+#ifdef EAGAIN
 #if (EAGAIN) != (SOCKEWOULDBLOCK)
     /* On some platforms EAGAIN and EWOULDBLOCK are the
      * same value, and on others they are different, hence
@@ -949,7 +950,7 @@ static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx,
   ctx->sock = CURL_SOCKET_BAD;
   ctx->transport = transport;
 
-  result = Curl_sock_assign_addr(&ctx->addr, ai, transport);
+  result = sock_assign_addr(&ctx->addr, ai, transport);
   if(result)
     return result;
 
@@ -1237,8 +1238,8 @@ static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
 
   (void)data;
   if(is_tcp_fastopen) {
-#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
-#  if defined(HAVE_BUILTIN_AVAILABLE)
+#ifdef CONNECT_DATA_IDEMPOTENT /* Darwin */
+#  ifdef HAVE_BUILTIN_AVAILABLE
     /* while connectx function is available since macOS 10.11 / iOS 9,
        it did not have the interface declared correctly until
        Xcode 9 / macOS SDK 10.13 */
@@ -1375,11 +1376,12 @@ out:
   return result;
 }
 
-static void cf_socket_adjust_pollset(struct Curl_cfilter *cf,
-                                     struct Curl_easy *data,
-                                     struct easy_pollset *ps)
+static CURLcode cf_socket_adjust_pollset(struct Curl_cfilter *cf,
+                                         struct Curl_easy *data,
+                                         struct easy_pollset *ps)
 {
   struct cf_socket_ctx *ctx = cf->ctx;
+  CURLcode result = CURLE_OK;
 
   if(ctx->sock != CURL_SOCKET_BAD) {
     /* A listening socket filter needs to be connected before the accept
@@ -1387,25 +1389,27 @@ static void cf_socket_adjust_pollset(struct Curl_cfilter *cf,
      * FTP no longer does the socket checks and accept calls and delegates
      * all that to the filter. */
     if(ctx->listening) {
-      Curl_pollset_set_in_only(data, ps, ctx->sock);
+      result = Curl_pollset_set_in_only(data, ps, ctx->sock);
       CURL_TRC_CF(data, cf, "adjust_pollset, listening, POLLIN fd=%"
                   FMT_SOCKET_T, ctx->sock);
     }
     else if(!cf->connected) {
-      Curl_pollset_set_out_only(data, ps, ctx->sock);
+      result = Curl_pollset_set_out_only(data, ps, ctx->sock);
       CURL_TRC_CF(data, cf, "adjust_pollset, !connected, POLLOUT fd=%"
                   FMT_SOCKET_T, ctx->sock);
     }
     else if(!ctx->active) {
-      Curl_pollset_add_in(data, ps, ctx->sock);
+      result = Curl_pollset_add_in(data, ps, ctx->sock);
       CURL_TRC_CF(data, cf, "adjust_pollset, !active, POLLIN fd=%"
                   FMT_SOCKET_T, ctx->sock);
     }
   }
+  return result;
 }
 
 #ifdef USE_WINSOCK
 
+/* Offered by mingw-w64 v13+. MS SDK 7.0A+. */
 #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
 #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
 #endif
@@ -1440,7 +1444,7 @@ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
   size_t orig_len = len;
   CURLcode result = CURLE_OK;
 
-  (void)eos; /* unused */
+  (void)eos;
   *pnwritten = 0;
   fdsave = cf->conn->sock[cf->sockindex];
   cf->conn->sock[cf->sockindex] = ctx->sock;
@@ -1505,7 +1509,7 @@ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
   else
     *pnwritten = (size_t)nwritten;
 
-#if defined(USE_WINSOCK)
+#ifdef USE_WINSOCK
   if(!result)
     win_update_sndbuf_size(ctx);
 #endif
@@ -1600,15 +1604,10 @@ static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
   /* use this socket from now on */
   cf->conn->sock[cf->sockindex] = ctx->sock;
   set_local_ip(cf, data);
-  if(cf->sockindex == FIRSTSOCKET) {
-    cf->conn->primary = ctx->ip;
-  #ifdef USE_IPV6
+#ifdef USE_IPV6
+  if(cf->sockindex == FIRSTSOCKET)
     cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6);
-  #endif
-  }
-  else {
-    cf->conn->secondary = ctx->ip;
-  }
+#endif
   ctx->active = TRUE;
 }
 
@@ -2111,13 +2110,13 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
     return CURLE_OK;
   }
 
-  if(0 == getsockname(ctx->sock, (struct sockaddr *) &add, &size)) {
+  if(!getsockname(ctx->sock, (struct sockaddr *) &add, &size)) {
     size = sizeof(add);
 #ifdef HAVE_ACCEPT4
     s_accepted = accept4(ctx->sock, (struct sockaddr *) &add, &size,
                          SOCK_NONBLOCK | SOCK_CLOEXEC);
 #else
-    s_accepted = accept(ctx->sock, (struct sockaddr *) &add, &size);
+    s_accepted = CURL_ACCEPT(ctx->sock, (struct sockaddr *) &add, &size);
 #endif
   }
 

+ 0 - 8
Utilities/cmcurl/lib/cf-socket.h

@@ -91,14 +91,6 @@ void Curl_sndbuf_init(curl_socket_t sockfd);
 #define Curl_sndbuf_init(y) Curl_nop_stmt
 #endif
 
-/**
- * Assign the address `ai` to the Curl_sockaddr_ex `dest` and
- * set the transport used.
- */
-CURLcode Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
-                               const struct Curl_addrinfo *ai,
-                               int transport);
-
 /**
  * Creates a cfilter that opens a TCP socket to the given address
  * when calling its `connect` implementation.

+ 168 - 202
Utilities/cmcurl/lib/cfilters.c

@@ -67,14 +67,15 @@ CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf,
 static void conn_report_connect_stats(struct Curl_easy *data,
                                       struct connectdata *conn);
 
-void Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf,
-                                struct Curl_easy *data,
-                                struct easy_pollset *ps)
+CURLcode Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf,
+                                    struct Curl_easy *data,
+                                    struct easy_pollset *ps)
 {
   /* NOP */
   (void)cf;
   (void)data;
   (void)ps;
+  return CURLE_OK;
 }
 
 bool Curl_cf_def_data_pending(struct Curl_cfilter *cf,
@@ -176,6 +177,10 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done)
   struct curltime now;
 
   DEBUGASSERT(data->conn);
+
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+
   /* Get the first connected filter that is not shut down already. */
   cf = data->conn->cfilter[sockindex];
   while(cf && (!cf->connected || cf->shutdown))
@@ -439,6 +444,29 @@ CURLcode Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
   return CURLE_RECV_ERROR;
 }
 
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static CURLcode cf_verboseconnect(struct Curl_easy *data,
+                                  struct Curl_cfilter *cf)
+{
+  if(Curl_trc_is_verbose(data)) {
+    struct ip_quadruple ipquad;
+    bool is_ipv6;
+    CURLcode result;
+
+    result = Curl_conn_cf_get_ip_info(cf, data, &is_ipv6, &ipquad);
+    if(result)
+      return result;
+
+    infof(data, "Established %sconnection to %s (%s port %u) from %s port %u ",
+          (cf->sockindex == SECONDARYSOCKET) ? "2nd " : "",
+          CURL_CONN_HOST_DISPNAME(data->conn),
+          ipquad.remote_ip, ipquad.remote_port,
+          ipquad.local_ip, ipquad.local_port);
+  }
+  return CURLE_OK;
+}
+#endif
+
 CURLcode Curl_conn_connect(struct Curl_easy *data,
                            int sockindex,
                            bool blocking,
@@ -446,12 +474,15 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
 {
 #define CF_CONN_NUM_POLLS_ON_STACK 5
   struct pollfd a_few_on_stack[CF_CONN_NUM_POLLS_ON_STACK];
+  struct easy_pollset ps;
   struct curl_pollfds cpfds;
   struct Curl_cfilter *cf;
   CURLcode result = CURLE_OK;
 
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return CURLE_BAD_FUNCTION_ARGUMENT;
 
   cf = data->conn->cfilter[sockindex];
   if(!cf) {
@@ -463,6 +494,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
   if(*done)
     return CURLE_OK;
 
+  Curl_pollset_init(&ps);
   Curl_pollfds_init(&cpfds, a_few_on_stack, CF_CONN_NUM_POLLS_ON_STACK);
   while(!*done) {
     if(Curl_conn_needs_flush(data, sockindex)) {
@@ -482,7 +514,9 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
       cf_cntrl_update_info(data, data->conn);
       conn_report_connect_stats(data, data->conn);
       data->conn->keepalive = curlx_now();
-      Curl_verboseconnect(data, data->conn, sockindex);
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+      result = cf_verboseconnect(data, cf);
+#endif
       goto out;
     }
     else if(result) {
@@ -498,7 +532,6 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
       /* check allowed time left */
       const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
       curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
-      struct easy_pollset ps;
       int rc;
 
       if(timeout_ms < 0) {
@@ -509,12 +542,14 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
       }
 
       CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), do poll");
+      Curl_pollset_reset(&ps);
       Curl_pollfds_reset(&cpfds);
-      memset(&ps, 0, sizeof(ps));
       /* In general, we want to send after connect, wait on that. */
       if(sockfd != CURL_SOCKET_BAD)
         Curl_pollset_set_out_only(data, &ps, sockfd);
-      Curl_conn_adjust_pollset(data, data->conn, &ps);
+      result = Curl_conn_adjust_pollset(data, data->conn, &ps);
+      if(result)
+        goto out;
       result = Curl_pollfds_add_ps(&cpfds, &ps);
       if(result)
         goto out;
@@ -532,12 +567,15 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
   }
 
 out:
+  Curl_pollset_cleanup(&ps);
   Curl_pollfds_cleanup(&cpfds);
   return result;
 }
 
 bool Curl_conn_is_setup(struct connectdata *conn, int sockindex)
 {
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return FALSE;
   return (conn->cfilter[sockindex] != NULL);
 }
 
@@ -545,6 +583,8 @@ bool Curl_conn_is_connected(struct connectdata *conn, int sockindex)
 {
   struct Curl_cfilter *cf;
 
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return FALSE;
   cf = conn->cfilter[sockindex];
   return cf && cf->connected;
 }
@@ -553,6 +593,8 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex)
 {
   struct Curl_cfilter *cf;
 
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return FALSE;
   cf = data->conn->cfilter[sockindex];
   while(cf) {
     if(cf->connected)
@@ -564,7 +606,7 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex)
   return FALSE;
 }
 
-bool Curl_conn_cf_is_ssl(struct Curl_cfilter *cf)
+static bool cf_is_ssl(struct Curl_cfilter *cf)
 {
   for(; cf; cf = cf->next) {
     if(cf->cft->flags & CF_TYPE_SSL)
@@ -577,13 +619,17 @@ bool Curl_conn_cf_is_ssl(struct Curl_cfilter *cf)
 
 bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex)
 {
-  return conn ? Curl_conn_cf_is_ssl(conn->cfilter[sockindex]) : FALSE;
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return FALSE;
+  return conn ? cf_is_ssl(conn->cfilter[sockindex]) : FALSE;
 }
 
 bool Curl_conn_get_ssl_info(struct Curl_easy *data,
                             struct connectdata *conn, int sockindex,
                             struct curl_tlssessioninfo *info)
 {
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return FALSE;
   if(Curl_conn_is_ssl(conn, sockindex)) {
     struct Curl_cfilter *cf = conn->cfilter[sockindex];
     CURLcode result = cf ? cf->cft->query(cf, data, CF_QUERY_SSL_INFO,
@@ -593,9 +639,24 @@ bool Curl_conn_get_ssl_info(struct Curl_easy *data,
   return FALSE;
 }
 
+CURLcode Curl_conn_get_ip_info(struct Curl_easy *data,
+                               struct connectdata *conn, int sockindex,
+                               bool *is_ipv6, struct ip_quadruple *ipquad)
+{
+  struct Curl_cfilter *cf;
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  cf = conn ? conn->cfilter[sockindex] : NULL;
+  return Curl_conn_cf_get_ip_info(cf, data, is_ipv6, ipquad);
+}
+
 bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex)
 {
-  struct Curl_cfilter *cf = conn ? conn->cfilter[sockindex] : NULL;
+  struct Curl_cfilter *cf;
+
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return FALSE;
+  cf = conn ? conn->cfilter[sockindex] : NULL;
 
   for(; cf; cf = cf->next) {
     if(cf->cft->flags & CF_TYPE_MULTIPLEX)
@@ -613,6 +674,13 @@ unsigned char Curl_conn_get_transport(struct Curl_easy *data,
   return Curl_conn_cf_get_transport(cf, data);
 }
 
+const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data,
+                                          struct connectdata *conn)
+{
+  struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET];
+  return Curl_conn_cf_get_alpn_negotiated(cf, data);
+}
+
 unsigned char Curl_conn_http_version(struct Curl_easy *data,
                                      struct connectdata *conn)
 {
@@ -644,6 +712,8 @@ bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex)
   (void)data;
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return FALSE;
 
   cf = data->conn->cfilter[sockindex];
   while(cf && !cf->connected) {
@@ -667,13 +737,16 @@ bool Curl_conn_cf_needs_flush(struct Curl_cfilter *cf,
 
 bool Curl_conn_needs_flush(struct Curl_easy *data, int sockindex)
 {
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return FALSE;
   return Curl_conn_cf_needs_flush(data->conn->cfilter[sockindex], data);
 }
 
-void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data,
-                                 struct easy_pollset *ps)
+CURLcode Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf,
+                                     struct Curl_easy *data,
+                                     struct easy_pollset *ps)
 {
+  CURLcode result = CURLE_OK;
   /* Get the lowest not-connected filter, if there are any */
   while(cf && !cf->connected && cf->next && !cf->next->connected)
     cf = cf->next;
@@ -682,23 +755,26 @@ void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf,
     cf = cf->next;
   /* From there on, give all filters a chance to adjust the pollset.
    * Lower filters are called later, so they may override */
-  while(cf) {
-    cf->cft->adjust_pollset(cf, data, ps);
+  while(cf && !result) {
+    result = cf->cft->adjust_pollset(cf, data, ps);
     cf = cf->next;
   }
+  return result;
 }
 
-void Curl_conn_adjust_pollset(struct Curl_easy *data,
-                              struct connectdata *conn,
-                              struct easy_pollset *ps)
+CURLcode Curl_conn_adjust_pollset(struct Curl_easy *data,
+                                  struct connectdata *conn,
+                                  struct easy_pollset *ps)
 {
+  CURLcode result = CURLE_OK;
   int i;
 
   DEBUGASSERT(data);
   DEBUGASSERT(conn);
-  for(i = 0; i < 2; ++i) {
-    Curl_conn_cf_adjust_pollset(conn->cfilter[i], data, ps);
+  for(i = 0; (i < 2) && !result; ++i) {
+    result = Curl_conn_cf_adjust_pollset(conn->cfilter[i], data, ps);
   }
+  return result;
 }
 
 int Curl_conn_cf_poll(struct Curl_cfilter *cf,
@@ -706,35 +782,18 @@ int Curl_conn_cf_poll(struct Curl_cfilter *cf,
                       timediff_t timeout_ms)
 {
   struct easy_pollset ps;
-  struct pollfd pfds[MAX_SOCKSPEREASYHANDLE];
-  unsigned int i, npfds = 0;
+  int result;
 
   DEBUGASSERT(cf);
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
-  memset(&ps, 0, sizeof(ps));
-  memset(pfds, 0, sizeof(pfds));
-
-  Curl_conn_cf_adjust_pollset(cf, data, &ps);
-  DEBUGASSERT(ps.num <= MAX_SOCKSPEREASYHANDLE);
-  for(i = 0; i < ps.num; ++i) {
-    short events = 0;
-    if(ps.actions[i] & CURL_POLL_IN) {
-      events |= POLLIN;
-    }
-    if(ps.actions[i] & CURL_POLL_OUT) {
-      events |= POLLOUT;
-    }
-    if(events) {
-      pfds[npfds].fd = ps.sockets[i];
-      pfds[npfds].events = events;
-      ++npfds;
-    }
-  }
+  Curl_pollset_init(&ps);
 
-  if(!npfds)
-    DEBUGF(infof(data, "no sockets to poll!"));
-  return Curl_poll(pfds, npfds, timeout_ms);
+  result = Curl_conn_cf_adjust_pollset(cf, data, &ps);
+  if(!result)
+    result = Curl_pollset_poll(data, &ps, timeout_ms);
+  Curl_pollset_cleanup(&ps);
+  return result;
 }
 
 void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex,
@@ -742,8 +801,14 @@ void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex,
 {
   struct Curl_cfilter *cf, *cf_proxy = NULL;
 
-  DEBUGASSERT(data->conn);
-  cf = data->conn->cfilter[sockindex];
+  if(!data->conn) {
+    DEBUGASSERT(0);
+    *phost = "";
+    *pport = -1;
+    return;
+  }
+
+  cf = CONN_SOCK_IDX_VALID(sockindex) ? data->conn->cfilter[sockindex] : NULL;
   /* Find the "lowest" tunneling proxy filter that has not connected yet. */
   while(cf && !cf->connected) {
     if((cf->cft->flags & (CF_TYPE_IP_CONNECT|CF_TYPE_PROXY)) ==
@@ -810,6 +875,17 @@ unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf,
   return (unsigned char)(data->conn ? data->conn->transport_wanted : 0);
 }
 
+const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf,
+                                             struct Curl_easy *data)
+{
+  const char *alpn = NULL;
+  CURL_TRC_CF(data, cf, "query ALPN");
+  if(cf && !cf->cft->query(cf, data, CF_QUERY_ALPN_NEGOTIATED, NULL,
+                           CURL_UNCONST(&alpn)))
+    return alpn;
+  return NULL;
+}
+
 static const struct Curl_sockaddr_ex *
 cf_get_remote_addr(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
@@ -823,36 +899,44 @@ cf_get_remote_addr(struct Curl_cfilter *cf, struct Curl_easy *data)
 
 CURLcode Curl_conn_cf_get_ip_info(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
-                                  int *is_ipv6, struct ip_quadruple *ipquad)
+                                  bool *is_ipv6, struct ip_quadruple *ipquad)
 {
-  if(cf)
-    return cf->cft->query(cf, data, CF_QUERY_IP_INFO, is_ipv6, ipquad);
-  return CURLE_UNKNOWN_OPTION;
+  CURLcode result = CURLE_UNKNOWN_OPTION;
+  if(cf) {
+    int ipv6 = 0;
+    result = cf->cft->query(cf, data, CF_QUERY_IP_INFO, &ipv6, ipquad);
+    *is_ipv6 = !!ipv6;
+  }
+  return result;
 }
 
-curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex)
+curl_socket_t Curl_conn_get_first_socket(struct Curl_easy *data)
 {
   struct Curl_cfilter *cf;
 
-  cf = data->conn ? data->conn->cfilter[sockindex] : NULL;
+  if(!data->conn)
+    return CURL_SOCKET_BAD;
+
+  cf = data->conn->cfilter[FIRSTSOCKET];
   /* if the top filter has not connected, ask it (and its sub-filters)
-   * for the socket. Otherwise conn->sock[sockindex] should have it.
-   */
+   * for the socket. Otherwise conn->sock[sockindex] should have it. */
   if(cf && !cf->connected)
     return Curl_conn_cf_get_socket(cf, data);
-  return data->conn ? data->conn->sock[sockindex] : CURL_SOCKET_BAD;
+  return data->conn->sock[FIRSTSOCKET];
 }
 
 const struct Curl_sockaddr_ex *
 Curl_conn_get_remote_addr(struct Curl_easy *data, int sockindex)
 {
-  struct Curl_cfilter *cf = data->conn ? data->conn->cfilter[sockindex] : NULL;
+  struct Curl_cfilter *cf =
+    (data->conn && CONN_SOCK_IDX_VALID(sockindex)) ?
+    data->conn->cfilter[sockindex] : NULL;
   return cf ? cf_get_remote_addr(cf, data) : NULL;
 }
 
 void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex)
 {
-  if(data->conn) {
+  if(data->conn && CONN_SOCK_IDX_VALID(sockindex)) {
     struct Curl_cfilter *cf = data->conn->cfilter[sockindex];
     if(cf)
       (void)Curl_conn_cf_cntrl(cf, data, TRUE,
@@ -894,6 +978,8 @@ CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data)
 
 CURLcode Curl_conn_flush(struct Curl_easy *data, int sockindex)
 {
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return CURLE_BAD_FUNCTION_ARGUMENT;
   return Curl_conn_cf_cntrl(data->conn->cfilter[sockindex], data, FALSE,
                             CF_CTRL_FLUSH, 0, NULL);
 }
@@ -963,7 +1049,11 @@ CURLcode Curl_conn_keep_alive(struct Curl_easy *data,
                               struct connectdata *conn,
                               int sockindex)
 {
-  struct Curl_cfilter *cf = conn->cfilter[sockindex];
+  struct Curl_cfilter *cf;
+
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  cf = conn->cfilter[sockindex];
   return cf ? cf->cft->keep_alive(cf, data) : CURLE_OK;
 }
 
@@ -971,23 +1061,34 @@ size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
                                     struct connectdata *conn,
                                     int sockindex)
 {
+  struct Curl_cfilter *cf;
   CURLcode result;
-  int n = 0;
+  int n = -1;
 
-  struct Curl_cfilter *cf = conn->cfilter[sockindex];
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return 0;
+
+  cf = conn->cfilter[sockindex];
   result = cf ? cf->cft->query(cf, data, CF_QUERY_MAX_CONCURRENT,
                                &n, NULL) : CURLE_UNKNOWN_OPTION;
-  return (result || n <= 0) ? 1 : (size_t)n;
+  /* If no filter answered the query, the default is a non-multiplexed
+   * connection with limit 1. Otherwise, the the query may return 0
+   * for connections that are in shutdown, e.g. server HTTP/2 GOAWAY. */
+  return (result || n < 0) ? 1 : (size_t)n;
 }
 
 int Curl_conn_get_stream_error(struct Curl_easy *data,
                                struct connectdata *conn,
                                int sockindex)
 {
+  struct Curl_cfilter *cf;
   CURLcode result;
   int n = 0;
 
-  struct Curl_cfilter *cf = conn->cfilter[sockindex];
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return 0;
+
+  cf = conn->cfilter[sockindex];
   result = cf ? cf->cft->query(cf, data, CF_QUERY_STREAM_ERROR,
                                &n, NULL) : CURLE_UNKNOWN_OPTION;
   return (result || n < 0) ? 0 : n;
@@ -1006,6 +1107,8 @@ CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex,
 {
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return CURLE_BAD_FUNCTION_ARGUMENT;
   if(data && data->conn && data->conn->recv[sockindex])
     return data->conn->recv[sockindex](data, sockindex, buf, blen, pnread);
   *pnread = 0;
@@ -1020,7 +1123,9 @@ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
 
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
-  DEBUGASSERT(sockindex >= 0 && sockindex < 2);
+  DEBUGASSERT(CONN_SOCK_IDX_VALID(sockindex));
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return CURLE_BAD_FUNCTION_ARGUMENT;
 #ifdef DEBUGBUILD
   if(write_len) {
     /* Allow debug builds to override this logic to force short sends
@@ -1041,142 +1146,3 @@ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
   *pnwritten = 0;
   return CURLE_FAILED_INIT;
 }
-
-void Curl_pollset_reset(struct Curl_easy *data,
-                        struct easy_pollset *ps)
-{
-  size_t i;
-  (void)data;
-  memset(ps, 0, sizeof(*ps));
-  for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++)
-    ps->sockets[i] = CURL_SOCKET_BAD;
-}
-
-/**
- *
- */
-void Curl_pollset_change(struct Curl_easy *data,
-                         struct easy_pollset *ps, curl_socket_t sock,
-                         int add_flags, int remove_flags)
-{
-  unsigned int i;
-
-  (void)data;
-  DEBUGASSERT(VALID_SOCK(sock));
-  if(!VALID_SOCK(sock))
-    return;
-
-  DEBUGASSERT(add_flags <= (CURL_POLL_IN|CURL_POLL_OUT));
-  DEBUGASSERT(remove_flags <= (CURL_POLL_IN|CURL_POLL_OUT));
-  DEBUGASSERT((add_flags&remove_flags) == 0); /* no overlap */
-  for(i = 0; i < ps->num; ++i) {
-    if(ps->sockets[i] == sock) {
-      ps->actions[i] &= (unsigned char)(~remove_flags);
-      ps->actions[i] |= (unsigned char)add_flags;
-      /* all gone? remove socket */
-      if(!ps->actions[i]) {
-        if((i + 1) < ps->num) {
-          memmove(&ps->sockets[i], &ps->sockets[i + 1],
-                  (ps->num - (i + 1)) * sizeof(ps->sockets[0]));
-          memmove(&ps->actions[i], &ps->actions[i + 1],
-                  (ps->num - (i + 1)) * sizeof(ps->actions[0]));
-        }
-        --ps->num;
-      }
-      return;
-    }
-  }
-  /* not present */
-  if(add_flags) {
-    /* Having more SOCKETS per easy handle than what is defined
-     * is a programming error. This indicates that we need
-     * to raise this limit, making easy_pollset larger.
-     * Since we use this in tight loops, we do not want to make
-     * the pollset dynamic unnecessarily.
-     * The current maximum in practise is HTTP/3 eyeballing where
-     * we have up to 4 sockets involved in connection setup.
-     */
-    DEBUGASSERT(i < MAX_SOCKSPEREASYHANDLE);
-    if(i < MAX_SOCKSPEREASYHANDLE) {
-      ps->sockets[i] = sock;
-      ps->actions[i] = (unsigned char)add_flags;
-      ps->num = i + 1;
-    }
-  }
-}
-
-void Curl_pollset_set(struct Curl_easy *data,
-                      struct easy_pollset *ps, curl_socket_t sock,
-                      bool do_in, bool do_out)
-{
-  Curl_pollset_change(data, ps, sock,
-                      (do_in ? CURL_POLL_IN : 0)|
-                      (do_out ? CURL_POLL_OUT : 0),
-                      (!do_in ? CURL_POLL_IN : 0)|
-                      (!do_out ? CURL_POLL_OUT : 0));
-}
-
-static void ps_add(struct Curl_easy *data, struct easy_pollset *ps,
-                   int bitmap, curl_socket_t *socks)
-{
-  if(bitmap) {
-    int i;
-    for(i = 0; i < MAX_SOCKSPEREASYHANDLE; ++i) {
-      if(!(bitmap & GETSOCK_MASK_RW(i)) || !VALID_SOCK((socks[i]))) {
-        break;
-      }
-      if(bitmap & GETSOCK_READSOCK(i)) {
-        if(bitmap & GETSOCK_WRITESOCK(i))
-          Curl_pollset_add_inout(data, ps, socks[i]);
-        else
-          /* is READ, since we checked MASK_RW above */
-          Curl_pollset_add_in(data, ps, socks[i]);
-      }
-      else
-        Curl_pollset_add_out(data, ps, socks[i]);
-    }
-  }
-}
-
-void Curl_pollset_add_socks(struct Curl_easy *data,
-                            struct easy_pollset *ps,
-                            int (*get_socks_cb)(struct Curl_easy *data,
-                                                curl_socket_t *socks))
-{
-  curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
-  int bitmap;
-
-  bitmap = get_socks_cb(data, socks);
-  ps_add(data, ps, bitmap, socks);
-}
-
-void Curl_pollset_check(struct Curl_easy *data,
-                        struct easy_pollset *ps, curl_socket_t sock,
-                        bool *pwant_read, bool *pwant_write)
-{
-  unsigned int i;
-
-  (void)data;
-  DEBUGASSERT(VALID_SOCK(sock));
-  for(i = 0; i < ps->num; ++i) {
-    if(ps->sockets[i] == sock) {
-      *pwant_read = !!(ps->actions[i] & CURL_POLL_IN);
-      *pwant_write = !!(ps->actions[i] & CURL_POLL_OUT);
-      return;
-    }
-  }
-  *pwant_read = *pwant_write = FALSE;
-}
-
-bool Curl_pollset_want_read(struct Curl_easy *data,
-                            struct easy_pollset *ps,
-                            curl_socket_t sock)
-{
-  unsigned int i;
-  (void)data;
-  for(i = 0; i < ps->num; ++i) {
-    if((ps->sockets[i] == sock) && (ps->actions[i] & CURL_POLL_IN))
-      return TRUE;
-  }
-  return FALSE;
-}

+ 28 - 67
Utilities/cmcurl/lib/cfilters.h

@@ -80,7 +80,7 @@ struct easy_pollset;
  * @param data   the easy handle the pollset is about
  * @param ps     the pollset (inout) for the easy handle
  */
-typedef void     Curl_cft_adjust_pollset(struct Curl_cfilter *cf,
+typedef CURLcode Curl_cft_adjust_pollset(struct Curl_cfilter *cf,
                                          struct Curl_easy *data,
                                          struct easy_pollset *ps);
 
@@ -158,6 +158,10 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
  * - CF_QUERY_SSL_CTX_INFO: same as CF_QUERY_SSL_INFO, but give the SSL_CTX
  *                      when available, or the same internal pointer
  *                      when the TLS stack does not differentiate.
+ * - CF_QUERY_ALPN_NEGOTIATED: The ALPN selected by the server as
+                        null-terminated string or NULL if none
+                        selected/handshake not done. Implemented by filter
+                        types CF_TYPE_SSL or CF_TYPE_IP_CONNECT.
  */
 /*      query                             res1       res2     */
 #define CF_QUERY_MAX_CONCURRENT     1  /* number     -        */
@@ -176,6 +180,7 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
 #define CF_QUERY_SSL_INFO          12  /* -    struct curl_tlssessioninfo * */
 #define CF_QUERY_SSL_CTX_INFO      13  /* -    struct curl_tlssessioninfo * */
 #define CF_QUERY_TRANSPORT         14  /* TRNSPRT_*  - * */
+#define CF_QUERY_ALPN_NEGOTIATED   15  /* -          const char * */
 
 /**
  * Query the cfilter for properties. Filters ignorant of a query will
@@ -239,7 +244,7 @@ void Curl_cf_def_destroy_this(struct Curl_cfilter *cf,
 
 /* Default implementations for the type functions, implementing pass-through
  * the filter chain. */
-void     Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf,
+CURLcode Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf,
                                     struct Curl_easy *data,
                                     struct easy_pollset *ps);
 bool     Curl_cf_def_data_pending(struct Curl_cfilter *cf,
@@ -331,12 +336,6 @@ CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf,
                             bool ignore_result,
                             int event, int arg1, void *arg2);
 
-/**
- * Determine if the connection filter chain is using SSL to the remote host
- * (or will be once connected).
- */
-bool Curl_conn_cf_is_ssl(struct Curl_cfilter *cf);
-
 /**
  * Get the socket used by the filter chain starting at `cf`.
  * Returns CURL_SOCKET_BAD if not available.
@@ -346,7 +345,7 @@ curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf,
 
 CURLcode Curl_conn_cf_get_ip_info(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
-                                  int *is_ipv6, struct ip_quadruple *ipquad);
+                                  bool *is_ipv6, struct ip_quadruple *ipquad);
 
 bool Curl_conn_cf_needs_flush(struct Curl_cfilter *cf,
                               struct Curl_easy *data);
@@ -354,6 +353,9 @@ bool Curl_conn_cf_needs_flush(struct Curl_cfilter *cf,
 unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf,
                                          struct Curl_easy *data);
 
+const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf,
+                                             struct Curl_easy *data);
+
 #define CURL_CF_SSL_DEFAULT  -1
 #define CURL_CF_SSL_DISABLE  0
 #define CURL_CF_SSL_ENABLE   1
@@ -402,6 +404,10 @@ bool Curl_conn_get_ssl_info(struct Curl_easy *data,
                             struct connectdata *conn, int sockindex,
                             struct curl_tlssessioninfo *info);
 
+CURLcode Curl_conn_get_ip_info(struct Curl_easy *data,
+                               struct connectdata *conn, int sockindex,
+                               bool *is_ipv6, struct ip_quadruple *ipquad);
+
 /**
  * Connection provides multiplexing of easy handles at `socketindex`.
  */
@@ -418,6 +424,10 @@ unsigned char Curl_conn_http_version(struct Curl_easy *data,
 unsigned char Curl_conn_get_transport(struct Curl_easy *data,
                                       struct connectdata *conn);
 
+/* Get the negotiated ALPN protocol or NULL if none in play */
+const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data,
+                                          struct connectdata *conn);
+
 /**
  * Close the filter chain at `sockindex` for connection `data->conn`.
   * Filters remain in place and may be connected again afterwards.
@@ -450,10 +460,11 @@ bool Curl_conn_needs_flush(struct Curl_easy *data, int sockindex);
 CURLcode Curl_conn_flush(struct Curl_easy *data, int sockindex);
 
 /**
- * Return the socket used on data's connection for the index.
+ * Return the socket used on data's connection for FIRSTSOCKET,
+ * querying filters if the whole chain has not connected yet.
  * Returns CURL_SOCKET_BAD if not available.
  */
-curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex);
+curl_socket_t Curl_conn_get_first_socket(struct Curl_easy *data);
 
 /* Return a pointer to the connected socket address or NULL. */
 const struct Curl_sockaddr_ex *
@@ -467,16 +478,16 @@ void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex);
 /**
  * Adjust the pollset for the filter chain starting at `cf`.
  */
-void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data,
-                                 struct easy_pollset *ps);
+CURLcode Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf,
+                                     struct Curl_easy *data,
+                                     struct easy_pollset *ps);
 
 /**
  * Adjust pollset from filters installed at transfer's connection.
  */
-void Curl_conn_adjust_pollset(struct Curl_easy *data,
-                              struct connectdata *conn,
-                              struct easy_pollset *ps);
+CURLcode Curl_conn_adjust_pollset(struct Curl_easy *data,
+                                  struct connectdata *conn,
+                                  struct easy_pollset *ps);
 
 /**
  * Curl_poll() the filter chain at `cf` with timeout `timeout_ms`.
@@ -622,56 +633,6 @@ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
                         size_t *pnwritten);
 
 
-void Curl_pollset_reset(struct Curl_easy *data,
-                        struct easy_pollset *ps);
-
-/* Change the poll flags (CURL_POLL_IN/CURL_POLL_OUT) to the poll set for
- * socket `sock`. If the socket is not already part of the poll set, it
- * will be added.
- * If the socket is present and all poll flags are cleared, it will be removed.
- */
-void Curl_pollset_change(struct Curl_easy *data,
-                         struct easy_pollset *ps, curl_socket_t sock,
-                         int add_flags, int remove_flags);
-
-void Curl_pollset_set(struct Curl_easy *data,
-                      struct easy_pollset *ps, curl_socket_t sock,
-                      bool do_in, bool do_out);
-
-#define Curl_pollset_add_in(data, ps, sock) \
-          Curl_pollset_change((data), (ps), (sock), CURL_POLL_IN, 0)
-#define Curl_pollset_add_out(data, ps, sock) \
-          Curl_pollset_change((data), (ps), (sock), CURL_POLL_OUT, 0)
-#define Curl_pollset_add_inout(data, ps, sock) \
-          Curl_pollset_change((data), (ps), (sock), \
-                               CURL_POLL_IN|CURL_POLL_OUT, 0)
-#define Curl_pollset_set_in_only(data, ps, sock) \
-          Curl_pollset_change((data), (ps), (sock), \
-                               CURL_POLL_IN, CURL_POLL_OUT)
-#define Curl_pollset_set_out_only(data, ps, sock) \
-          Curl_pollset_change((data), (ps), (sock), \
-                               CURL_POLL_OUT, CURL_POLL_IN)
-
-void Curl_pollset_add_socks(struct Curl_easy *data,
-                            struct easy_pollset *ps,
-                            int (*get_socks_cb)(struct Curl_easy *data,
-                                                curl_socket_t *socks));
-
-/**
- * Check if the pollset, as is, wants to read and/or write regarding
- * the given socket.
- */
-void Curl_pollset_check(struct Curl_easy *data,
-                        struct easy_pollset *ps, curl_socket_t sock,
-                        bool *pwant_read, bool *pwant_write);
-
-/**
- * Return TRUE if the pollset contains socket with CURL_POLL_IN.
- */
-bool Curl_pollset_want_read(struct Curl_easy *data,
-                            struct easy_pollset *ps,
-                            curl_socket_t sock);
-
 /**
  * Types and macros used to keep the current easy handle in filter calls,
  * allowing for nested invocations. See #10336.

+ 37 - 2
Utilities/cmcurl/lib/conncache.c

@@ -515,7 +515,7 @@ static bool cpool_foreach(struct Curl_easy *data,
       struct connectdata *conn = Curl_node_elem(curr);
       curr = Curl_node_next(curr);
 
-      if(1 == func(data, conn, param)) {
+      if(func(data, conn, param) == 1) {
         return TRUE;
       }
     }
@@ -709,7 +709,8 @@ static int cpool_reap_dead_cb(struct Curl_easy *data,
                               struct connectdata *conn, void *param)
 {
   struct cpool_reaper_ctx *rctx = param;
-  if(Curl_conn_seems_dead(conn, data, &rctx->now)) {
+  if((!CONN_INUSE(conn) && conn->bits.no_reuse) ||
+     Curl_conn_seems_dead(conn, data, &rctx->now)) {
     /* stop the iteration here, pass back the connection that was pruned */
     Curl_conn_terminate(data, conn, FALSE);
     return 1;
@@ -849,6 +850,40 @@ void Curl_cpool_do_locked(struct Curl_easy *data,
     cb(conn, data, cbdata);
 }
 
+static int cpool_mark_stale(struct Curl_easy *data,
+                            struct connectdata *conn, void *param)
+{
+  (void)data;
+  (void)param;
+  conn->bits.no_reuse = TRUE;
+  return 0;
+}
+
+static int cpool_reap_no_reuse(struct Curl_easy *data,
+                               struct connectdata *conn, void *param)
+{
+  (void)data;
+  (void)param;
+  if(!CONN_INUSE(conn) && conn->bits.no_reuse) {
+    Curl_conn_terminate(data, conn, FALSE);
+    return 1;
+  }
+  return 0; /* continue iteration */
+}
+
+void Curl_cpool_nw_changed(struct Curl_easy *data)
+{
+  struct cpool *cpool = cpool_get_instance(data);
+
+  if(cpool) {
+    CPOOL_LOCK(cpool, data);
+    cpool_foreach(data, cpool, NULL, cpool_mark_stale);
+    while(cpool_foreach(data, cpool, NULL, cpool_reap_no_reuse))
+      ;
+    CPOOL_UNLOCK(cpool, data);
+  }
+}
+
 #if 0
 /* Useful for debugging the connection pool */
 void Curl_cpool_print(struct cpool *cpool)

+ 4 - 0
Utilities/cmcurl/lib/conncache.h

@@ -163,4 +163,8 @@ void Curl_cpool_do_locked(struct Curl_easy *data,
                           struct connectdata *conn,
                           Curl_cpool_conn_do_cb *cb, void *cbdata);
 
+/* Close all unused connections, prevent reuse of existing ones. */
+void Curl_cpool_nw_changed(struct Curl_easy *data);
+
+
 #endif /* HEADER_CURL_CONNCACHE_H */

+ 6 - 912
Utilities/cmcurl/lib/connect.c

@@ -61,6 +61,7 @@
 #include "connect.h"
 #include "cf-haproxy.h"
 #include "cf-https-connect.h"
+#include "cf-ip-happy.h"
 #include "cf-socket.h"
 #include "select.h"
 #include "url.h" /* for Curl_safefree() */
@@ -74,8 +75,6 @@
 #include "conncache.h"
 #include "multihandle.h"
 #include "share.h"
-#include "curlx/version_win32.h"
-#include "vquic/vquic.h" /* for quic cfilters */
 #include "http_proxy.h"
 #include "socks.h"
 
@@ -232,28 +231,6 @@ bool Curl_shutdown_started(struct Curl_easy *data, int sockindex)
   return (pt->tv_sec > 0) || (pt->tv_usec > 0);
 }
 
-static const struct Curl_addrinfo *
-addr_first_match(const struct Curl_addrinfo *addr, int family)
-{
-  while(addr) {
-    if(addr->ai_family == family)
-      return addr;
-    addr = addr->ai_next;
-  }
-  return NULL;
-}
-
-static const struct Curl_addrinfo *
-addr_next_match(const struct Curl_addrinfo *addr, int family)
-{
-  while(addr && addr->ai_next) {
-    addr = addr->ai_next;
-    if(addr->ai_family == family)
-      return addr;
-  }
-  return NULL;
-}
-
 /* retrieves ip address and port from a sockaddr structure. note it calls
    curlx_inet_ntop which sets errno on fail, not SOCKERRNO. */
 bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
@@ -372,874 +349,6 @@ void Curl_conncontrol(struct connectdata *conn,
   }
 }
 
-/**
- * job walking the matching addr infos, creating a sub-cfilter with the
- * provided method `cf_create` and running setup/connect on it.
- */
-struct eyeballer {
-  const char *name;
-  const struct Curl_addrinfo *first; /* complete address list, not owned */
-  const struct Curl_addrinfo *addr;  /* List of addresses to try, not owned */
-  int ai_family;                     /* matching address family only */
-  cf_ip_connect_create *cf_create;   /* for creating cf */
-  struct Curl_cfilter *cf;           /* current sub-cfilter connecting */
-  struct eyeballer *primary;         /* eyeballer this one is backup for */
-  timediff_t delay_ms;               /* delay until start */
-  struct curltime started;           /* start of current attempt */
-  timediff_t timeoutms;              /* timeout for current attempt */
-  expire_id timeout_id;              /* ID for Curl_expire() */
-  CURLcode result;
-  int error;
-  BIT(has_started);                  /* attempts have started */
-  BIT(is_done);                      /* out of addresses/time */
-  BIT(connected);                    /* cf has connected */
-  BIT(shutdown);                     /* cf has shutdown */
-  BIT(inconclusive);                 /* connect was not a hard failure, we
-                                      * might talk to a restarting server */
-};
-
-
-typedef enum {
-  SCFST_INIT,
-  SCFST_WAITING,
-  SCFST_DONE
-} cf_connect_state;
-
-struct cf_he_ctx {
-  int transport;
-  cf_ip_connect_create *cf_create;
-  cf_connect_state state;
-  struct eyeballer *baller[2];
-  struct eyeballer *winner;
-  struct curltime started;
-};
-
-/* when there are more than one IP address left to use, this macro returns how
-   much of the given timeout to spend on *this* attempt */
-#define TIMEOUT_LARGE 600
-#define USETIME(ms) ((ms > TIMEOUT_LARGE) ? (ms / 2) : ms)
-
-static CURLcode eyeballer_new(struct eyeballer **pballer,
-                              cf_ip_connect_create *cf_create,
-                              const struct Curl_addrinfo *addr,
-                              int ai_family,
-                              struct eyeballer *primary,
-                              timediff_t delay_ms,
-                              timediff_t timeout_ms,
-                              expire_id timeout_id)
-{
-  struct eyeballer *baller;
-
-  *pballer = NULL;
-  baller = calloc(1, sizeof(*baller));
-  if(!baller)
-    return CURLE_OUT_OF_MEMORY;
-
-  baller->name = ((ai_family == AF_INET) ? "ipv4" : (
-#ifdef USE_IPV6
-                  (ai_family == AF_INET6) ? "ipv6" :
-#endif
-                  "ip"));
-  baller->cf_create = cf_create;
-  baller->first = baller->addr = addr;
-  baller->ai_family = ai_family;
-  baller->primary = primary;
-  baller->delay_ms = delay_ms;
-  baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ?
-    USETIME(timeout_ms) : timeout_ms;
-  baller->timeout_id = timeout_id;
-  baller->result = CURLE_COULDNT_CONNECT;
-
-  *pballer = baller;
-  return CURLE_OK;
-}
-
-static void baller_close(struct eyeballer *baller,
-                         struct Curl_easy *data)
-{
-  if(baller && baller->cf) {
-    Curl_conn_cf_discard_chain(&baller->cf, data);
-  }
-}
-
-static void baller_free(struct eyeballer *baller,
-                        struct Curl_easy *data)
-{
-  if(baller) {
-    baller_close(baller, data);
-    free(baller);
-  }
-}
-
-static void baller_rewind(struct eyeballer *baller)
-{
-  baller->addr = baller->first;
-  baller->inconclusive = FALSE;
-}
-
-static void baller_next_addr(struct eyeballer *baller)
-{
-  baller->addr = addr_next_match(baller->addr, baller->ai_family);
-}
-
-/*
- * Initiate a connect attempt walk.
- *
- * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
- * CURL_SOCKET_BAD. Other errors will however return proper errors.
- */
-static void baller_initiate(struct Curl_cfilter *cf,
-                            struct Curl_easy *data,
-                            struct eyeballer *baller)
-{
-  struct cf_he_ctx *ctx = cf->ctx;
-  struct Curl_cfilter *cf_prev = baller->cf;
-  struct Curl_cfilter *wcf;
-  CURLcode result;
-
-
-  /* Do not close a previous cfilter yet to ensure that the next IP's
-     socket gets a different file descriptor, which can prevent bugs when
-     the curl_multi_socket_action interface is used with certain select()
-     replacements such as kqueue. */
-  result = baller->cf_create(&baller->cf, data, cf->conn, baller->addr,
-                             ctx->transport);
-  if(result)
-    goto out;
-
-  /* the new filter might have sub-filters */
-  for(wcf = baller->cf; wcf; wcf = wcf->next) {
-    wcf->conn = cf->conn;
-    wcf->sockindex = cf->sockindex;
-  }
-
-  if(addr_next_match(baller->addr, baller->ai_family)) {
-    Curl_expire(data, baller->timeoutms, baller->timeout_id);
-  }
-
-out:
-  if(result) {
-    CURL_TRC_CF(data, cf, "%s failed", baller->name);
-    baller_close(baller, data);
-  }
-  if(cf_prev)
-    Curl_conn_cf_discard_chain(&cf_prev, data);
-  baller->result = result;
-}
-
-/**
- * Start a connection attempt on the current baller address.
- * Will return CURLE_OK on the first address where a socket
- * could be created and the non-blocking connect started.
- * Returns error when all remaining addresses have been tried.
- */
-static CURLcode baller_start(struct Curl_cfilter *cf,
-                             struct Curl_easy *data,
-                             struct eyeballer *baller,
-                             timediff_t timeoutms)
-{
-  baller->error = 0;
-  baller->connected = FALSE;
-  baller->has_started = TRUE;
-
-  while(baller->addr) {
-    baller->started = curlx_now();
-    baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ?
-      USETIME(timeoutms) : timeoutms;
-    baller_initiate(cf, data, baller);
-    if(!baller->result)
-      break;
-    baller_next_addr(baller);
-  }
-  if(!baller->addr) {
-    baller->is_done = TRUE;
-  }
-  return baller->result;
-}
-
-
-/* Used within the multi interface. Try next IP address, returns error if no
-   more address exists or error */
-static CURLcode baller_start_next(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data,
-                                  struct eyeballer *baller,
-                                  timediff_t timeoutms)
-{
-  if(cf->sockindex == FIRSTSOCKET) {
-    baller_next_addr(baller);
-    /* If we get inconclusive answers from the server(s), we start
-     * again until this whole thing times out. This allows us to
-     * connect to servers that are gracefully restarting and the
-     * packet routing to the new instance has not happened yet (e.g. QUIC). */
-    if(!baller->addr && baller->inconclusive)
-      baller_rewind(baller);
-    baller_start(cf, data, baller, timeoutms);
-  }
-  else {
-    baller->error = 0;
-    baller->connected = FALSE;
-    baller->has_started = TRUE;
-    baller->is_done = TRUE;
-    baller->result = CURLE_COULDNT_CONNECT;
-  }
-  return baller->result;
-}
-
-static CURLcode baller_connect(struct Curl_cfilter *cf,
-                               struct Curl_easy *data,
-                               struct eyeballer *baller,
-                               struct curltime *now,
-                               bool *connected)
-{
-  (void)cf;
-  *connected = baller->connected;
-  if(!baller->result &&  !*connected) {
-    /* evaluate again */
-    baller->result = Curl_conn_cf_connect(baller->cf, data, connected);
-
-    if(!baller->result) {
-      if(*connected) {
-        baller->connected = TRUE;
-        baller->is_done = TRUE;
-      }
-      else if(curlx_timediff(*now, baller->started) >= baller->timeoutms) {
-        infof(data, "%s connect timeout after %" FMT_TIMEDIFF_T
-              "ms, move on!", baller->name, baller->timeoutms);
-#ifdef SOCKETIMEDOUT
-        baller->error = SOCKETIMEDOUT;
-#endif
-        baller->result = CURLE_OPERATION_TIMEDOUT;
-      }
-    }
-    else if(baller->result == CURLE_WEIRD_SERVER_REPLY)
-      baller->inconclusive = TRUE;
-  }
-  return baller->result;
-}
-
-/*
- * is_connected() checks if the socket has connected.
- */
-static CURLcode is_connected(struct Curl_cfilter *cf,
-                             struct Curl_easy *data,
-                             bool *connected)
-{
-  struct cf_he_ctx *ctx = cf->ctx;
-  struct connectdata *conn = cf->conn;
-  CURLcode result;
-  struct curltime now;
-  size_t i;
-  int ongoing, not_started;
-  const char *hostname;
-
-  /* Check if any of the conn->tempsock we use for establishing connections
-   * succeeded and, if so, close any ongoing other ones.
-   * Transfer the successful conn->tempsock to conn->sock[sockindex]
-   * and set conn->tempsock to CURL_SOCKET_BAD.
-   * If transport is QUIC, we need to shutdown the ongoing 'other'
-   * cot ballers in a QUIC appropriate way. */
-evaluate:
-  *connected = FALSE; /* a negative world view is best */
-  now = curlx_now();
-  ongoing = not_started = 0;
-  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
-    struct eyeballer *baller = ctx->baller[i];
-
-    if(!baller || baller->is_done)
-      continue;
-
-    if(!baller->has_started) {
-      ++not_started;
-      continue;
-    }
-    baller->result = baller_connect(cf, data, baller, &now, connected);
-    CURL_TRC_CF(data, cf, "%s connect -> %d, connected=%d",
-                baller->name, baller->result, *connected);
-
-    if(!baller->result) {
-      if(*connected) {
-        /* connected, declare the winner */
-        ctx->winner = baller;
-        ctx->baller[i] = NULL;
-        break;
-      }
-      else { /* still waiting */
-        ++ongoing;
-      }
-    }
-    else if(!baller->is_done) {
-      /* The baller failed to connect, start its next attempt */
-      if(baller->error) {
-        data->state.os_errno = baller->error;
-        SET_SOCKERRNO(baller->error);
-      }
-      baller_start_next(cf, data, baller, Curl_timeleft(data, &now, TRUE));
-      if(baller->is_done) {
-        CURL_TRC_CF(data, cf, "%s done", baller->name);
-      }
-      else {
-        /* next attempt was started */
-        CURL_TRC_CF(data, cf, "%s trying next", baller->name);
-        ++ongoing;
-        Curl_multi_mark_dirty(data);
-      }
-    }
-  }
-
-  if(ctx->winner) {
-    *connected = TRUE;
-    return CURLE_OK;
-  }
-
-  /* Nothing connected, check the time before we might
-   * start new ballers or return ok. */
-  if((ongoing || not_started) && Curl_timeleft(data, &now, TRUE) < 0) {
-    failf(data, "Connection timeout after %" FMT_OFF_T " ms",
-          curlx_timediff(now, data->progress.t_startsingle));
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
-  /* Check if we have any waiting ballers to start now. */
-  if(not_started > 0) {
-    int added = 0;
-
-    for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
-      struct eyeballer *baller = ctx->baller[i];
-
-      if(!baller || baller->has_started)
-        continue;
-      /* We start its primary baller has failed to connect or if
-       * its start delay_ms have expired */
-      if((baller->primary && baller->primary->is_done) ||
-          curlx_timediff(now, ctx->started) >= baller->delay_ms) {
-        baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE));
-        if(baller->is_done) {
-          CURL_TRC_CF(data, cf, "%s done", baller->name);
-        }
-        else {
-          CURL_TRC_CF(data, cf, "%s starting (timeout=%" FMT_TIMEDIFF_T "ms)",
-                      baller->name, baller->timeoutms);
-          ++ongoing;
-          ++added;
-        }
-      }
-    }
-    if(added > 0)
-      goto evaluate;
-  }
-
-  if(ongoing > 0) {
-    /* We are still trying, return for more waiting */
-    *connected = FALSE;
-    return CURLE_OK;
-  }
-
-  /* all ballers have failed to connect. */
-  CURL_TRC_CF(data, cf, "all eyeballers failed");
-  result = CURLE_COULDNT_CONNECT;
-  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
-    struct eyeballer *baller = ctx->baller[i];
-    if(!baller)
-      continue;
-    CURL_TRC_CF(data, cf, "%s assess started=%d, result=%d",
-                baller->name, baller->has_started, baller->result);
-    if(baller->has_started && baller->result) {
-      result = baller->result;
-      break;
-    }
-  }
-
-#ifndef CURL_DISABLE_PROXY
-  if(conn->bits.socksproxy)
-    hostname = conn->socks_proxy.host.name;
-  else if(conn->bits.httpproxy)
-    hostname = conn->http_proxy.host.name;
-  else
-#endif
-    if(conn->bits.conn_to_host)
-      hostname = conn->conn_to_host.name;
-  else
-    hostname = conn->host.name;
-
-  failf(data, "Failed to connect to %s port %u after "
-        "%" FMT_TIMEDIFF_T " ms: %s",
-        hostname, conn->primary.remote_port,
-        curlx_timediff(now, data->progress.t_startsingle),
-        curl_easy_strerror(result));
-
-#ifdef SOCKETIMEDOUT
-  if(SOCKETIMEDOUT == data->state.os_errno)
-    result = CURLE_OPERATION_TIMEDOUT;
-#endif
-
-  return result;
-}
-
-/*
- * Connect to the given host with timeout, proxy or remote does not matter.
- * There might be more than one IP address to try out.
- */
-static CURLcode start_connect(struct Curl_cfilter *cf,
-                              struct Curl_easy *data)
-{
-  struct cf_he_ctx *ctx = cf->ctx;
-  struct connectdata *conn = cf->conn;
-  CURLcode result = CURLE_COULDNT_CONNECT;
-  int ai_family0 = 0, ai_family1 = 0;
-  timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
-  const struct Curl_addrinfo *addr0 = NULL, *addr1 = NULL;
-  struct Curl_dns_entry *dns = data->state.dns[cf->sockindex];
-
-  if(!dns)
-    return CURLE_FAILED_INIT;
-
-  if(timeout_ms < 0) {
-    /* a precaution, no need to continue if time already is up */
-    failf(data, "Connection time-out");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
-  ctx->started = curlx_now();
-
-  /* dns->addr is the list of addresses from the resolver, each
-   * with an address family. The list has at least one entry, possibly
-   * many more.
-   * We try at most 2 at a time, until we either get a connection or
-   * run out of addresses to try. Since likelihood of success is tied
-   * to the address family (e.g. IPV6 might not work at all ), we want
-   * the 2 connect attempt ballers to try different families, if possible.
-   *
-   */
-  if(conn->ip_version == CURL_IPRESOLVE_V6) {
-#ifdef USE_IPV6
-    ai_family0 = AF_INET6;
-    addr0 = addr_first_match(dns->addr, ai_family0);
-#endif
-  }
-  else if(conn->ip_version == CURL_IPRESOLVE_V4) {
-    ai_family0 = AF_INET;
-    addr0 = addr_first_match(dns->addr, ai_family0);
-  }
-  else {
-    /* no user preference, we try ipv6 always first when available */
-#ifdef USE_IPV6
-    ai_family0 = AF_INET6;
-    addr0 = addr_first_match(dns->addr, ai_family0);
-#endif
-    /* next candidate is ipv4 */
-    ai_family1 = AF_INET;
-    addr1 = addr_first_match(dns->addr, ai_family1);
-    /* no ip address families, probably AF_UNIX or something, use the
-     * address family given to us */
-    if(!addr1 && !addr0 && dns->addr) {
-      ai_family0 = dns->addr->ai_family;
-      addr0 = addr_first_match(dns->addr, ai_family0);
-    }
-  }
-
-  if(!addr0 && addr1) {
-    /* switch around, so a single baller always uses addr0 */
-    addr0 = addr1;
-    ai_family0 = ai_family1;
-    addr1 = NULL;
-  }
-
-  /* We found no address that matches our criteria, we cannot connect */
-  if(!addr0) {
-    return CURLE_COULDNT_CONNECT;
-  }
-
-  memset(ctx->baller, 0, sizeof(ctx->baller));
-  result = eyeballer_new(&ctx->baller[0], ctx->cf_create, addr0, ai_family0,
-                          NULL, 0, /* no primary/delay, start now */
-                          timeout_ms,  EXPIRE_DNS_PER_NAME);
-  if(result)
-    return result;
-  CURL_TRC_CF(data, cf, "created %s (timeout %" FMT_TIMEDIFF_T "ms)",
-              ctx->baller[0]->name, ctx->baller[0]->timeoutms);
-  if(addr1) {
-    /* second one gets a delayed start */
-    result = eyeballer_new(&ctx->baller[1], ctx->cf_create, addr1, ai_family1,
-                            ctx->baller[0], /* wait on that to fail */
-                            /* or start this delayed */
-                            data->set.happy_eyeballs_timeout,
-                            timeout_ms,  EXPIRE_DNS_PER_NAME2);
-    if(result)
-      return result;
-    CURL_TRC_CF(data, cf, "created %s (timeout %" FMT_TIMEDIFF_T "ms)",
-                ctx->baller[1]->name, ctx->baller[1]->timeoutms);
-    Curl_expire(data, data->set.happy_eyeballs_timeout,
-                EXPIRE_HAPPY_EYEBALLS);
-  }
-
-  return CURLE_OK;
-}
-
-static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
-  struct cf_he_ctx *ctx = cf->ctx;
-  size_t i;
-
-  DEBUGASSERT(ctx);
-  DEBUGASSERT(data);
-  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
-    baller_free(ctx->baller[i], data);
-    ctx->baller[i] = NULL;
-  }
-  baller_free(ctx->winner, data);
-  ctx->winner = NULL;
-}
-
-static CURLcode cf_he_shutdown(struct Curl_cfilter *cf,
-                               struct Curl_easy *data, bool *done)
-{
-  struct cf_he_ctx *ctx = cf->ctx;
-  size_t i;
-  CURLcode result = CURLE_OK;
-
-  DEBUGASSERT(data);
-  if(cf->connected) {
-    *done = TRUE;
-    return CURLE_OK;
-  }
-
-  /* shutdown all ballers that have not done so already. If one fails,
-   * continue shutting down others until all are shutdown. */
-  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
-    struct eyeballer *baller = ctx->baller[i];
-    bool bdone = FALSE;
-    if(!baller || !baller->cf || baller->shutdown)
-      continue;
-    baller->result = baller->cf->cft->do_shutdown(baller->cf, data, &bdone);
-    if(baller->result || bdone)
-      baller->shutdown = TRUE; /* treat a failed shutdown as done */
-  }
-
-  *done = TRUE;
-  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
-    if(ctx->baller[i] && !ctx->baller[i]->shutdown)
-      *done = FALSE;
-  }
-  if(*done) {
-    for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
-      if(ctx->baller[i] && ctx->baller[i]->result)
-        result = ctx->baller[i]->result;
-    }
-  }
-  CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done);
-  return result;
-}
-
-static void cf_he_adjust_pollset(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data,
-                                 struct easy_pollset *ps)
-{
-  struct cf_he_ctx *ctx = cf->ctx;
-  size_t i;
-
-  if(!cf->connected) {
-    for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
-      struct eyeballer *baller = ctx->baller[i];
-      if(!baller || !baller->cf)
-        continue;
-      Curl_conn_cf_adjust_pollset(baller->cf, data, ps);
-    }
-    CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num);
-  }
-}
-
-static CURLcode cf_he_connect(struct Curl_cfilter *cf,
-                              struct Curl_easy *data,
-                              bool *done)
-{
-  struct cf_he_ctx *ctx = cf->ctx;
-  CURLcode result = CURLE_OK;
-
-  if(cf->connected) {
-    *done = TRUE;
-    return CURLE_OK;
-  }
-
-  DEBUGASSERT(ctx);
-  *done = FALSE;
-
-  switch(ctx->state) {
-    case SCFST_INIT:
-      DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data));
-      DEBUGASSERT(!cf->connected);
-      result = start_connect(cf, data);
-      if(result)
-        return result;
-      ctx->state = SCFST_WAITING;
-      FALLTHROUGH();
-    case SCFST_WAITING:
-      result = is_connected(cf, data, done);
-      if(!result && *done) {
-        DEBUGASSERT(ctx->winner);
-        DEBUGASSERT(ctx->winner->cf);
-        DEBUGASSERT(ctx->winner->cf->connected);
-        /* we have a winner. Install and activate it.
-         * close/free all others. */
-        ctx->state = SCFST_DONE;
-        cf->connected = TRUE;
-        cf->next = ctx->winner->cf;
-        ctx->winner->cf = NULL;
-        cf_he_ctx_clear(cf, data);
-
-        if(cf->conn->handler->protocol & PROTO_FAMILY_SSH)
-          Curl_pgrsTime(data, TIMER_APPCONNECT); /* we are connected already */
-        if(Curl_trc_cf_is_verbose(cf, data)) {
-          struct ip_quadruple ipquad;
-          int is_ipv6;
-          if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) {
-            const char *host;
-            int port;
-            Curl_conn_get_current_host(data, cf->sockindex, &host, &port);
-            CURL_TRC_CF(data, cf, "Connected to %s (%s) port %u",
-                        host, ipquad.remote_ip, ipquad.remote_port);
-          }
-        }
-        data->info.numconnects++; /* to track the # of connections made */
-      }
-      break;
-    case SCFST_DONE:
-      *done = TRUE;
-      break;
-  }
-  return result;
-}
-
-static void cf_he_close(struct Curl_cfilter *cf,
-                        struct Curl_easy *data)
-{
-  struct cf_he_ctx *ctx = cf->ctx;
-
-  CURL_TRC_CF(data, cf, "close");
-  cf_he_ctx_clear(cf, data);
-  cf->connected = FALSE;
-  ctx->state = SCFST_INIT;
-
-  if(cf->next) {
-    cf->next->cft->do_close(cf->next, data);
-    Curl_conn_cf_discard_chain(&cf->next, data);
-  }
-}
-
-static bool cf_he_data_pending(struct Curl_cfilter *cf,
-                               const struct Curl_easy *data)
-{
-  struct cf_he_ctx *ctx = cf->ctx;
-  size_t i;
-
-  if(cf->connected)
-    return cf->next->cft->has_data_pending(cf->next, data);
-
-  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
-    struct eyeballer *baller = ctx->baller[i];
-    if(!baller || !baller->cf)
-      continue;
-    if(baller->cf->cft->has_data_pending(baller->cf, data))
-      return TRUE;
-  }
-  return FALSE;
-}
-
-static struct curltime get_max_baller_time(struct Curl_cfilter *cf,
-                                           struct Curl_easy *data,
-                                           int query)
-{
-  struct cf_he_ctx *ctx = cf->ctx;
-  struct curltime t, tmax;
-  size_t i;
-
-  memset(&tmax, 0, sizeof(tmax));
-  for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
-    struct eyeballer *baller = ctx->baller[i];
-
-    memset(&t, 0, sizeof(t));
-    if(baller && baller->cf &&
-       !baller->cf->cft->query(baller->cf, data, query, NULL, &t)) {
-      if((t.tv_sec || t.tv_usec) && curlx_timediff_us(t, tmax) > 0)
-        tmax = t;
-    }
-  }
-  return tmax;
-}
-
-static CURLcode cf_he_query(struct Curl_cfilter *cf,
-                            struct Curl_easy *data,
-                            int query, int *pres1, void *pres2)
-{
-  struct cf_he_ctx *ctx = cf->ctx;
-
-  if(!cf->connected) {
-    switch(query) {
-    case CF_QUERY_CONNECT_REPLY_MS: {
-      int reply_ms = -1;
-      size_t i;
-
-      for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
-        struct eyeballer *baller = ctx->baller[i];
-        int breply_ms;
-
-        if(baller && baller->cf &&
-           !baller->cf->cft->query(baller->cf, data, query,
-                                   &breply_ms, NULL)) {
-          if(breply_ms >= 0 && (reply_ms < 0 || breply_ms < reply_ms))
-            reply_ms = breply_ms;
-        }
-      }
-      *pres1 = reply_ms;
-      CURL_TRC_CF(data, cf, "query connect reply: %dms", *pres1);
-      return CURLE_OK;
-    }
-    case CF_QUERY_TIMER_CONNECT: {
-      struct curltime *when = pres2;
-      *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT);
-      return CURLE_OK;
-    }
-    case CF_QUERY_TIMER_APPCONNECT: {
-      struct curltime *when = pres2;
-      *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT);
-      return CURLE_OK;
-    }
-    default:
-      break;
-    }
-  }
-
-  return cf->next ?
-    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
-    CURLE_UNKNOWN_OPTION;
-}
-
-static void cf_he_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
-  struct cf_he_ctx *ctx = cf->ctx;
-
-  CURL_TRC_CF(data, cf, "destroy");
-  if(ctx) {
-    cf_he_ctx_clear(cf, data);
-  }
-  /* release any resources held in state */
-  Curl_safefree(ctx);
-}
-
-struct Curl_cftype Curl_cft_happy_eyeballs = {
-  "HAPPY-EYEBALLS",
-  0,
-  CURL_LOG_LVL_NONE,
-  cf_he_destroy,
-  cf_he_connect,
-  cf_he_close,
-  cf_he_shutdown,
-  cf_he_adjust_pollset,
-  cf_he_data_pending,
-  Curl_cf_def_send,
-  Curl_cf_def_recv,
-  Curl_cf_def_cntrl,
-  Curl_cf_def_conn_is_alive,
-  Curl_cf_def_conn_keep_alive,
-  cf_he_query,
-};
-
-/**
- * Create a happy eyeball connection filter that uses the, once resolved,
- * address information to connect on ip families based on connection
- * configuration.
- * @param pcf        output, the created cfilter
- * @param data       easy handle used in creation
- * @param conn       connection the filter is created for
- * @param cf_create  method to create the sub-filters performing the
- *                   actual connects.
- */
-static CURLcode
-cf_happy_eyeballs_create(struct Curl_cfilter **pcf,
-                         struct Curl_easy *data,
-                         struct connectdata *conn,
-                         cf_ip_connect_create *cf_create,
-                         int transport)
-{
-  struct cf_he_ctx *ctx = NULL;
-  CURLcode result;
-
-  (void)data;
-  (void)conn;
-  *pcf = NULL;
-  ctx = calloc(1, sizeof(*ctx));
-  if(!ctx) {
-    result = CURLE_OUT_OF_MEMORY;
-    goto out;
-  }
-  ctx->transport = transport;
-  ctx->cf_create = cf_create;
-
-  result = Curl_cf_create(pcf, &Curl_cft_happy_eyeballs, ctx);
-
-out:
-  if(result) {
-    Curl_safefree(*pcf);
-    free(ctx);
-  }
-  return result;
-}
-
-struct transport_provider {
-  int transport;
-  cf_ip_connect_create *cf_create;
-};
-
-static
-#ifndef UNITTESTS
-const
-#endif
-struct transport_provider transport_providers[] = {
-  { TRNSPRT_TCP, Curl_cf_tcp_create },
-#ifdef USE_HTTP3
-  { TRNSPRT_QUIC, Curl_cf_quic_create },
-#endif
-#ifndef CURL_DISABLE_TFTP
-  { TRNSPRT_UDP, Curl_cf_udp_create },
-#endif
-#ifdef USE_UNIX_SOCKETS
-  { TRNSPRT_UNIX, Curl_cf_unix_create },
-#endif
-};
-
-static cf_ip_connect_create *get_cf_create(int transport)
-{
-  size_t i;
-  for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
-    if(transport == transport_providers[i].transport)
-      return transport_providers[i].cf_create;
-  }
-  return NULL;
-}
-
-static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at,
-                                   struct Curl_easy *data,
-                                   int transport)
-{
-  cf_ip_connect_create *cf_create;
-  struct Curl_cfilter *cf;
-  CURLcode result;
-
-  /* Need to be first */
-  DEBUGASSERT(cf_at);
-  cf_create = get_cf_create(transport);
-  if(!cf_create) {
-    CURL_TRC_CF(data, cf_at, "unsupported transport type %d", transport);
-    return CURLE_UNSUPPORTED_PROTOCOL;
-  }
-  result = cf_happy_eyeballs_create(&cf, data, cf_at->conn,
-                                    cf_create, transport);
-  if(result)
-    return result;
-
-  Curl_conn_cf_insert_after(cf_at, cf);
-  return CURLE_OK;
-}
-
 typedef enum {
   CF_SETUP_INIT,
   CF_SETUP_CNNCT_EYEBALLS,
@@ -1281,7 +390,7 @@ connect_sub_chain:
   }
 
   if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
-    result = cf_he_insert_after(cf, data, ctx->transport);
+    result = cf_ip_happy_insert_after(cf, data, ctx->transport);
     if(result)
       return result;
     ctx->state = CF_SETUP_CNNCT_EYEBALLS;
@@ -1310,7 +419,7 @@ connect_sub_chain:
     }
 #endif /* USE_SSL */
 
-#if !defined(CURL_DISABLE_HTTP)
+#ifndef CURL_DISABLE_HTTP
     if(cf->conn->bits.tunnel_proxy) {
       result = Curl_cf_http_proxy_insert_after(cf, data);
       if(result)
@@ -1324,7 +433,7 @@ connect_sub_chain:
 #endif /* !CURL_DISABLE_PROXY */
 
   if(ctx->state < CF_SETUP_CNNCT_HAPROXY) {
-#if !defined(CURL_DISABLE_PROXY)
+#ifndef CURL_DISABLE_PROXY
     if(data->set.haproxyprotocol) {
       if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
         failf(data, "haproxy protocol not support with SSL "
@@ -1456,21 +565,6 @@ out:
   return result;
 }
 
-#ifdef UNITTESTS
-/* used by unit2600.c */
-void Curl_debug_set_transport_provider(int transport,
-                                       cf_ip_connect_create *cf_create)
-{
-  size_t i;
-  for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
-    if(transport == transport_providers[i].transport) {
-      transport_providers[i].cf_create = cf_create;
-      return;
-    }
-  }
-}
-#endif /* UNITTESTS */
-
 CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
                                     struct Curl_easy *data,
                                     int transport,
@@ -1503,7 +597,7 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
   Curl_resolv_unlink(data, &data->state.dns[sockindex]);
   data->state.dns[sockindex] = dns;
 
-#if !defined(CURL_DISABLE_HTTP)
+#ifndef CURL_DISABLE_HTTP
   if(!conn->cfilter[sockindex] &&
      conn->handler->protocol == CURLPROTO_HTTPS) {
     DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE);
@@ -1511,7 +605,7 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
     if(result)
       goto out;
   }
-#endif /* !defined(CURL_DISABLE_HTTP) */
+#endif /* !CURL_DISABLE_HTTP */
 
   /* Still no cfilter set, apply default. */
   if(!conn->cfilter[sockindex]) {

+ 0 - 23
Utilities/cmcurl/lib/connect.h

@@ -107,23 +107,6 @@ void Curl_conncontrol(struct connectdata *conn,
 #define connkeep(x,y) Curl_conncontrol(x, CONNCTRL_KEEP)
 #endif
 
-/**
- * Create a cfilter for making an "ip" connection to the
- * given address, using parameters from `conn`. The "ip" connection
- * can be a TCP socket, a UDP socket or even a QUIC connection.
- *
- * It MUST use only the supplied `ai` for its connection attempt.
- *
- * Such a filter may be used in "happy eyeball" scenarios, and its
- * `connect` implementation needs to support non-blocking. Once connected,
- * it MAY be installed in the connection filter chain to serve transfers.
- */
-typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf,
-                                      struct Curl_easy *data,
-                                      struct connectdata *conn,
-                                      const struct Curl_addrinfo *ai,
-                                      int transport);
-
 CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
                                     struct Curl_easy *data,
                                     int transport,
@@ -140,12 +123,6 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
                          struct Curl_dns_entry *dns,
                          int ssl_mode);
 
-extern struct Curl_cftype Curl_cft_happy_eyeballs;
 extern struct Curl_cftype Curl_cft_setup;
 
-#ifdef UNITTESTS
-void Curl_debug_set_transport_provider(int transport,
-                                       cf_ip_connect_create *cf_create);
-#endif
-
 #endif /* HEADER_CURL_CONNECT_H */

+ 12 - 12
Utilities/cmcurl/lib/content_encoding.c

@@ -96,7 +96,7 @@ struct zlib_writer {
 static voidpf
 zalloc_cb(voidpf opaque, unsigned int items, unsigned int size)
 {
-  (void) opaque;
+  (void)opaque;
   /* not a typo, keep it calloc() */
   return (voidpf) calloc(items, size);
 }
@@ -104,7 +104,7 @@ zalloc_cb(voidpf opaque, unsigned int items, unsigned int size)
 static void
 zfree_cb(voidpf opaque, voidpf ptr)
 {
-  (void) opaque;
+  (void)opaque;
   free(ptr);
 }
 
@@ -411,7 +411,7 @@ static CURLcode brotli_do_init(struct Curl_easy *data,
                                struct Curl_cwriter *writer)
 {
   struct brotli_writer *bp = (struct brotli_writer *) writer;
-  (void) data;
+  (void)data;
 
   bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL);
   return bp->br ? CURLE_OK : CURLE_OUT_OF_MEMORY;
@@ -466,7 +466,7 @@ static void brotli_do_close(struct Curl_easy *data,
                             struct Curl_cwriter *writer)
 {
   struct brotli_writer *bp = (struct brotli_writer *) writer;
-  (void) data;
+  (void)data;
 
   if(bp->br) {
     BrotliDecoderDestroyInstance(bp->br);
@@ -671,9 +671,9 @@ static CURLcode error_do_write(struct Curl_easy *data,
                                struct Curl_cwriter *writer, int type,
                                const char *buf, size_t nbytes)
 {
-  (void) writer;
-  (void) buf;
-  (void) nbytes;
+  (void)writer;
+  (void)buf;
+  (void)nbytes;
 
   if(!(type & CLIENTWRITE_BODY) || !nbytes)
     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
@@ -689,8 +689,8 @@ static CURLcode error_do_write(struct Curl_easy *data,
 static void error_do_close(struct Curl_easy *data,
                            struct Curl_cwriter *writer)
 {
-  (void) data;
-  (void) writer;
+  (void)data;
+  (void)writer;
 }
 
 static const struct Curl_cwtype error_writer = {
@@ -843,9 +843,9 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
 CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
                                      const char *enclist, int is_transfer)
 {
-  (void) data;
-  (void) enclist;
-  (void) is_transfer;
+  (void)data;
+  (void)enclist;
+  (void)is_transfer;
   return CURLE_NOT_BUILT_IN;
 }
 

+ 48 - 42
Utilities/cmcurl/lib/cookie.c

@@ -107,7 +107,7 @@ static void strstore(char **str, const char *newstr, size_t len);
 */
 static void cap_expires(time_t now, struct Cookie *co)
 {
-  if((TIME_T_MAX - COOKIES_MAXAGE - 30) > now) {
+  if(co->expires && (TIME_T_MAX - COOKIES_MAXAGE - 30) > now) {
     timediff_t cap = now + COOKIES_MAXAGE;
     if(co->expires > cap) {
       cap += 30;
@@ -167,13 +167,13 @@ static bool pathmatch(const char *cookie_path, const char *uri_path)
 
   /* cookie_path must not have last '/' separator. ex: /sample */
   cookie_path_len = strlen(cookie_path);
-  if(1 == cookie_path_len) {
+  if(cookie_path_len == 1) {
     /* cookie_path must be '/' */
     return TRUE;
   }
 
   /* #-fragments are already cut off! */
-  if(0 == strlen(uri_path) || uri_path[0] != '/')
+  if(strlen(uri_path) == 0 || uri_path[0] != '/')
     uri_path = "/";
 
   /*
@@ -296,9 +296,9 @@ static char *sanitize_cookie_path(const char *cookie_path)
     /* Let cookie-path be the default-path. */
     return strdup("/");
 
-  /* remove trailing slash */
+  /* remove trailing slash when path is non-empty */
   /* convert /hoge/ to /hoge */
-  if(len && cookie_path[len - 1] == '/')
+  if(len > 1 && cookie_path[len - 1] == '/')
     len--;
 
   return Curl_memdup0(cookie_path, len);
@@ -388,17 +388,17 @@ static void remove_expired(struct CookieInfo *ci)
     for(n = Curl_llist_head(&ci->cookielist[i]); n; n = e) {
       co = Curl_node_elem(n);
       e = Curl_node_next(n);
-      if(co->expires && co->expires < now) {
-        Curl_node_remove(n);
-        freecookie(co);
-        ci->numcookies--;
-      }
-      else {
-        /*
-         * If this cookie has an expiration timestamp earlier than what we
-         * have seen so far then record it for the next round of expirations.
-         */
-        if(co->expires && co->expires < ci->next_expiration)
+      if(co->expires) {
+        if(co->expires < now) {
+          Curl_node_remove(n);
+          freecookie(co);
+          ci->numcookies--;
+        }
+        else if(co->expires < ci->next_expiration)
+          /*
+           * If this cookie has an expiration timestamp earlier than what we
+           * have seen so far then record it for the next round of expirations.
+           */
           ci->next_expiration = co->expires;
       }
     }
@@ -666,7 +666,6 @@ parse_cookie_header(struct Curl_easy *data,
         if(*maxage == '\"')
           maxage++;
         rc = curlx_str_number(&maxage, &co->expires, CURL_OFF_T_MAX);
-
         switch(rc) {
         case STRE_OVERFLOW:
           /* overflow, used max value */
@@ -678,8 +677,7 @@ parse_cookie_header(struct Curl_easy *data,
           break;
         case STRE_OK:
           if(!co->expires)
-            /* already expired */
-            co->expires = 1;
+            co->expires = 1; /* expire now */
           else if(CURL_OFF_T_MAX - now < co->expires)
             /* would overflow */
             co->expires = CURL_OFF_T_MAX;
@@ -698,18 +696,15 @@ parse_cookie_header(struct Curl_easy *data,
            * will be treated as a session cookie
            */
           char dbuf[MAX_DATE_LENGTH + 1];
+          time_t date = 0;
           memcpy(dbuf, curlx_str(&val), curlx_strlen(&val));
           dbuf[curlx_strlen(&val)] = 0;
-          co->expires = Curl_getdate_capped(dbuf);
-
-          /*
-           * Session cookies have expires set to 0 so if we get that back
-           * from the date parser let's add a second to make it a
-           * non-session cookie
-           */
-          if(co->expires == 0)
-            co->expires = 1;
-          else if(co->expires < 0)
+          if(!Curl_getdate_capped(dbuf, &date)) {
+            if(!date)
+              date++;
+            co->expires = (curl_off_t)date;
+          }
+          else
             co->expires = 0;
           cap_expires(now, co);
         }
@@ -872,7 +867,7 @@ parse_netscape(struct Cookie *co,
       break;
     }
   }
-  if(6 == fields) {
+  if(fields == 6) {
     /* we got a cookie with blank contents, fix it */
     co->value = strdup("");
     if(!co->value)
@@ -881,7 +876,7 @@ parse_netscape(struct Cookie *co,
       fields++;
   }
 
-  if(7 != fields)
+  if(fields != 7)
     /* we did not find the sufficient number of fields */
     return CERR_FIELDS;
 
@@ -965,7 +960,7 @@ replace_existing(struct Curl_easy *data,
          clist->spath && co->spath && /* both have paths */
          clist->secure && !co->secure && !secure) {
         size_t cllen;
-        const char *sep;
+        const char *sep = NULL;
 
         /*
          * A non-secure cookie may not overlay an existing secure cookie.
@@ -974,8 +969,9 @@ replace_existing(struct Curl_easy *data,
          * "/loginhelper" is ok.
          */
 
-        sep = strchr(clist->spath + 1, '/');
-
+        DEBUGASSERT(clist->spath[0]);
+        if(clist->spath[0])
+          sep = strchr(clist->spath + 1, '/');
         if(sep)
           cllen = sep - clist->spath;
         else
@@ -1102,7 +1098,7 @@ Curl_cookie_add(struct Curl_easy *data,
 
   if(!ci->running &&    /* read from a file */
      ci->newsession &&  /* clean session cookies */
-     !co->expires)      /* this is a session cookie since it does not expire */
+     !co->expires)      /* this is a session cookie */
     goto fail;
 
   co->livecookie = ci->running;
@@ -1293,6 +1289,14 @@ static int cookie_sort_ct(const void *p1, const void *p2)
   return (c2->creationtime > c1->creationtime) ? 1 : -1;
 }
 
+bool Curl_secure_context(struct connectdata *conn, const char *host)
+{
+  return conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) ||
+    curl_strequal("localhost", host) ||
+    !strcmp(host, "127.0.0.1") ||
+    !strcmp(host, "::1");
+}
+
 /*
  * Curl_cookie_getlist
  *
@@ -1305,15 +1309,17 @@ static int cookie_sort_ct(const void *p1, const void *p2)
  * Returns 0 when there is a list returned. Otherwise non-zero.
  */
 int Curl_cookie_getlist(struct Curl_easy *data,
-                        struct CookieInfo *ci,
-                        const char *host, const char *path,
-                        bool secure,
+                        struct connectdata *conn,
+                        const char *host,
                         struct Curl_llist *list)
 {
   size_t matches = 0;
-  bool is_ip;
+  const bool is_ip = Curl_host_is_ipnum(host);
   const size_t myhash = cookiehash(host);
   struct Curl_llist_node *n;
+  const bool secure = Curl_secure_context(conn, host);
+  struct CookieInfo *ci = data->cookies;
+  const char *path = data->state.up.path;
 
   Curl_llist_init(list, NULL);
 
@@ -1323,9 +1329,6 @@ int Curl_cookie_getlist(struct Curl_easy *data,
   /* at first, remove expired cookies */
   remove_expired(ci);
 
-  /* check if host is an IP(v4|v6) address */
-  is_ip = Curl_host_is_ipnum(host);
-
   for(n = Curl_llist_head(&ci->cookielist[myhash]);
       n; n = Curl_node_next(n)) {
     struct Cookie *co = Curl_node_elem(n);
@@ -1614,6 +1617,9 @@ static struct curl_slist *cookie_list(struct Curl_easy *data)
   if(!data->cookies || (data->cookies->numcookies == 0))
     return NULL;
 
+  /* at first, remove expired cookies */
+  remove_expired(data->cookies);
+
   for(i = 0; i < COOKIE_HASH_SIZE; i++) {
     for(n = Curl_llist_head(&data->cookies->cookielist[i]); n;
         n = Curl_node_next(n)) {

+ 5 - 5
Utilities/cmcurl/lib/cookie.h

@@ -105,21 +105,21 @@ struct CookieInfo {
 #define MAX_COOKIE_SEND_AMOUNT 150
 
 struct Curl_easy;
+struct connectdata;
+
 /*
  * Add a cookie to the internal list of cookies. The domain and path arguments
  * are only used if the header boolean is TRUE.
  */
 
+bool Curl_secure_context(struct connectdata *conn, const char *host);
 struct Cookie *Curl_cookie_add(struct Curl_easy *data,
                                struct CookieInfo *c, bool header,
                                bool noexpiry, const char *lineptr,
                                const char *domain, const char *path,
                                bool secure);
-
-int Curl_cookie_getlist(struct Curl_easy *data,
-                        struct CookieInfo *c, const char *host,
-                        const char *path, bool secure,
-                        struct Curl_llist *list);
+int Curl_cookie_getlist(struct Curl_easy *data, struct connectdata *conn,
+                        const char *host, struct Curl_llist *list);
 void Curl_cookie_clearall(struct CookieInfo *cookies);
 void Curl_cookie_clearsess(struct CookieInfo *cookies);
 

+ 27 - 12
Utilities/cmcurl/lib/cshutdn.c

@@ -486,19 +486,25 @@ void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
 {
   if(Curl_llist_head(&cshutdn->list)) {
     struct Curl_llist_node *e;
+    struct easy_pollset ps;
 
+    Curl_pollset_init(&ps);
     for(e = Curl_llist_head(&cshutdn->list); e;
         e = Curl_node_next(e)) {
-      struct easy_pollset ps;
       unsigned int i;
       struct connectdata *conn = Curl_node_elem(e);
-      memset(&ps, 0, sizeof(ps));
+      CURLcode result;
+
+      Curl_pollset_reset(&ps);
       Curl_attach_connection(data, conn);
-      Curl_conn_adjust_pollset(data, conn, &ps);
+      result = Curl_conn_adjust_pollset(data, conn, &ps);
       Curl_detach_connection(data);
 
-      for(i = 0; i < ps.num; i++) {
-#if defined(__DJGPP__)
+      if(result)
+        continue;
+
+      for(i = 0; i < ps.n; i++) {
+#ifdef __DJGPP__
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Warith-conversion"
 #endif
@@ -506,7 +512,7 @@ void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
           FD_SET(ps.sockets[i], read_fd_set);
         if(ps.actions[i] & CURL_POLL_OUT)
           FD_SET(ps.sockets[i], write_fd_set);
-#if defined(__DJGPP__)
+#ifdef __DJGPP__
 #pragma GCC diagnostic pop
 #endif
         if((ps.actions[i] & (CURL_POLL_OUT | CURL_POLL_IN)) &&
@@ -514,6 +520,7 @@ void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
           *maxfd = (int)ps.sockets[i];
       }
     }
+    Curl_pollset_cleanup(&ps);
   }
 }
 
@@ -528,17 +535,21 @@ unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
     struct Curl_llist_node *e;
     struct easy_pollset ps;
     struct connectdata *conn;
+    CURLcode result;
 
+    Curl_pollset_init(&ps);
     for(e = Curl_llist_head(&cshutdn->list); e;
         e = Curl_node_next(e)) {
       conn = Curl_node_elem(e);
-      memset(&ps, 0, sizeof(ps));
+      Curl_pollset_reset(&ps);
       Curl_attach_connection(data, conn);
-      Curl_conn_adjust_pollset(data, conn, &ps);
+      result = Curl_conn_adjust_pollset(data, conn, &ps);
       Curl_detach_connection(data);
 
-      need += Curl_waitfds_add_ps(cwfds, &ps);
+      if(!result)
+        need += Curl_waitfds_add_ps(cwfds, &ps);
     }
+    Curl_pollset_cleanup(&ps);
   }
   return need;
 }
@@ -554,20 +565,24 @@ CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn,
     struct easy_pollset ps;
     struct connectdata *conn;
 
+    Curl_pollset_init(&ps);
     for(e = Curl_llist_head(&cshutdn->list); e;
         e = Curl_node_next(e)) {
       conn = Curl_node_elem(e);
-      memset(&ps, 0, sizeof(ps));
+      Curl_pollset_reset(&ps);
       Curl_attach_connection(data, conn);
-      Curl_conn_adjust_pollset(data, conn, &ps);
+      result = Curl_conn_adjust_pollset(data, conn, &ps);
       Curl_detach_connection(data);
 
-      result = Curl_pollfds_add_ps(cpfds, &ps);
+      if(!result)
+        result = Curl_pollfds_add_ps(cpfds, &ps);
       if(result) {
+        Curl_pollset_cleanup(&ps);
         Curl_pollfds_cleanup(cpfds);
         goto out;
       }
     }
+    Curl_pollset_cleanup(&ps);
   }
 out:
   return result;

+ 4 - 12
Utilities/cmcurl/lib/curl_addrinfo.c

@@ -68,7 +68,7 @@
  */
 
 #if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
-    defined(__OPTIMIZE__) && defined(__unix__) &&  defined(__i386__)
+  defined(__OPTIMIZE__) && defined(__unix__) &&  defined(__i386__)
   /* workaround icc 9.1 optimizer issue */
 # define vqualifier volatile
 #else
@@ -398,11 +398,7 @@ Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port)
     addr = (void *)ai->ai_addr; /* storage area for this info */
 
     memcpy(&addr->sin_addr, inaddr, sizeof(struct in_addr));
-#ifdef __MINGW32__
-    addr->sin_family = (short)af;
-#else
     addr->sin_family = (CURL_SA_FAMILY_T)af;
-#endif
     addr->sin_port = htons((unsigned short)port);
     break;
 
@@ -411,11 +407,7 @@ Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port)
     addr6 = (void *)ai->ai_addr; /* storage area for this info */
 
     memcpy(&addr6->sin6_addr, inaddr, sizeof(struct in6_addr));
-#ifdef __MINGW32__
-    addr6->sin6_family = (short)af;
-#else
     addr6->sin6_family = (CURL_SA_FAMILY_T)af;
-#endif
     addr6->sin6_port = htons((unsigned short)port);
     break;
 #endif
@@ -521,7 +513,7 @@ curl_dbg_freeaddrinfo(struct addrinfo *freethis,
   freeaddrinfo(freethis);
 #endif
 }
-#endif /* defined(CURLDEBUG) && defined(HAVE_FREEADDRINFO) */
+#endif /* CURLDEBUG && HAVE_FREEADDRINFO */
 
 
 #if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO)
@@ -552,7 +544,7 @@ curl_dbg_getaddrinfo(const char *hostname,
 #else
   int res = getaddrinfo(hostname, service, hints, result);
 #endif
-  if(0 == res)
+  if(res == 0)
     /* success */
     curl_dbg_log("ADDR %s:%d getaddrinfo() = %p\n",
                  source, line, (void *)*result);
@@ -561,7 +553,7 @@ curl_dbg_getaddrinfo(const char *hostname,
                  source, line);
   return res;
 }
-#endif /* defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) */
+#endif /* CURLDEBUG && HAVE_GETADDRINFO */
 
 #if defined(HAVE_GETADDRINFO) && defined(USE_RESOLVE_ON_IPS)
 /*

+ 0 - 6
Utilities/cmcurl/lib/curl_config.h.cmake

@@ -329,9 +329,6 @@
 /* Define to 1 if symbol `sa_family_t' exists */
 #cmakedefine HAVE_SA_FAMILY_T 1
 
-/* Define to 1 if symbol `ADDRESS_FAMILY' exists */
-#cmakedefine HAVE_ADDRESS_FAMILY 1
-
 /* Define to 1 if you have the ioctlsocket function. */
 #cmakedefine HAVE_IOCTLSOCKET 1
 
@@ -770,9 +767,6 @@ ${SIZEOF_TIME_T_CODE}
 /* Define to 1 if you have the quiche_conn_set_qlog_fd function. */
 #cmakedefine HAVE_QUICHE_CONN_SET_QLOG_FD 1
 
-/* to enable msh3 */
-#cmakedefine USE_MSH3 1
-
 /* if Unix domain sockets are enabled  */
 #cmakedefine USE_UNIX_SOCKETS 1
 

+ 3 - 4
Utilities/cmcurl/lib/curl_fnmatch.c

@@ -74,14 +74,13 @@ typedef enum {
 static int parsekeyword(const unsigned char **pattern, unsigned char *charset)
 {
   parsekey_state state = CURLFNM_PKW_INIT;
-#define KEYLEN 10
-  char keyword[KEYLEN] = { 0 };
-  int i;
+  char keyword[10] = { 0 };
+  size_t i;
   const unsigned char *p = *pattern;
   bool found = FALSE;
   for(i = 0; !found; i++) {
     char c = (char)*p++;
-    if(i >= KEYLEN)
+    if(i >= sizeof(keyword))
       return SETCHARSET_FAIL;
     switch(state) {
     case CURLFNM_PKW_INIT:

+ 2 - 2
Utilities/cmcurl/lib/curl_gethostname.c

@@ -46,8 +46,8 @@ int Curl_gethostname(char * const name, GETHOSTNAME_TYPE_ARG2 namelen)
 #ifndef HAVE_GETHOSTNAME
 
   /* Allow compilation and return failure when unavailable */
-  (void) name;
-  (void) namelen;
+  (void)name;
+  (void)namelen;
   return -1;
 
 #else

+ 1 - 1
Utilities/cmcurl/lib/curl_gssapi.c

@@ -34,7 +34,7 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#if defined(__GNUC__)
+#ifdef __GNUC__
 #define CURL_ALIGN8  __attribute__((aligned(8)))
 #else
 #define CURL_ALIGN8

+ 1 - 1
Utilities/cmcurl/lib/curl_hmac.h

@@ -26,7 +26,7 @@
 
 #if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) ||      \
   !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) ||   \
-  defined(USE_SSL)
+  defined(USE_LIBSSH2) || defined(USE_SSL)
 
 #include <curl/curl.h>
 

+ 2 - 2
Utilities/cmcurl/lib/curl_md4.h

@@ -27,13 +27,13 @@
 #include "curl_setup.h"
 #include <curl/curl.h>
 
-#if defined(USE_CURL_NTLM_CORE)
+#ifdef USE_CURL_NTLM_CORE
 
 #define MD4_DIGEST_LENGTH 16
 
 CURLcode Curl_md4it(unsigned char *output, const unsigned char *input,
                     const size_t len);
 
-#endif /* defined(USE_CURL_NTLM_CORE) */
+#endif /* USE_CURL_NTLM_CORE */
 
 #endif /* HEADER_CURL_MD4_H */

+ 2 - 2
Utilities/cmcurl/lib/curl_md5.h

@@ -24,8 +24,8 @@
  *
  ***************************************************************************/
 
-#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \
-    || !defined(CURL_DISABLE_DIGEST_AUTH)
+#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \
+  !defined(CURL_DISABLE_DIGEST_AUTH)
 
 #include "curl_hmac.h"
 

+ 31 - 22
Utilities/cmcurl/lib/vquic/curl_msh3.h → Utilities/cmcurl/lib/curl_mem_undef.h

@@ -1,5 +1,3 @@
-#ifndef HEADER_CURL_VQUIC_CURL_MSH3_H
-#define HEADER_CURL_VQUIC_CURL_MSH3_H
 /***************************************************************************
  *                                  _   _ ____  _
  *  Project                     ___| | | |  _ \| |
@@ -24,23 +22,34 @@
  *
  ***************************************************************************/
 
-#include "../curl_setup.h"
-
-#ifdef USE_MSH3
-
-#include <msh3.h>
-
-void Curl_msh3_ver(char *p, size_t len);
-
-CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
-                             struct Curl_easy *data,
-                             struct connectdata *conn,
-                             const struct Curl_addrinfo *ai);
-
-bool Curl_conn_is_msh3(const struct Curl_easy *data,
-                       const struct connectdata *conn,
-                       int sockindex);
-
-#endif /* USE_MSQUIC */
-
-#endif /* HEADER_CURL_VQUIC_CURL_MSH3_H */
+/* Unset redefined system symbols. */
+
+#undef strdup
+#undef malloc
+#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)
+#endif
+#undef fdopen
+#undef fclose
+
+#undef HEADER_CURL_MEMORY_H
+#undef HEADER_CURL_MEMDEBUG_H

+ 0 - 68
Utilities/cmcurl/lib/curl_memory.h

@@ -54,74 +54,6 @@
  *
  */
 
-#ifdef HEADER_CURL_MEMDEBUG_H
-/* cleanup after memdebug.h */
-
-#ifdef CURLDEBUG
-
-#undef strdup
-#undef malloc
-#undef calloc
-#undef realloc
-#undef free
-#undef send
-#undef recv
-
-#ifdef _WIN32
-#undef _tcsdup
-#endif
-
-#undef socket
-#undef accept
-#ifdef HAVE_ACCEPT4
-#undef accept4
-#endif
-#ifdef HAVE_SOCKETPAIR
-#undef socketpair
-#endif
-
-/* sclose is probably already defined, redefine it! */
-#undef sclose
-#define sclose(x)  CURL_SCLOSE(x)
-#undef fopen
-#ifdef CURL_FOPEN
-#define fopen(fname, mode)  CURL_FOPEN(fname, mode)
-#endif
-#undef fdopen
-#undef fclose
-
-#endif /* CURLDEBUG */
-
-#undef HEADER_CURL_MEMDEBUG_H
-#endif /* HEADER_CURL_MEMDEBUG_H */
-
-/*
-** Following section applies even when CURLDEBUG is not defined.
-*/
-
-#undef fake_sclose
-
-#ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS /* only if not already done */
-/*
- * The following memory function replacement typedef's are COPIED from
- * curl/curl.h and MUST match the originals. We copy them to avoid having to
- * include curl/curl.h here. We avoid that include since it includes stdio.h
- * and other headers that may get messed up with defines done here.
- */
-typedef void *(*curl_malloc_callback)(size_t size);
-typedef void (*curl_free_callback)(void *ptr);
-typedef void *(*curl_realloc_callback)(void *ptr, size_t size);
-typedef char *(*curl_strdup_callback)(const char *str);
-typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size);
-#define CURL_DID_MEMORY_FUNC_TYPEDEFS
-#endif
-
-extern curl_malloc_callback Curl_cmalloc;
-extern curl_free_callback Curl_cfree;
-extern curl_realloc_callback Curl_crealloc;
-extern curl_strdup_callback Curl_cstrdup;
-extern curl_calloc_callback Curl_ccalloc;
-
 #ifndef CURLDEBUG
 
 /*

+ 18 - 23
Utilities/cmcurl/lib/curl_ntlm_core.c

@@ -24,7 +24,7 @@
 
 #include "curl_setup.h"
 
-#if defined(USE_CURL_NTLM_CORE)
+#ifdef USE_CURL_NTLM_CORE
 
 /*
  * NTLM details:
@@ -51,39 +51,35 @@
      in NTLM type-3 messages.
  */
 
-#if defined(USE_OPENSSL)
+#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>
-  #if !defined(NO_DES3)
+  #ifndef NO_DES3
     #define USE_OPENSSL_DES
   #endif
 #endif
 
-#if defined(USE_OPENSSL_DES)
+#ifdef USE_OPENSSL_DES
 
-#if defined(USE_OPENSSL)
+#ifdef USE_OPENSSL
 #  include <openssl/des.h>
 #  include <openssl/md5.h>
 #  include <openssl/ssl.h>
 #  include <openssl/rand.h>
-#  if defined(OPENSSL_IS_AWSLC)
+#  ifdef OPENSSL_IS_AWSLC  /* for versions 1.2.0 to 1.30.1 */
 #    define DES_set_key_unchecked (void)DES_set_key
-#    define DESKEYARG(x) *x
-#    define DESKEY(x) &x
-#  else
-#    define DESKEYARG(x) *x
-#    define DESKEY(x) &x
 #  endif
+#  define DESKEY(x) &x
 #else
 #  include <wolfssl/openssl/des.h>
 #  include <wolfssl/openssl/md5.h>
 #  include <wolfssl/openssl/ssl.h>
 #  include <wolfssl/openssl/rand.h>
-#  if defined(OPENSSL_COEXIST)
+#  ifdef OPENSSL_COEXIST
 #    define DES_key_schedule WOLFSSL_DES_key_schedule
 #    define DES_cblock WOLFSSL_DES_cblock
 #    define DES_set_odd_parity wolfSSL_DES_set_odd_parity
@@ -91,12 +87,11 @@
 #    define DES_set_key_unchecked wolfSSL_DES_set_key_unchecked
 #    define DES_ecb_encrypt wolfSSL_DES_ecb_encrypt
 #    define DESKEY(x) ((WOLFSSL_DES_key_schedule *)(x))
-#    define DESKEYARG(x) *x
 #  else
-#    define DESKEYARG(x) *x
 #    define DESKEY(x) &x
 #  endif
 #endif
+#define DESKEYARG(x) *x
 
 #elif defined(USE_GNUTLS)
 
@@ -129,7 +124,7 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#if !defined(CURL_NTLM_NOT_SUPPORTED)
+#ifndef CURL_NTLM_NOT_SUPPORTED
 /*
 * Turns a 56-bit key into being 64-bit wide.
 */
@@ -146,7 +141,7 @@ static void extend_key_56_to_64(const unsigned char *key_56, char *key)
 }
 #endif
 
-#if defined(USE_OPENSSL_DES)
+#ifdef USE_OPENSSL_DES
 /*
  * Turns a 56-bit key into a 64-bit, odd parity key and sets the key. The
  * key schedule ks is also set.
@@ -277,7 +272,7 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out,
   return TRUE;
 }
 
-#endif /* defined(USE_WIN32_CRYPTO) */
+#endif /* USE_WIN32_CRYPTO */
 
  /*
   * takes a 21 byte array and treats it as 3 56-bit DES keys. The
@@ -288,7 +283,7 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys,
                             const unsigned char *plaintext,
                             unsigned char *results)
 {
-#if defined(USE_OPENSSL_DES)
+#ifdef USE_OPENSSL_DES
   DES_key_schedule ks;
 
   setup_des_key(keys, DESKEY(ks));
@@ -329,7 +324,7 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password,
                                    unsigned char *lmbuffer /* 21 bytes */)
 {
   unsigned char pw[14];
-#if !defined(CURL_NTLM_NOT_SUPPORTED)
+#ifndef CURL_NTLM_NOT_SUPPORTED
   static const unsigned char magic[] = {
     0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 /* i.e. KGS!@#$% */
   };
@@ -342,7 +337,7 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password,
   {
     /* Create LanManager hashed password. */
 
-#if defined(USE_OPENSSL_DES)
+#ifdef USE_OPENSSL_DES
     DES_key_schedule ks;
 
     setup_des_key(pw, DESKEY(ks));
@@ -380,7 +375,7 @@ static void ascii_to_unicode_le(unsigned char *dest, const char *src,
   }
 }
 
-#if !defined(USE_WINDOWS_SSPI)
+#ifndef USE_WINDOWS_SSPI
 
 static void ascii_uppercase_to_unicode_le(unsigned char *dest,
                                           const char *src, size_t srclen)
@@ -404,7 +399,7 @@ CURLcode Curl_ntlm_core_mk_nt_hash(const char *password,
   size_t len = strlen(password);
   unsigned char *pw;
   CURLcode result;
-  if(len > SIZE_T_MAX/2) /* avoid integer overflow */
+  if(len > SIZE_MAX/2) /* avoid integer overflow */
     return CURLE_OUT_OF_MEMORY;
   pw = len ? malloc(len * 2) : (unsigned char *)strdup("");
   if(!pw)
@@ -422,7 +417,7 @@ CURLcode Curl_ntlm_core_mk_nt_hash(const char *password,
   return result;
 }
 
-#if !defined(USE_WINDOWS_SSPI)
+#ifndef USE_WINDOWS_SSPI
 
 #define NTLMv2_BLOB_SIGNATURE "\x01\x01\x00\x00"
 #define NTLMv2_BLOB_LEN       (44 -16 + ntlm->target_info_len + 4)

+ 2 - 2
Utilities/cmcurl/lib/curl_ntlm_core.h

@@ -26,7 +26,7 @@
 
 #include "curl_setup.h"
 
-#if defined(USE_CURL_NTLM_CORE)
+#ifdef USE_CURL_NTLM_CORE
 
 #include "vauth/vauth.h"
 
@@ -47,7 +47,7 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password,
 CURLcode Curl_ntlm_core_mk_nt_hash(const char *password,
                                    unsigned char *ntbuffer /* 21 bytes */);
 
-#if !defined(USE_WINDOWS_SSPI)
+#ifndef USE_WINDOWS_SSPI
 
 CURLcode Curl_hmac_md5(const unsigned char *key, unsigned int keylen,
                        const unsigned char *data, unsigned int datalen,

+ 32 - 32
Utilities/cmcurl/lib/curl_rtmp.c

@@ -81,10 +81,10 @@ const struct Curl_handler Curl_handler_rtmp = {
   rtmp_connect,                         /* connect_it */
   ZERO_NULL,                            /* connecting */
   ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* proto_pollset */
+  ZERO_NULL,                            /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  ZERO_NULL,                            /* perform_pollset */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
@@ -106,10 +106,10 @@ const struct Curl_handler Curl_handler_rtmpt = {
   rtmp_connect,                         /* connect_it */
   ZERO_NULL,                            /* connecting */
   ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* proto_pollset */
+  ZERO_NULL,                            /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  ZERO_NULL,                            /* perform_pollset */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
@@ -131,10 +131,10 @@ const struct Curl_handler Curl_handler_rtmpe = {
   rtmp_connect,                         /* connect_it */
   ZERO_NULL,                            /* connecting */
   ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* proto_pollset */
+  ZERO_NULL,                            /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  ZERO_NULL,                            /* perform_pollset */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
@@ -156,10 +156,10 @@ const struct Curl_handler Curl_handler_rtmpte = {
   rtmp_connect,                         /* connect_it */
   ZERO_NULL,                            /* connecting */
   ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* proto_pollset */
+  ZERO_NULL,                            /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  ZERO_NULL,                            /* perform_pollset */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
@@ -181,10 +181,10 @@ const struct Curl_handler Curl_handler_rtmps = {
   rtmp_connect,                         /* connect_it */
   ZERO_NULL,                            /* connecting */
   ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* proto_pollset */
+  ZERO_NULL,                            /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  ZERO_NULL,                            /* perform_pollset */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
@@ -206,10 +206,10 @@ const struct Curl_handler Curl_handler_rtmpts = {
   rtmp_connect,                         /* connect_it */
   ZERO_NULL,                            /* connecting */
   ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* proto_pollset */
+  ZERO_NULL,                            /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  ZERO_NULL,                            /* perform_pollset */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
@@ -296,10 +296,10 @@ static CURLcode rtmp_do(struct Curl_easy *data, bool *done)
 
   if(data->state.upload) {
     Curl_pgrsSetUploadSize(data, data->state.infilesize);
-    Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
+    Curl_xfer_setup_send(data, FIRSTSOCKET);
   }
   else
-    Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE);
+    Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
   *done = TRUE;
   return CURLE_OK;
 }
@@ -307,9 +307,9 @@ static CURLcode rtmp_do(struct Curl_easy *data, bool *done)
 static CURLcode rtmp_done(struct Curl_easy *data, CURLcode status,
                           bool premature)
 {
-  (void)data; /* unused */
-  (void)status; /* unused */
-  (void)premature; /* unused */
+  (void)data;
+  (void)status;
+  (void)premature;
 
   return CURLE_OK;
 }
@@ -334,7 +334,7 @@ static CURLcode rtmp_recv(struct Curl_easy *data, int sockindex, char *buf,
   CURLcode result = CURLE_OK;
   ssize_t nread;
 
-  (void)sockindex; /* unused */
+  (void)sockindex;
   *pnread = 0;
   if(!r)
     return CURLE_FAILED_INIT;
@@ -362,8 +362,8 @@ static CURLcode rtmp_send(struct Curl_easy *data, int sockindex,
   RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
   ssize_t nwritten;
 
-  (void)sockindex; /* unused */
-  (void)eos; /* unused */
+  (void)sockindex;
+  (void)eos;
   *pnwritten = 0;
   if(!r)
     return CURLE_FAILED_INIT;

+ 5 - 5
Utilities/cmcurl/lib/curl_sasl.c

@@ -222,7 +222,7 @@ static void sasl_state(struct SASL *sasl, struct Curl_easy *data,
     infof(data, "SASL %p state change from %s to %s",
           (void *)sasl, names[sasl->state], names[newstate]);
 #else
-  (void) data;
+  (void)data;
 #endif
 
   sasl->state = newstate;
@@ -557,7 +557,7 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
   /* Calculate the supported authentication mechanism, by decreasing order of
      security, as well as the initial response where appropriate */
   if(sasl_choose_external(data, &sctx) ||
-#if defined(USE_KERBEROS5)
+#ifdef USE_KERBEROS5
      sasl_choose_krb5(data, &sctx) ||
 #endif
 #ifdef USE_GSASL
@@ -615,8 +615,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
   struct bufref resp;
   const char *hostname;
   int port;
-#if defined(USE_KERBEROS5) || defined(USE_NTLM) \
-    || !defined(CURL_DISABLE_DIGEST_AUTH)
+#if defined(USE_KERBEROS5) || defined(USE_NTLM) || \
+  !defined(CURL_DISABLE_DIGEST_AUTH)
   const char *service = data->set.str[STRING_SERVICE_NAME] ?
     data->set.str[STRING_SERVICE_NAME] :
     sasl->params->service;
@@ -722,7 +722,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
   }
 #endif
 
-#if defined(USE_KERBEROS5)
+#ifdef USE_KERBEROS5
   case SASL_GSSAPI: {
     struct kerberos5data *krb5 = Curl_auth_krb5_get(conn);
     result = !krb5 ? CURLE_OUT_OF_MEMORY :

+ 168 - 28
Utilities/cmcurl/lib/curl_setup.h

@@ -75,6 +75,11 @@
 #endif
 #endif
 
+#if defined(__MINGW32__) && !defined(__MINGW32CE__) && \
+  (!defined(__MINGW64_VERSION_MAJOR) || (__MINGW64_VERSION_MAJOR < 3))
+#error "Building curl requires mingw-w64 3.0 or later"
+#endif
+
 /* Visual Studio 2008 is the minimum Visual Studio version we support.
    Workarounds for older versions of Visual Studio have been removed. */
 #if defined(_MSC_VER) && (_MSC_VER < 1500)
@@ -335,10 +340,10 @@
 #define CURL_CONC_MACROS(A,B) CURL_CONC_MACROS_(A,B)
 
 /* curl uses its own printf() function internally. It understands the GNU
- * format. Use this format, so that is matches the GNU format attribute we
+ * format. Use this format, so that it matches the GNU format attribute we
  * use with the MinGW compiler, allowing it to verify them at compile-time.
  */
-#ifdef  __MINGW32__
+#ifdef __MINGW32__
 #  undef CURL_FORMAT_CURL_OFF_T
 #  undef CURL_FORMAT_CURL_OFF_TU
 #  define CURL_FORMAT_CURL_OFF_T   "lld"
@@ -384,7 +389,7 @@
  * performing this task will result in a synthesized IPv6 address.
  */
 #if defined(__APPLE__) && !defined(USE_ARES)
-#define USE_RESOLVE_ON_IPS 1
+#  define USE_RESOLVE_ON_IPS 1
 #  if TARGET_OS_MAC && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && \
      defined(USE_IPV6)
 #    define CURL_MACOS_CALL_COPYPROXIES 1
@@ -474,6 +479,12 @@
 #include <curl/stdcheaders.h>
 #endif
 
+#if defined(HAVE_STDINT_H) || defined(USE_WOLFSSL)
+#include <stdint.h>
+#endif
+
+#include <limits.h>
+
 /* Default Windows file API selection.  */
 #ifdef _WIN32
 # if defined(_MSC_VER) && (_INTEGRAL_MAX_BITS >= 64)
@@ -564,11 +575,11 @@
 
 #ifndef SIZEOF_OFF_T
 #  if defined(__VMS) && !defined(__VAX)
-#    if defined(_LARGEFILE)
+#    ifdef _LARGEFILE
 #      define SIZEOF_OFF_T 8
 #    endif
 #  elif defined(__OS400__) && defined(__ILEC400__)
-#    if defined(_LARGE_FILES)
+#    ifdef _LARGE_FILES
 #      define SIZEOF_OFF_T 8
 #    endif
 #  elif defined(__MVS__) && defined(__IBMC__)
@@ -628,21 +639,21 @@
 #  endif
 #endif
 
-#ifndef SIZE_T_MAX
+#ifndef SIZE_MAX
 /* some limits.h headers have this defined, some do not */
 #if defined(SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > 4)
-#define SIZE_T_MAX 18446744073709551615U
+#define SIZE_MAX 18446744073709551615U
 #else
-#define SIZE_T_MAX 4294967295U
+#define SIZE_MAX 4294967295U
 #endif
 #endif
 
-#ifndef SSIZE_T_MAX
+#ifndef SSIZE_MAX
 /* some limits.h headers have this defined, some do not */
 #if defined(SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > 4)
-#define SSIZE_T_MAX 9223372036854775807
+#define SSIZE_MAX 9223372036854775807
 #else
-#define SSIZE_T_MAX 2147483647
+#define SSIZE_MAX 2147483647
 #endif
 #endif
 
@@ -789,20 +800,12 @@
  * Parameters should of course normally not be unused, but for example when
  * we have multiple implementations of the same interface it may happen.
  */
-
 #if defined(__GNUC__) && ((__GNUC__ >= 3) || \
   ((__GNUC__ == 2) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 7)))
-#  define UNUSED_PARAM __attribute__((__unused__))
 #  define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
-#elif defined(__IAR_SYSTEMS_ICC__)
-#  define UNUSED_PARAM __attribute__((__unused__))
-#  if (__VER__ >= 9040001)
-#    define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
-#  else
-#    define WARN_UNUSED_RESULT
-#  endif
+#elif defined(__IAR_SYSTEMS_ICC__) && (__VER__ >= 9040001)
+#  define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
 #else
-#  define UNUSED_PARAM /* NOTHING */
 #  define WARN_UNUSED_RESULT
 #endif
 
@@ -947,10 +950,10 @@ endings either CRLF or LF so 't' is appropriate.
 
 /* for systems that do not detect this in configure */
 #ifndef CURL_SA_FAMILY_T
-#  if defined(HAVE_SA_FAMILY_T)
-#    define CURL_SA_FAMILY_T sa_family_t
-#  elif defined(HAVE_ADDRESS_FAMILY)
+#  if defined(_WIN32) && !defined(UNDER_CE)
 #    define CURL_SA_FAMILY_T ADDRESS_FAMILY
+#  elif defined(HAVE_SA_FAMILY_T)
+#    define CURL_SA_FAMILY_T sa_family_t
 #  elif defined(__AMIGA__)
 #    define CURL_SA_FAMILY_T unsigned char
 #  else
@@ -971,15 +974,145 @@ endings either CRLF or LF so 't' is appropriate.
 
 #define CURL_ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
 
+#ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS /* only if not already done */
+/*
+ * The following memory function replacement typedef's are COPIED from
+ * curl/curl.h and MUST match the originals. We copy them to avoid having to
+ * include curl/curl.h here. We avoid that include since it includes stdio.h
+ * and other headers that may get messed up with defines done here.
+ */
+typedef void *(*curl_malloc_callback)(size_t size);
+typedef void (*curl_free_callback)(void *ptr);
+typedef void *(*curl_realloc_callback)(void *ptr, size_t size);
+typedef char *(*curl_strdup_callback)(const char *str);
+typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size);
+#define CURL_DID_MEMORY_FUNC_TYPEDEFS
+#endif
+
+extern curl_malloc_callback Curl_cmalloc;
+extern curl_free_callback Curl_cfree;
+extern curl_realloc_callback Curl_crealloc;
+extern curl_strdup_callback Curl_cstrdup;
+extern curl_calloc_callback Curl_ccalloc;
+
+/*
+ * Curl_safefree defined as a macro to allow MemoryTracking feature
+ * to log free() calls at same location where Curl_safefree is used.
+ * This macro also assigns NULL to given pointer when free'd.
+ */
+#define Curl_safefree(ptr) \
+  do { free((ptr)); (ptr) = NULL;} while(0)
+
 #ifdef CURLDEBUG
+#ifdef __clang__
+#  define ALLOC_FUNC         __attribute__((__malloc__))
+#  if __clang_major__ >= 4
+#  define ALLOC_SIZE(s)      __attribute__((__alloc_size__(s)))
+#  define ALLOC_SIZE2(n, s)  __attribute__((__alloc_size__(n, s)))
+#  else
+#  define ALLOC_SIZE(s)
+#  define ALLOC_SIZE2(n, s)
+#  endif
+#elif defined(__GNUC__) && __GNUC__ >= 3
+#  define ALLOC_FUNC         __attribute__((__malloc__))
+#  define ALLOC_SIZE(s)      __attribute__((__alloc_size__(s)))
+#  define ALLOC_SIZE2(n, s)  __attribute__((__alloc_size__(n, s)))
+#elif defined(_MSC_VER)
+#  define ALLOC_FUNC         __declspec(restrict)
+#  define ALLOC_SIZE(s)
+#  define ALLOC_SIZE2(n, s)
+#else
+#  define ALLOC_FUNC
+#  define ALLOC_SIZE(s)
+#  define ALLOC_SIZE2(n, s)
+#endif
+
+#include <curl/curl.h> /* for CURL_EXTERN */
+
+extern FILE *curl_dbg_logfile;
+
+/* memory functions */
+CURL_EXTERN void curl_dbg_free(void *ptr, int line, const char *source);
+CURL_EXTERN ALLOC_FUNC ALLOC_SIZE(1)
+  void *curl_dbg_malloc(size_t size, int line, const char *source);
+CURL_EXTERN ALLOC_FUNC ALLOC_SIZE2(1, 2)
+  void *curl_dbg_calloc(size_t n, size_t size, int line, const char *source);
+CURL_EXTERN ALLOC_SIZE(2)
+  void *curl_dbg_realloc(void *ptr, size_t size, int line, const char *source);
+CURL_EXTERN ALLOC_FUNC
+  char *curl_dbg_strdup(const char *str, int line, const char *src);
+#if defined(_WIN32) && defined(UNICODE)
+CURL_EXTERN ALLOC_FUNC
+  wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source);
+#endif
+
+CURL_EXTERN void curl_dbg_memdebug(const char *logname);
+CURL_EXTERN void curl_dbg_memlimit(long limit);
+CURL_EXTERN void curl_dbg_log(const char *format, ...) CURL_PRINTF(1, 2);
+
+/* file descriptor manipulators */
+CURL_EXTERN curl_socket_t curl_dbg_socket(int domain, int type, int protocol,
+                                          int line, const char *source);
+CURL_EXTERN void curl_dbg_mark_sclose(curl_socket_t sockfd,
+                                      int line, const char *source);
+CURL_EXTERN int curl_dbg_sclose(curl_socket_t sockfd,
+                                int line, const char *source);
+CURL_EXTERN curl_socket_t curl_dbg_accept(curl_socket_t s, void *a, void *alen,
+                                          int line, const char *source);
+#ifdef HAVE_ACCEPT4
+CURL_EXTERN curl_socket_t curl_dbg_accept4(curl_socket_t s, void *saddr,
+                                           void *saddrlen, int flags,
+                                           int line, const char *source);
+#endif
+#ifdef HAVE_SOCKETPAIR
+CURL_EXTERN int curl_dbg_socketpair(int domain, int type, int protocol,
+                                    curl_socket_t socket_vector[2],
+                                    int line, const char *source);
+#endif
+
+/* send/receive sockets */
+CURL_EXTERN SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd,
+                                         SEND_QUAL_ARG2 SEND_TYPE_ARG2 buf,
+                                         SEND_TYPE_ARG3 len,
+                                         SEND_TYPE_ARG4 flags, int line,
+                                         const char *source);
+CURL_EXTERN RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd,
+                                         RECV_TYPE_ARG2 buf,
+                                         RECV_TYPE_ARG3 len,
+                                         RECV_TYPE_ARG4 flags, int line,
+                                         const char *source);
+
+/* FILE functions */
+CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source);
+CURL_EXTERN ALLOC_FUNC
+  FILE *curl_dbg_fopen(const char *file, const char *mode,
+                       int line, const char *source);
+CURL_EXTERN ALLOC_FUNC
+  FILE *curl_dbg_fdopen(int filedes, const char *mode,
+                        int line, const char *source);
+
+#define sclose(sockfd) curl_dbg_sclose(sockfd,__LINE__,__FILE__)
+#define fake_sclose(sockfd) curl_dbg_mark_sclose(sockfd,__LINE__,__FILE__)
+
 #define CURL_GETADDRINFO(host,serv,hint,res) \
   curl_dbg_getaddrinfo(host, serv, hint, res, __LINE__, __FILE__)
 #define CURL_FREEADDRINFO(data) \
   curl_dbg_freeaddrinfo(data, __LINE__, __FILE__)
-#else
+
+#define CURL_ACCEPT(sock,addr,len) \
+  curl_dbg_accept(sock, addr, len, __LINE__, __FILE__)
+
+#else /* !CURLDEBUG */
+
+#define sclose(x) CURL_SCLOSE(x)
+#define fake_sclose(x) Curl_nop_stmt
+
 #define CURL_GETADDRINFO getaddrinfo
 #define CURL_FREEADDRINFO freeaddrinfo
-#endif
+
+#define CURL_ACCEPT accept
+
+#endif /* CURLDEBUG */
 
 /* Some versions of the Android NDK is missing the declaration */
 #if defined(HAVE_GETPWUID_R) && \
@@ -1001,7 +1134,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
 
 #if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \
     (defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)) || \
-    defined(USE_QUICHE) || defined(USE_MSH3)
+    defined(USE_QUICHE)
 
 #ifdef CURL_WITH_MULTI_SSL
 #error "MultiSSL combined with QUIC is not supported"
@@ -1010,6 +1143,11 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
 #define USE_HTTP3
 #endif
 
+/* WebAssembly builds have TCP_NODELAY, but runtime support is missing. */
+#ifndef __EMSCRIPTEN__
+#define CURL_TCP_NODELAY_SUPPORTED
+#endif
+
 /* Certain Windows implementations are not aligned with what curl expects,
    so always use the local one on this platform. E.g. the mingw-w64
    implementation can return wrong results for non-ASCII inputs. */
@@ -1018,7 +1156,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
 #endif
 
 #if defined(USE_UNIX_SOCKETS) && defined(_WIN32)
-#  if !defined(UNIX_PATH_MAX)
+#  ifndef UNIX_PATH_MAX
      /* Replicating logic present in afunix.h
         (distributed with newer Windows 10 SDK versions only) */
 #    define UNIX_PATH_MAX 108
@@ -1066,3 +1204,5 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
 #endif
 
 #endif /* HEADER_CURL_SETUP_H */
+
+#include "curl_mem_undef.h"

+ 0 - 6
Utilities/cmcurl/lib/curl_setup_once.h

@@ -63,10 +63,6 @@
 #include <unistd.h>
 #endif
 
-#if defined(HAVE_STDINT_H) || defined(USE_WOLFSSL)
-#include <stdint.h>
-#endif
-
 /* Macro to strip 'const' without triggering a compiler warning.
    Use it for APIs that do not or cannot support the const qualifier. */
 #ifdef HAVE_STDINT_H
@@ -206,8 +202,6 @@ struct timeval {
 #  define CURL_SCLOSE(x)  close((x))
 #endif
 
-#define sclose(x)  CURL_SCLOSE(x)
-
 /*
  * Stack-independent version of fcntl() on sockets:
  */

+ 2 - 2
Utilities/cmcurl/lib/curl_sha256.h

@@ -25,8 +25,8 @@
  *
  ***************************************************************************/
 
-#if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \
-    || defined(USE_LIBSSH2) || defined(USE_SSL)
+#if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \
+  defined(USE_LIBSSH2) || defined(USE_SSL)
 
 #include <curl/curl.h>
 #include "curl_hmac.h"

+ 50 - 68
Utilities/cmcurl/lib/curl_sha512_256.c

@@ -38,13 +38,12 @@
  * * Rustls
  * Skip the backend if it does not support the required algorithm */
 
-#if defined(USE_OPENSSL)
+#ifdef USE_OPENSSL
 #  include <openssl/opensslv.h>
 #  if (!defined(LIBRESSL_VERSION_NUMBER) && \
-        defined(OPENSSL_VERSION_NUMBER) && \
-        (OPENSSL_VERSION_NUMBER >= 0x10101000L)) || \
+       OPENSSL_VERSION_NUMBER >= 0x10101000L) || \
       (defined(LIBRESSL_VERSION_NUMBER) && \
-        (LIBRESSL_VERSION_NUMBER >= 0x3080000fL))
+       LIBRESSL_VERSION_NUMBER >= 0x3080000fL)
 #    include <openssl/opensslconf.h>
 #    if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_SHA512)
 #      include <openssl/evp.h>
@@ -78,12 +77,12 @@
 
 #if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_GNUTLS)
 #  include <nettle/sha.h>
-#  if defined(SHA512_256_DIGEST_SIZE)
+#  ifdef SHA512_256_DIGEST_SIZE
 #    define USE_GNUTLS_SHA512_256           1
 #  endif
 #endif /* ! HAS_SHA512_256_IMPLEMENTATION && USE_GNUTLS */
 
-#if defined(USE_OPENSSL_SHA512_256)
+#ifdef USE_OPENSSL_SHA512_256
 
 /* OpenSSL does not provide macros for SHA-512/256 sizes */
 
@@ -110,8 +109,7 @@ typedef EVP_MD_CTX *Curl_sha512_256_ctx;
  * @return CURLE_OK if succeed,
  *         error code otherwise
  */
-static CURLcode
-Curl_sha512_256_init(void *context)
+static CURLcode Curl_sha512_256_init(void *context)
 {
   Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
 
@@ -142,10 +140,9 @@ Curl_sha512_256_init(void *context)
  * @return CURLE_OK if succeed,
  *         error code otherwise
  */
-static CURLcode
-Curl_sha512_256_update(void *context,
-                       const unsigned char *data,
-                       size_t length)
+static CURLcode Curl_sha512_256_update(void *context,
+                                       const unsigned char *data,
+                                       size_t length)
 {
   Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
 
@@ -165,9 +162,7 @@ Curl_sha512_256_update(void *context,
  * @return CURLE_OK if succeed,
  *         error code otherwise
  */
-static CURLcode
-Curl_sha512_256_finish(unsigned char *digest,
-                       void *context)
+static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context)
 {
   CURLcode ret;
   Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
@@ -207,8 +202,7 @@ typedef struct sha512_256_ctx Curl_sha512_256_ctx;
  * @param context the calculation context
  * @return always CURLE_OK
  */
-static CURLcode
-Curl_sha512_256_init(void *context)
+static CURLcode Curl_sha512_256_init(void *context)
 {
   Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
 
@@ -229,10 +223,9 @@ Curl_sha512_256_init(void *context)
  * @param length number of bytes in @a data
  * @return always CURLE_OK
  */
-static CURLcode
-Curl_sha512_256_update(void *context,
-                       const unsigned char *data,
-                       size_t length)
+static CURLcode Curl_sha512_256_update(void *context,
+                                       const unsigned char *data,
+                                       size_t length)
 {
   Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
 
@@ -252,9 +245,8 @@ Curl_sha512_256_update(void *context,
  #             bytes
  * @return always CURLE_OK
  */
-static CURLcode
-Curl_sha512_256_finish(unsigned char *digest,
-                       void *context)
+static CURLcode Curl_sha512_256_finish(unsigned char *digest,
+                                       void *context)
 {
   Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
 
@@ -284,13 +276,13 @@ Curl_sha512_256_finish(unsigned char *digest,
 
 #if !defined(CURL_FORCEINLINE) && \
   defined(_MSC_VER) && !defined(__GNUC__) && !defined(__clang__)
-#  define CURL_FORCEINLINE __forceinline
+#define CURL_FORCEINLINE __forceinline
 #endif
 
-#if !defined(CURL_FORCEINLINE)
-   /* Assume that 'CURL_INLINE' keyword works or the
-    * macro was already defined correctly. */
-#  define CURL_FORCEINLINE CURL_INLINE
+/* Assume that 'CURL_INLINE' keyword works or the
+ * macro was already defined correctly. */
+#ifndef CURL_FORCEINLINE
+#define CURL_FORCEINLINE CURL_INLINE
 #endif
 
 /* Bits manipulation macros and functions.
@@ -320,11 +312,11 @@ Curl_sha512_256_finish(unsigned char *digest,
 /* Defined as a function. The macro version may duplicate the binary code
  * size as each argument is used twice, so if any calculation is used
  * as an argument, the calculation could be done twice. */
-static CURL_FORCEINLINE curl_uint64_t
-Curl_rotr64(curl_uint64_t value, unsigned int bits)
+static CURL_FORCEINLINE curl_uint64_t Curl_rotr64(curl_uint64_t value,
+                                                  unsigned int bits)
 {
   bits %= 64;
-  if(0 == bits)
+  if(bits == 0)
     return value;
   /* Defined in a form which modern compiler could optimize. */
   return (value >> bits) | (value << (64 - bits));
@@ -380,8 +372,7 @@ Curl_rotr64(curl_uint64_t value, unsigned int bits)
 /**
  * SHA-512/256 calculation context
  */
-struct Curl_sha512_256ctx
-{
+struct Curl_sha512_256ctx {
   /**
    * Intermediate hash value. The variable is properly aligned. Smart
    * compilers may automatically use fast load/store instruction for big
@@ -417,8 +408,7 @@ typedef struct Curl_sha512_256ctx Curl_sha512_256_ctx;
  * @param context the calculation context
  * @return always CURLE_OK
  */
-static CURLcode
-Curl_sha512_256_init(void *context)
+static CURLcode Curl_sha512_256_init(void *context)
 {
   struct Curl_sha512_256ctx *const ctx = (struct Curl_sha512_256ctx *)context;
 
@@ -453,9 +443,9 @@ Curl_sha512_256_init(void *context)
  * @param H     hash values
  * @param data  the data buffer with #CURL_SHA512_256_BLOCK_SIZE bytes block
  */
-static void
-Curl_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS],
-                          const void *data)
+static
+void Curl_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS],
+                               const void *data)
 {
   /* Working variables,
      see FIPS PUB 180-4 section 6.7, 6.4. */
@@ -619,10 +609,9 @@ Curl_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS],
  * @param length number of bytes in @a data
  * @return always CURLE_OK
  */
-static CURLcode
-Curl_sha512_256_update(void *context,
-                       const unsigned char *data,
-                       size_t length)
+static CURLcode Curl_sha512_256_update(void *context,
+                                       const unsigned char *data,
+                                       size_t length)
 {
   unsigned int bytes_have; /**< Number of bytes in the context buffer */
   struct Curl_sha512_256ctx *const ctx = (struct Curl_sha512_256ctx *)context;
@@ -631,7 +620,7 @@ Curl_sha512_256_update(void *context,
 
   DEBUGASSERT((data != NULL) || (length == 0));
 
-  if(0 == length)
+  if(length == 0)
     return CURLE_OK; /* Shortcut, do nothing */
 
   /* Note: (count & (CURL_SHA512_256_BLOCK_SIZE-1))
@@ -643,7 +632,7 @@ Curl_sha512_256_update(void *context,
   ctx->count_bits_hi += ctx->count >> 61;
   ctx->count &= CURL_UINT64_C(0x1FFFFFFFFFFFFFFF);
 
-  if(0 != bytes_have) {
+  if(bytes_have) {
     unsigned int bytes_left = CURL_SHA512_256_BLOCK_SIZE - bytes_have;
     if(length >= bytes_left) {
       /* Combine new data with data in the buffer and process the full
@@ -666,7 +655,7 @@ Curl_sha512_256_update(void *context,
     length -= CURL_SHA512_256_BLOCK_SIZE;
   }
 
-  if(0 != length) {
+  if(length) {
     /* Copy incomplete block of new data (if any)
        to the buffer. */
     memcpy(((unsigned char *) ctx_buf) + bytes_have, data, length);
@@ -676,7 +665,6 @@ Curl_sha512_256_update(void *context,
 }
 
 
-
 /**
  * Size of "length" insertion in bits.
  * See FIPS PUB 180-4 section 5.1.2.
@@ -696,9 +684,7 @@ Curl_sha512_256_update(void *context,
  #             bytes
  * @return always CURLE_OK
  */
-static CURLcode
-Curl_sha512_256_finish(unsigned char *digest,
-                       void *context)
+static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context)
 {
   struct Curl_sha512_256ctx *const ctx = (struct Curl_sha512_256ctx *)context;
   curl_uint64_t num_bits;   /**< Number of processed bits */
@@ -746,14 +732,14 @@ Curl_sha512_256_finish(unsigned char *digest,
      part of number of bits as big-endian values.
      See FIPS PUB 180-4 section 5.1.2. */
   /* Note: the target location is predefined and buffer is always aligned */
-  CURL_PUT_64BIT_BE(((unsigned char *) ctx_buf)  \
+  CURL_PUT_64BIT_BE(((unsigned char *) ctx_buf)       \
                       + CURL_SHA512_256_BLOCK_SIZE    \
                       - SHA512_256_SIZE_OF_LEN_ADD,   \
                       ctx->count_bits_hi);
-  CURL_PUT_64BIT_BE(((unsigned char *) ctx_buf)      \
-                      + CURL_SHA512_256_BLOCK_SIZE        \
-                      - SHA512_256_SIZE_OF_LEN_ADD        \
-                      + SHA512_256_BYTES_IN_WORD,         \
+  CURL_PUT_64BIT_BE(((unsigned char *) ctx_buf)       \
+                      + CURL_SHA512_256_BLOCK_SIZE    \
+                      - SHA512_256_SIZE_OF_LEN_ADD    \
+                      + SHA512_256_BYTES_IN_WORD,     \
                       num_bits);
   /* Process the full final block. */
   Curl_sha512_256_transform(ctx->H, ctx->buffer);
@@ -782,9 +768,8 @@ Curl_sha512_256_finish(unsigned char *digest,
  * @param input_size the size of the data pointed by @a input
  * @return always #CURLE_OK
  */
-CURLcode
-Curl_sha512_256it(unsigned char *output, const unsigned char *input,
-                  size_t input_size)
+CURLcode Curl_sha512_256it(unsigned char *output, const unsigned char *input,
+                           size_t input_size)
 {
   Curl_sha512_256_ctx ctx;
   CURLcode res;
@@ -796,7 +781,7 @@ Curl_sha512_256it(unsigned char *output, const unsigned char *input,
   res = Curl_sha512_256_update(&ctx, (const void *) input, input_size);
 
   if(res != CURLE_OK) {
-    (void) Curl_sha512_256_finish(output, &ctx);
+    (void)Curl_sha512_256_finish(output, &ctx);
     return res;
   }
 
@@ -804,22 +789,19 @@ Curl_sha512_256it(unsigned char *output, const unsigned char *input,
 }
 
 /* Wrapper function, takes 'unsigned int' as length type, returns void */
-static void
-Curl_sha512_256_update_i(void *context,
-                         const unsigned char *data,
-                         unsigned int length)
+static void Curl_sha512_256_update_i(void *context,
+                                     const unsigned char *data,
+                                     unsigned int length)
 {
   /* Hypothetically the function may fail, but assume it does not */
-  (void) Curl_sha512_256_update(context, data, length);
+  (void)Curl_sha512_256_update(context, data, length);
 }
 
 /* Wrapper function, returns void */
-static void
-Curl_sha512_256_finish_v(unsigned char *result,
-                         void *context)
+static void Curl_sha512_256_finish_v(unsigned char *result, void *context)
 {
   /* Hypothetically the function may fail, but assume it does not */
-  (void) Curl_sha512_256_finish(result, context);
+  (void)Curl_sha512_256_finish(result, context);
 }
 
 /* Wrapper function, takes 'unsigned int' as length type, returns void */

+ 6 - 2
Utilities/cmcurl/lib/curl_sspi.c

@@ -31,7 +31,6 @@
 #include "strdup.h"
 #include "curlx/multibyte.h"
 #include "system_win32.h"
-#include "curlx/version_win32.h"
 #include "curlx/warnless.h"
 
 /* The last #include files should be: */
@@ -178,7 +177,12 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
   curlx_unicodefree(passwd.tchar_ptr);
 
   /* Setup the identity's flags */
-  identity->Flags = SECFLAG_WINNT_AUTH_IDENTITY;
+  identity->Flags = (unsigned long)
+#ifdef UNICODE
+    SEC_WINNT_AUTH_IDENTITY_UNICODE;
+#else
+    SEC_WINNT_AUTH_IDENTITY_ANSI;
+#endif
 
   return CURLE_OK;
 }

+ 11 - 27
Utilities/cmcurl/lib/curl_sspi.h

@@ -65,6 +65,7 @@ extern PSecurityFunctionTable Curl_pSecFn;
 #define SP_NAME_NEGOTIATE           "Negotiate"
 #define SP_NAME_KERBEROS            "Kerberos"
 
+/* Offered by mingw-w64 v9+. MS SDK 7.0A+. */
 #ifndef ISC_REQ_USE_HTTP_STYLE
 #define ISC_REQ_USE_HTTP_STYLE                0x01000000
 #endif
@@ -288,14 +289,17 @@ extern PSecurityFunctionTable Curl_pSecFn;
 #define SEC_E_KDC_CERT_REVOKED                ((HRESULT)0x8009035BL)
 #endif
 #endif /* __MINGW32CE__ */
+/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */
 #ifndef SEC_E_INVALID_PARAMETER
-# define SEC_E_INVALID_PARAMETER              ((HRESULT)0x8009035DL)
+#define SEC_E_INVALID_PARAMETER               ((HRESULT)0x8009035DL)
 #endif
+/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */
 #ifndef SEC_E_DELEGATION_POLICY
-# define SEC_E_DELEGATION_POLICY              ((HRESULT)0x8009035EL)
+#define SEC_E_DELEGATION_POLICY               ((HRESULT)0x8009035EL)
 #endif
+/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */
 #ifndef SEC_E_POLICY_NLTM_ONLY
-# define SEC_E_POLICY_NLTM_ONLY               ((HRESULT)0x8009035FL)
+#define SEC_E_POLICY_NLTM_ONLY                ((HRESULT)0x8009035FL)
 #endif
 
 #ifdef __MINGW32CE__
@@ -324,37 +328,17 @@ extern PSecurityFunctionTable Curl_pSecFn;
 #define SEC_I_NO_LSA_CONTEXT                  ((HRESULT)0x00090323L)
 #endif
 #endif /* __MINGW32CE__ */
+
+/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */
 #ifndef SEC_I_SIGNATURE_NEEDED
 #define SEC_I_SIGNATURE_NEEDED                ((HRESULT)0x0009035CL)
 #endif
 
-#ifndef CRYPT_E_REVOKED
-#define CRYPT_E_REVOKED                       ((HRESULT)0x80092010L)
-#endif
-
-#ifndef CRYPT_E_NO_REVOCATION_DLL
-#define CRYPT_E_NO_REVOCATION_DLL             ((HRESULT)0x80092011L)
-#endif
-
-#ifndef CRYPT_E_NO_REVOCATION_CHECK
-#define CRYPT_E_NO_REVOCATION_CHECK           ((HRESULT)0x80092012L)
-#endif
-
-#ifndef CRYPT_E_REVOCATION_OFFLINE
-#define CRYPT_E_REVOCATION_OFFLINE            ((HRESULT)0x80092013L)
-#endif
-
+#ifdef __MINGW32CE__
 #ifndef CRYPT_E_NOT_IN_REVOCATION_DATABASE
 #define CRYPT_E_NOT_IN_REVOCATION_DATABASE    ((HRESULT)0x80092014L)
 #endif
-
-#ifdef UNICODE
-#  define SECFLAG_WINNT_AUTH_IDENTITY \
-     (unsigned long)SEC_WINNT_AUTH_IDENTITY_UNICODE
-#else
-#  define SECFLAG_WINNT_AUTH_IDENTITY \
-     (unsigned long)SEC_WINNT_AUTH_IDENTITY_ANSI
-#endif
+#endif /* __MINGW32CE__ */
 
 /*
  * Definitions required from ntsecapi.h are directly provided below this point

+ 44 - 13
Utilities/cmcurl/lib/curl_threads.c

@@ -26,7 +26,7 @@
 
 #include <curl/curl.h>
 
-#if defined(USE_THREADS_POSIX)
+#ifdef USE_THREADS_POSIX
 #  ifdef HAVE_PTHREAD_H
 #    include <pthread.h>
 #  endif
@@ -36,10 +36,10 @@
 
 #include "curl_threads.h"
 #include "curl_memory.h"
-/* The last #include file should be: */
+/* The last #include FILE should be: */
 #include "memdebug.h"
 
-#if defined(USE_THREADS_POSIX)
+#ifdef USE_THREADS_POSIX
 
 struct Curl_actual_call {
   unsigned int (*func)(void *);
@@ -59,7 +59,8 @@ static void *curl_thread_create_thunk(void *arg)
   return 0;
 }
 
-curl_thread_t Curl_thread_create(unsigned int (*func) (void *), void *arg)
+curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T
+                                 (CURL_STDCALL *func) (void *), void *arg)
 {
   curl_thread_t t = malloc(sizeof(pthread_t));
   struct Curl_actual_call *ac = malloc(sizeof(struct Curl_actual_call));
@@ -99,16 +100,38 @@ int Curl_thread_join(curl_thread_t *hnd)
   return ret;
 }
 
-#elif defined(USE_THREADS_WIN32)
+/* 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__ */
 
-curl_thread_t Curl_thread_create(
-#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
-                                 DWORD
+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
-                                 unsigned int
+    return 1; /* not supported */
 #endif
-                                 (CURL_STDCALL *func) (void *),
-                                 void *arg)
+  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;
@@ -147,8 +170,7 @@ void Curl_thread_destroy(curl_thread_t *hnd)
 
 int Curl_thread_join(curl_thread_t *hnd)
 {
-#if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \
-    (_WIN32_WINNT < _WIN32_WINNT_VISTA)
+#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < _WIN32_WINNT_VISTA)
   int ret = (WaitForSingleObject(*hnd, INFINITE) == WAIT_OBJECT_0);
 #else
   int ret = (WaitForSingleObjectEx(*hnd, INFINITE, FALSE) == WAIT_OBJECT_0);
@@ -156,7 +178,16 @@ 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_* */

+ 27 - 10
Utilities/cmcurl/lib/curl_threads.h

@@ -25,7 +25,7 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
-#if defined(USE_THREADS_POSIX)
+#ifdef USE_THREADS_POSIX
 #  define CURL_STDCALL
 #  define curl_mutex_t           pthread_mutex_t
 #  define curl_thread_t          pthread_t *
@@ -39,8 +39,7 @@
 #  define curl_mutex_t           CRITICAL_SECTION
 #  define curl_thread_t          HANDLE
 #  define curl_thread_t_null     (HANDLE)0
-#  if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \
-      (_WIN32_WINNT < _WIN32_WINNT_VISTA)
+#  if !defined(_WIN32_WINNT) || (_WIN32_WINNT < _WIN32_WINNT_VISTA)
 #    define Curl_mutex_init(m)   InitializeCriticalSection(m)
 #  else
 #    define Curl_mutex_init(m)   InitializeCriticalSectionEx(m, 0, 1)
@@ -48,23 +47,41 @@
 #  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(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
-
-curl_thread_t Curl_thread_create(
 #if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
-                                 DWORD
+#define CURL_THREAD_RETURN_T DWORD
 #else
-                                 unsigned int
+#define CURL_THREAD_RETURN_T unsigned int
 #endif
-                                 (CURL_STDCALL *func) (void *),
-                                 void *arg);
+
+#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
+
+curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T
+                                 (CURL_STDCALL *func) (void *), void *arg);
 
 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 */

+ 54 - 12
Utilities/cmcurl/lib/curl_trc.c

@@ -41,6 +41,7 @@
 #include "cf-h2-proxy.h"
 #include "cf-haproxy.h"
 #include "cf-https-connect.h"
+#include "cf-ip-happy.h"
 #include "socks.h"
 #include "curlx/strparse.h"
 #include "vtls/vtls.h"
@@ -161,10 +162,12 @@ void Curl_debug(struct Curl_easy *data, curl_infotype type,
       case CURLINFO_TEXT:
       case CURLINFO_HEADER_OUT:
       case CURLINFO_HEADER_IN:
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
         if(CURL_TRC_IDS(data)) {
           len = trc_print_ids(data, buf, TRC_LINE_MAX);
           fwrite(buf, len, 1, data->set.err);
         }
+#endif
         fwrite(s_infotype[type], 2, 1, data->set.err);
         fwrite(ptr, size, 1, data->set.err);
         break;
@@ -199,8 +202,7 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
   }
 }
 
-#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
-
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
 
 static void trc_infof(struct Curl_easy *data,
                       struct curl_trc_feat *feat,
@@ -271,6 +273,45 @@ struct curl_trc_feat Curl_trc_feat_dns = {
   CURL_LOG_LVL_NONE,
 };
 
+static const char * const Curl_trc_timer_names[]={
+  "100_TIMEOUT",
+  "ASYNC_NAME",
+  "CONNECTTIMEOUT",
+  "DNS_PER_NAME",
+  "DNS_PER_NAME2",
+  "HAPPY_EYEBALLS_DNS",
+  "HAPPY_EYEBALLS",
+  "MULTI_PENDING",
+  "SPEEDCHECK",
+  "TIMEOUT",
+  "TOOFAST",
+  "QUIC",
+  "FTP_ACCEPT",
+  "ALPN_EYEBALLS",
+  "SHUTDOWN",
+};
+
+const char *Curl_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)
+{
+  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));
+    }
+  }
+}
 
 static const char * const Curl_trc_mstate_names[]={
   "INIT",
@@ -461,9 +502,9 @@ static struct trc_cft_def trc_cfts[] = {
   { &Curl_cft_udp,            TRC_CT_NETWORK },
   { &Curl_cft_unix,           TRC_CT_NETWORK },
   { &Curl_cft_tcp_accept,     TRC_CT_NETWORK },
-  { &Curl_cft_happy_eyeballs, TRC_CT_NETWORK },
+  { &Curl_cft_ip_happy,       TRC_CT_NETWORK },
   { &Curl_cft_setup,          TRC_CT_PROTOCOL },
-#ifdef USE_NGHTTP2
+#if !defined(CURL_DISABLE_HTTP) && defined(USE_NGHTTP2)
   { &Curl_cft_nghttp2,        TRC_CT_PROTOCOL },
 #endif
 #ifdef USE_SSL
@@ -472,8 +513,8 @@ static struct trc_cft_def trc_cfts[] = {
   { &Curl_cft_ssl_proxy,      TRC_CT_PROXY },
 #endif
 #endif
-#if !defined(CURL_DISABLE_PROXY)
-#if !defined(CURL_DISABLE_HTTP)
+#ifndef CURL_DISABLE_PROXY
+#ifndef CURL_DISABLE_HTTP
   { &Curl_cft_h1_proxy,       TRC_CT_PROXY },
 #ifdef USE_NGHTTP2
   { &Curl_cft_h2_proxy,       TRC_CT_PROXY },
@@ -483,10 +524,10 @@ static struct trc_cft_def trc_cfts[] = {
   { &Curl_cft_haproxy,        TRC_CT_PROXY },
   { &Curl_cft_socks_proxy,    TRC_CT_PROXY },
 #endif /* !CURL_DISABLE_PROXY */
-#ifdef USE_HTTP3
+#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3)
   { &Curl_cft_http3,          TRC_CT_PROTOCOL },
 #endif
-#if !defined(CURL_DISABLE_HTTP)
+#ifndef CURL_DISABLE_HTTP
   { &Curl_cft_http_connect,   TRC_CT_PROTOCOL },
 #endif
 };
@@ -581,7 +622,7 @@ CURLcode Curl_trc_init(void)
 #endif
 }
 
-#else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */
+#else /* CURL_DISABLE_VERBOSE_STRINGS */
 
 CURLcode Curl_trc_init(void)
 {
@@ -633,17 +674,18 @@ void Curl_trc_smtp(struct Curl_easy *data, const char *fmt, ...)
   (void)data; (void)fmt;
 }
 #endif
-#if !defined(CURL_DISABLE_WEBSOCKETS) || !defined(CURL_DISABLE_HTTP)
+#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
 void Curl_trc_ws(struct Curl_easy *data, const char *fmt, ...)
 {
   (void)data; (void)fmt;
 }
 #endif
-
+#ifdef USE_SSL
 void Curl_trc_ssls(struct Curl_easy *data, const char *fmt, ...)
 {
   (void)data;
   (void)fmt;
 }
+#endif
 
-#endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */
+#endif /* !CURL_DISABLE_VERBOSE_STRINGS */

+ 15 - 3
Utilities/cmcurl/lib/curl_trc.h

@@ -85,6 +85,9 @@ void Curl_trc_cf_infof(struct Curl_easy *data, const struct Curl_cfilter *cf,
 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_write(struct Curl_easy *data,
                     const char *fmt, ...) CURL_PRINTF(2, 3);
 void Curl_trc_read(struct Curl_easy *data,
@@ -113,12 +116,15 @@ void Curl_trc_ws(struct Curl_easy *data,
                  const char *fmt, ...) CURL_PRINTF(2, 3);
 #endif
 
+#define CURL_TRC_M_is_verbose(data) \
+  Curl_trc_ft_is_verbose(data, &Curl_trc_feat_multi)
+
 #if defined(CURL_HAVE_C99) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
 #define infof(data, ...) \
   do { if(Curl_trc_is_verbose(data)) \
          Curl_infof(data, __VA_ARGS__); } while(0)
 #define CURL_TRC_M(data, ...) \
-  do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_multi)) \
+  do { if(CURL_TRC_M_is_verbose(data)) \
          Curl_trc_multi(data, __VA_ARGS__); } while(0)
 #define CURL_TRC_CF(data, cf, ...) \
   do { if(Curl_trc_cf_is_verbose(cf, data)) \
@@ -202,15 +208,21 @@ 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)
 
-#else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */
+#else /* CURL_DISABLE_VERBOSE_STRINGS */
 /* All informational messages are not compiled in for size savings */
 
 #define Curl_trc_is_verbose(d)        (FALSE)
 #define Curl_trc_cf_is_verbose(x,y)   (FALSE)
 #define Curl_trc_ft_is_verbose(x,y)   (FALSE)
 #define CURL_MSTATE_NAME(x)           ((void)(x), "-")
+#define CURL_TIMER_NAME(x)            ((void)(x), "-")
+#define CURL_TRC_M_TIMEOUTS(x)        Curl_nop_stmt
 
-#endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */
+#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
 
 #endif /* HEADER_CURL_TRC_H */

+ 0 - 9
Utilities/cmcurl/lib/curlx/base64.c

@@ -26,13 +26,6 @@
 
 #include "../curl_setup.h"
 
-#if !defined(CURL_DISABLE_HTTP_AUTH) || defined(USE_SSH) || \
-  !defined(CURL_DISABLE_LDAP) || \
-  !defined(CURL_DISABLE_SMTP) || \
-  !defined(CURL_DISABLE_POP3) || \
-  !defined(CURL_DISABLE_IMAP) || \
-  !defined(CURL_DISABLE_DIGEST_AUTH) || \
-  !defined(CURL_DISABLE_DOH) || defined(USE_SSL) || !defined(BUILDING_LIBCURL)
 #include <curl/curl.h>
 #include "warnless.h"
 #include "base64.h"
@@ -281,5 +274,3 @@ CURLcode curlx_base64url_encode(const char *inputbuff, size_t insize,
 {
   return base64_encode(base64url, 0, inputbuff, insize, outptr, outlen);
 }
-
-#endif /* no users so disabled */

+ 7 - 6
Utilities/cmcurl/lib/curlx/dynbuf.c

@@ -71,14 +71,14 @@ void curlx_dyn_free(struct dynbuf *s)
 static CURLcode dyn_nappend(struct dynbuf *s,
                             const unsigned char *mem, size_t len)
 {
-  size_t indx = s->leng;
+  size_t idx = s->leng;
   size_t a = s->allc;
-  size_t fit = len + indx + 1; /* new string + old string + zero byte */
+  size_t fit = len + idx + 1; /* new string + old string + zero byte */
 
   /* try to detect if there is rubbish in the struct */
   DEBUGASSERT(s->init == DYNINIT);
   DEBUGASSERT(s->toobig);
-  DEBUGASSERT(indx < s->toobig);
+  DEBUGASSERT(idx < s->toobig);
   DEBUGASSERT(!s->leng || s->bufr);
   DEBUGASSERT(a <= s->toobig);
   DEBUGASSERT(!len || mem);
@@ -88,7 +88,7 @@ static CURLcode dyn_nappend(struct dynbuf *s,
     return CURLE_TOO_LARGE;
   }
   else if(!a) {
-    DEBUGASSERT(!indx);
+    DEBUGASSERT(!idx);
     /* first invoke */
     if(MIN_FIRST_ALLOC > s->toobig)
       a = s->toobig;
@@ -118,8 +118,8 @@ static CURLcode dyn_nappend(struct dynbuf *s,
   }
 
   if(len)
-    memcpy(&s->bufr[indx], mem, len);
-  s->leng = indx + len;
+    memcpy(&s->bufr[idx], mem, len);
+  s->leng = idx + len;
   s->bufr[s->leng] = 0;
   return CURLE_OK;
 }
@@ -231,6 +231,7 @@ CURLcode curlx_dyn_addf(struct dynbuf *s, const char *fmt, ...)
   DEBUGASSERT(s);
   DEBUGASSERT(s->init == DYNINIT);
   DEBUGASSERT(!s->leng || s->bufr);
+  DEBUGASSERT(strcmp(fmt, "%s")); /* use curlx_dyn_add instead */
   va_start(ap, fmt);
   result = curlx_dyn_vaddf(s, fmt, ap);
   va_end(ap);

+ 1 - 1
Utilities/cmcurl/lib/curlx/dynbuf.h

@@ -62,7 +62,7 @@ int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save);
 char *curlx_dyn_take(struct dynbuf *s, size_t *plen);
 
 /* Dynamic buffer max sizes */
-#define MAX_DYNBUF_SIZE (SIZE_T_MAX/2)
+#define MAX_DYNBUF_SIZE (SIZE_MAX/2)
 
 #define DYN_DOH_RESPONSE    3000
 #define DYN_DOH_CNAME       256

+ 1 - 1
Utilities/cmcurl/lib/curlx/inet_ntop.c

@@ -54,7 +54,7 @@
  *
  * Returns `dst' (as a const)
  * Note:
- *  - uses no statics
+ *  - uses no static variables
  *  - takes an unsigned char* not an in_addr as input
  */
 static char *inet_ntop4(const unsigned char *src, char *dst, size_t size)

+ 3 - 3
Utilities/cmcurl/lib/curlx/inet_ntop.h

@@ -26,8 +26,6 @@
 
 #include "../curl_setup.h"
 
-char *curlx_inet_ntop(int af, const void *addr, char *buf, size_t size);
-
 #ifdef HAVE_INET_NTOP
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
@@ -46,6 +44,8 @@ char *curlx_inet_ntop(int af, const void *addr, char *buf, size_t size);
 #define curlx_inet_ntop(af,addr,buf,size)                \
   inet_ntop(af, addr, buf, (curl_socklen_t)(size))
 #endif
-#endif
+#else
+char *curlx_inet_ntop(int af, const void *addr, char *buf, size_t size);
+#endif /* HAVE_INET_NTOP */
 
 #endif /* HEADER_CURL_INET_NTOP_H */

+ 3 - 3
Utilities/cmcurl/lib/curlx/inet_pton.h

@@ -26,8 +26,6 @@
 
 #include "../curl_setup.h"
 
-int curlx_inet_pton(int, const char *, void *);
-
 #ifdef HAVE_INET_PTON
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
@@ -43,6 +41,8 @@ int curlx_inet_pton(int, const char *, void *);
 #else
 #define curlx_inet_pton(x,y,z) inet_pton(x,y,z)
 #endif
-#endif
+#else
+int curlx_inet_pton(int, const char *, void *);
+#endif /* HAVE_INET_PTON */
 
 #endif /* HEADER_CURL_INET_PTON_H */

+ 2 - 6
Utilities/cmcurl/lib/curlx/multibyte.h

@@ -26,14 +26,10 @@
 #include "../curl_setup.h"
 
 #ifdef _WIN32
-
- /*
-  * MultiByte conversions using Windows kernel32 library.
-  */
-
+/* MultiByte conversions using Windows kernel32 library. */
 wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8);
 char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w);
-#endif /* _WIN32 */
+#endif
 
 /*
  * Macros curlx_convert_UTF8_to_tchar(), curlx_convert_tchar_to_UTF8()

+ 1 - 1
Utilities/cmcurl/lib/curlx/nonblock.c

@@ -46,7 +46,7 @@
 int curlx_nonblock(curl_socket_t sockfd,    /* operate on this */
                    int nonblock   /* TRUE or FALSE */)
 {
-#if defined(HAVE_FCNTL_O_NONBLOCK)
+#ifdef HAVE_FCNTL_O_NONBLOCK
   /* most recent Unix versions */
   int flags;
   flags = sfcntl(sockfd, F_GETFL, 0);

+ 1 - 1
Utilities/cmcurl/lib/curlx/strparse.c

@@ -165,7 +165,7 @@ static int str_num_base(const char **linep, curl_off_t *nump, curl_off_t max,
     (base == 16) ? 'f' : '7';
   DEBUGASSERT(linep && *linep && nump);
   DEBUGASSERT((base == 8) || (base == 10) || (base == 16));
-  DEBUGASSERT(max >= 0); /* mostly to catch SIZE_T_MAX, which is too large */
+  DEBUGASSERT(max >= 0); /* mostly to catch SIZE_MAX, which is too large */
   *nump = 0;
   p = *linep;
   if(!valid_digit(*p, m))

+ 5 - 5
Utilities/cmcurl/lib/curlx/timeval.c

@@ -115,7 +115,7 @@ struct curltime curlx_now(void)
         (HAVE_BUILTIN_AVAILABLE == 1)
     have_clock_gettime &&
 #endif
-    (0 == clock_gettime(CLOCK_MONOTONIC_RAW, &tsnow))) {
+    (clock_gettime(CLOCK_MONOTONIC_RAW, &tsnow) == 0)) {
     cnow.tv_sec = tsnow.tv_sec;
     cnow.tv_usec = (int)(tsnow.tv_nsec / 1000);
   }
@@ -127,7 +127,7 @@ struct curltime curlx_now(void)
         (HAVE_BUILTIN_AVAILABLE == 1)
     have_clock_gettime &&
 #endif
-    (0 == clock_gettime(CLOCK_MONOTONIC, &tsnow))) {
+    (clock_gettime(CLOCK_MONOTONIC, &tsnow) == 0)) {
     cnow.tv_sec = tsnow.tv_sec;
     cnow.tv_usec = (int)(tsnow.tv_nsec / 1000);
   }
@@ -168,11 +168,11 @@ struct curltime curlx_now(void)
   struct curltime cnow;
   uint64_t usecs;
 
-  if(0 == timebase.denom)
-    (void) mach_timebase_info(&timebase);
+  if(timebase.denom == 0)
+    (void)mach_timebase_info(&timebase);
 
   usecs = mach_absolute_time();
-  usecs *= timebase.numer;
+  usecs *= timebase.numer; /* spellchecker:disable-line */
   usecs /= timebase.denom;
   usecs /= 1000;
 

+ 1 - 1
Utilities/cmcurl/lib/curlx/wait.c

@@ -68,7 +68,7 @@ int curlx_wait_ms(timediff_t timeout_ms)
     SET_SOCKERRNO(SOCKEINVAL);
     return -1;
   }
-#if defined(MSDOS)
+#ifdef MSDOS
   delay((unsigned int)timeout_ms);
 #elif defined(_WIN32)
   /* prevent overflow, timeout_ms is typecast to ULONG/DWORD. */

+ 3 - 3
Utilities/cmcurl/lib/curlx/warnless.c

@@ -98,7 +98,7 @@ unsigned long curlx_uztoul(size_t uznum)
 #  pragma warning(disable:810) /* conversion may lose significant bits */
 #endif
 
-#if ULONG_MAX < SIZE_T_MAX
+#if ULONG_MAX < SIZE_MAX
   DEBUGASSERT(uznum <= (size_t) CURL_MASK_ULONG);
 #endif
   return (unsigned long)(uznum & (size_t) CURL_MASK_ULONG);
@@ -119,7 +119,7 @@ unsigned int curlx_uztoui(size_t uznum)
 #  pragma warning(disable:810) /* conversion may lose significant bits */
 #endif
 
-#if UINT_MAX < SIZE_T_MAX
+#if UINT_MAX < SIZE_MAX
   DEBUGASSERT(uznum <= (size_t) CURL_MASK_UINT);
 #endif
   return (unsigned int)(uznum & (size_t) CURL_MASK_UINT);
@@ -243,7 +243,7 @@ int curlx_sztosi(ssize_t sznum)
 #endif
 
   DEBUGASSERT(sznum >= 0);
-#if INT_MAX < SSIZE_T_MAX
+#if INT_MAX < SSIZE_MAX
   DEBUGASSERT((size_t) sznum <= (size_t) CURL_MASK_SINT);
 #endif
   return (int)(sznum & (ssize_t) CURL_MASK_SINT);

+ 72 - 37
Utilities/cmcurl/lib/cw-out.c

@@ -74,6 +74,7 @@
 typedef enum {
   CW_OUT_NONE,
   CW_OUT_BODY,
+  CW_OUT_BODY_0LEN,
   CW_OUT_HDS
 } cw_out_type;
 
@@ -170,6 +171,7 @@ static void cw_get_writefunc(struct Curl_easy *data, cw_out_type otype,
 {
   switch(otype) {
   case CW_OUT_BODY:
+  case CW_OUT_BODY_0LEN:
     *pwcb = data->set.fwrite_func;
     *pwcb_data = data->set.out;
     *pmax_write = CURL_MAX_WRITE_SIZE;
@@ -193,6 +195,51 @@ static void cw_get_writefunc(struct Curl_easy *data, cw_out_type otype,
   }
 }
 
+static CURLcode cw_out_cb_write(struct cw_out_ctx *ctx,
+                                struct Curl_easy *data,
+                                curl_write_callback wcb,
+                                void *wcb_data,
+                                cw_out_type otype,
+                                const char *buf, size_t blen,
+                                size_t *pnwritten)
+{
+  size_t nwritten;
+  CURLcode result;
+
+  DEBUGASSERT(data->conn);
+  *pnwritten = 0;
+  Curl_set_in_callback(data, TRUE);
+  nwritten = wcb((char *)CURL_UNCONST(buf), 1, blen, wcb_data);
+  Curl_set_in_callback(data, FALSE);
+  CURL_TRC_WRITE(data, "[OUT] wrote %zu %s bytes -> %zu",
+                 blen, (otype == CW_OUT_HDS) ? "header" : "body",
+                 nwritten);
+  if(CURL_WRITEFUNC_PAUSE == nwritten) {
+    if(data->conn->handler->flags & PROTOPT_NONETWORK) {
+      /* Protocols that work without network cannot be paused. This is
+         actually only FILE:// just now, and it cannot pause since the
+         transfer is not done using the "normal" procedure. */
+      failf(data, "Write callback asked for PAUSE when not supported");
+      return CURLE_WRITE_ERROR;
+    }
+    ctx->paused = TRUE;
+    CURL_TRC_WRITE(data, "[OUT] PAUSE requested by client");
+    result = Curl_xfer_pause_recv(data, TRUE);
+    return result ? result : CURLE_AGAIN;
+  }
+  else if(CURL_WRITEFUNC_ERROR == nwritten) {
+    failf(data, "client returned ERROR on write of %zu bytes", blen);
+    return CURLE_WRITE_ERROR;
+  }
+  else if(nwritten != blen) {
+    failf(data, "Failure writing output to destination, "
+          "passed %zu returned %zd", blen, nwritten);
+    return CURLE_WRITE_ERROR;
+  }
+  *pnwritten = nwritten;
+  return CURLE_OK;
+}
+
 static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
                                  struct Curl_easy *data,
                                  cw_out_type otype,
@@ -204,6 +251,7 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
   void *wcb_data;
   size_t max_write, min_write;
   size_t wlen, nwritten;
+  CURLcode result;
 
   /* If we errored once, we do not invoke the client callback  again */
   if(ctx->errored)
@@ -217,40 +265,24 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
   }
 
   *pconsumed = 0;
-  while(blen && !ctx->paused) {
-    if(!flush_all && blen < min_write)
-      break;
-    wlen = max_write ? CURLMIN(blen, max_write) : blen;
-    Curl_set_in_callback(data, TRUE);
-    nwritten = wcb((char *)CURL_UNCONST(buf), 1, wlen, wcb_data);
-    Curl_set_in_callback(data, FALSE);
-    CURL_TRC_WRITE(data, "[OUT] wrote %zu %s bytes -> %zu",
-                   wlen, (otype == CW_OUT_BODY) ? "body" : "header",
-                   nwritten);
-    if(CURL_WRITEFUNC_PAUSE == nwritten) {
-      if(data->conn && data->conn->handler->flags & PROTOPT_NONETWORK) {
-        /* Protocols that work without network cannot be paused. This is
-           actually only FILE:// just now, and it cannot pause since the
-           transfer is not done using the "normal" procedure. */
-        failf(data, "Write callback asked for PAUSE when not supported");
-        return CURLE_WRITE_ERROR;
-      }
-      ctx->paused = TRUE;
-      CURL_TRC_WRITE(data, "[OUT] PAUSE requested by client");
-      return Curl_xfer_pause_recv(data, TRUE);
-    }
-    else if(CURL_WRITEFUNC_ERROR == nwritten) {
-      failf(data, "client returned ERROR on write of %zu bytes", wlen);
-      return CURLE_WRITE_ERROR;
-    }
-    else if(nwritten != wlen) {
-      failf(data, "Failure writing output to destination, "
-            "passed %zu returned %zd", wlen, nwritten);
-      return CURLE_WRITE_ERROR;
+  if(otype == CW_OUT_BODY_0LEN) {
+    DEBUGASSERT(!blen);
+    return cw_out_cb_write(ctx, data, wcb, wcb_data, otype,
+                           buf, blen, &nwritten);
+  }
+  else {
+    while(blen && !ctx->paused) {
+      if(!flush_all && blen < min_write)
+        break;
+      wlen = max_write ? CURLMIN(blen, max_write) : blen;
+      result = cw_out_cb_write(ctx, data, wcb, wcb_data, otype,
+                               buf, wlen, &nwritten);
+      if(result)
+        return result;
+      *pconsumed += nwritten;
+      blen -= nwritten;
+      buf += nwritten;
     }
-    *pconsumed += nwritten;
-    blen -= nwritten;
-    buf += nwritten;
   }
   return CURLE_OK;
 }
@@ -262,14 +294,14 @@ static CURLcode cw_out_buf_flush(struct cw_out_ctx *ctx,
 {
   CURLcode result = CURLE_OK;
 
-  if(curlx_dyn_len(&cwbuf->b)) {
+  if(curlx_dyn_len(&cwbuf->b) || (cwbuf->type == CW_OUT_BODY_0LEN)) {
     size_t consumed;
 
     result = cw_out_ptr_flush(ctx, data, cwbuf->type, flush_all,
                               curlx_dyn_ptr(&cwbuf->b),
                               curlx_dyn_len(&cwbuf->b),
                               &consumed);
-    if(result)
+    if(result && (result != CURLE_AGAIN))
       return result;
 
     if(consumed) {
@@ -382,8 +414,9 @@ static CURLcode cw_out_do_write(struct cw_out_ctx *ctx,
     size_t consumed;
     result = cw_out_ptr_flush(ctx, data, otype, flush_all,
                               buf, blen, &consumed);
-    if(result)
+    if(result && (result != CURLE_AGAIN))
       return result;
+    result = CURLE_OK;
     if(consumed < blen) {
       /* did not write all, append the rest */
       result = cw_out_append(ctx, data, otype,
@@ -413,7 +446,9 @@ static CURLcode cw_out_write(struct Curl_easy *data,
 
   if((type & CLIENTWRITE_BODY) ||
      ((type & CLIENTWRITE_HEADER) && data->set.include_header)) {
-    result = cw_out_do_write(ctx, data, CW_OUT_BODY, flush_all, buf, blen);
+    cw_out_type otype = (!blen && (type & CLIENTWRITE_0LEN)) ?
+                        CW_OUT_BODY_0LEN : CW_OUT_BODY;
+    result = cw_out_do_write(ctx, data, otype, flush_all, buf, blen);
     if(result)
       return result;
   }

+ 7 - 7
Utilities/cmcurl/lib/dict.c

@@ -92,10 +92,10 @@ const struct Curl_handler Curl_handler_dict = {
   ZERO_NULL,                            /* connect_it */
   ZERO_NULL,                            /* connecting */
   ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* proto_pollset */
+  ZERO_NULL,                            /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  ZERO_NULL,                            /* perform_pollset */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
@@ -242,7 +242,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
       failf(data, "Failed sending DICT request");
       goto error;
     }
-    Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); /* no upload */
+    Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
   }
   else if(curl_strnequal(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) ||
           curl_strnequal(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) ||
@@ -283,7 +283,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
       failf(data, "Failed sending DICT request");
       goto error;
     }
-    Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE);
+    Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
   }
   else {
 
@@ -305,7 +305,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
         goto error;
       }
 
-      Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE);
+      Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
     }
   }
 

+ 2 - 4
Utilities/cmcurl/lib/dllmain.c

@@ -37,10 +37,8 @@
 #if defined(_WIN32) && !defined(CURL_STATICLIB)
 
 #if defined(USE_OPENSSL) && \
-    !defined(OPENSSL_IS_AWSLC) && \
-    !defined(OPENSSL_IS_BORINGSSL) && \
-    !defined(LIBRESSL_VERSION_NUMBER) && \
-    (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+  !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) && \
+  !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L
 #define PREVENT_OPENSSL_MEMLEAK
 #endif
 

+ 25 - 24
Utilities/cmcurl/lib/doh.c

@@ -348,10 +348,10 @@ static CURLcode doh_probe_run(struct Curl_easy *data,
 #endif
 #ifndef DEBUGBUILD
   /* enforce HTTPS if not debug */
-  ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, (long)CURLPROTO_HTTPS);
+  ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
 #else
   /* in debug mode, also allow http */
-  ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, (long)CURLPROTO_HTTP|CURLPROTO_HTTPS);
+  ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
 #endif
   ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
   ERROR_CHECK_SETOPT(CURLOPT_SHARE, (CURLSH *)data->share);
@@ -484,7 +484,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
   data->sub_xfer_done = doh_probe_done;
 
   /* create IPv4 DoH request */
-  result = doh_probe_run(data, DNS_TYPE_A,
+  result = doh_probe_run(data, CURL_DNS_TYPE_A,
                          hostname, data->set.str[STRING_DOH],
                          data->multi,
                          &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid);
@@ -495,7 +495,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
 #ifdef USE_IPV6
   if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
     /* create IPv6 DoH request */
-    result = doh_probe_run(data, DNS_TYPE_AAAA,
+    result = doh_probe_run(data, CURL_DNS_TYPE_AAAA,
                            hostname, data->set.str[STRING_DOH],
                            data->multi,
                            &dohp->probe_resp[DOH_SLOT_IPV6].probe_mid);
@@ -514,7 +514,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
       if(!qname)
         goto error;
     }
-    result = doh_probe_run(data, DNS_TYPE_HTTPS,
+    result = doh_probe_run(data, CURL_DNS_TYPE_HTTPS,
                            qname ? qname : hostname, data->set.str[STRING_DOH],
                            data->multi,
                            &dohp->probe_resp[DOH_SLOT_HTTPS_RR].probe_mid);
@@ -581,7 +581,7 @@ static void doh_store_a(const unsigned char *doh, int index,
   /* silently ignore addresses over the limit */
   if(d->numaddr < DOH_MAX_ADDR) {
     struct dohaddr *a = &d->addr[d->numaddr];
-    a->type = DNS_TYPE_A;
+    a->type = CURL_DNS_TYPE_A;
     memcpy(&a->ip.v4, &doh[index], 4);
     d->numaddr++;
   }
@@ -593,7 +593,7 @@ static void doh_store_aaaa(const unsigned char *doh, int index,
   /* silently ignore addresses over the limit */
   if(d->numaddr < DOH_MAX_ADDR) {
     struct dohaddr *a = &d->addr[d->numaddr];
-    a->type = DNS_TYPE_AAAA;
+    a->type = CURL_DNS_TYPE_AAAA;
     memcpy(&a->ip.v6, &doh[index], 16);
     d->numaddr++;
   }
@@ -681,29 +681,29 @@ static DOHcode doh_rdata(const unsigned char *doh,
   DOHcode rc;
 
   switch(type) {
-  case DNS_TYPE_A:
+  case CURL_DNS_TYPE_A:
     if(rdlength != 4)
       return DOH_DNS_RDATA_LEN;
     doh_store_a(doh, index, d);
     break;
-  case DNS_TYPE_AAAA:
+  case CURL_DNS_TYPE_AAAA:
     if(rdlength != 16)
       return DOH_DNS_RDATA_LEN;
     doh_store_aaaa(doh, index, d);
     break;
 #ifdef USE_HTTPSRR
-  case DNS_TYPE_HTTPS:
+  case CURL_DNS_TYPE_HTTPS:
     rc = doh_store_https(doh, index, d, rdlength);
     if(rc)
       return rc;
     break;
 #endif
-  case DNS_TYPE_CNAME:
+  case CURL_DNS_TYPE_CNAME:
     rc = doh_store_cname(doh, dohlen, (unsigned int)index, d);
     if(rc)
       return rc;
     break;
-  case DNS_TYPE_DNAME:
+  case CURL_DNS_TYPE_DNAME:
     /* explicit for clarity; just skip; rely on synthesized CNAME  */
     break;
   default:
@@ -770,8 +770,8 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh,
       return DOH_DNS_OUT_OF_RANGE;
 
     type = doh_get16bit(doh, index);
-    if((type != DNS_TYPE_CNAME)    /* may be synthesized from DNAME */
-       && (type != DNS_TYPE_DNAME) /* if present, accept and ignore */
+    if((type != CURL_DNS_TYPE_CNAME)    /* may be synthesized from DNAME */
+       && (type != CURL_DNS_TYPE_DNAME) /* if present, accept and ignore */
        && (type != dnstype))
       /* Not the same type as was asked for nor CNAME nor DNAME */
       return DOH_DNS_UNEXPECTED_TYPE;
@@ -855,9 +855,10 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh,
     return DOH_DNS_MALFORMAT; /* something is wrong */
 
 #ifdef USE_HTTTPS
-  if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr && !d->numhttps_rrs)
+  if((type != CURL_DNS_TYPE_NS) && !d->numcname && !d->numaddr &&
+      !d->numhttps_rrs)
 #else
-  if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
+  if((type != CURL_DNS_TYPE_NS) && !d->numcname && !d->numaddr)
 #endif
     /* nothing stored! */
     return DOH_NO_CONTENT;
@@ -873,12 +874,12 @@ static void doh_show(struct Curl_easy *data,
   infof(data, "[DoH] TTL: %u seconds", d->ttl);
   for(i = 0; i < d->numaddr; i++) {
     const struct dohaddr *a = &d->addr[i];
-    if(a->type == DNS_TYPE_A) {
+    if(a->type == CURL_DNS_TYPE_A) {
       infof(data, "[DoH] A: %u.%u.%u.%u",
             a->ip.v4[0], a->ip.v4[1],
             a->ip.v4[2], a->ip.v4[3]);
     }
-    else if(a->type == DNS_TYPE_AAAA) {
+    else if(a->type == CURL_DNS_TYPE_AAAA) {
       int j;
       char buffer[128] = "[DoH] AAAA: ";
       size_t len = strlen(buffer);
@@ -948,7 +949,7 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname,
   for(i = 0; i < de->numaddr; i++) {
     size_t ss_size;
     CURL_SA_FAMILY_T addrtype;
-    if(de->addr[i].type == DNS_TYPE_AAAA) {
+    if(de->addr[i].type == CURL_DNS_TYPE_AAAA) {
 #ifndef USE_IPV6
       /* we cannot handle IPv6 addresses */
       continue;
@@ -1025,12 +1026,12 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname,
 static const char *doh_type2name(DNStype dnstype)
 {
   switch(dnstype) {
-    case DNS_TYPE_A:
+    case CURL_DNS_TYPE_A:
       return "A";
-    case DNS_TYPE_AAAA:
+    case CURL_DNS_TYPE_AAAA:
       return "AAAA";
 #ifdef USE_HTTPSRR
-    case DNS_TYPE_HTTPS:
+    case CURL_DNS_TYPE_HTTPS:
       return "HTTPS";
 #endif
     default:
@@ -1248,8 +1249,8 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
                                  p->dnstype, &de);
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
       if(rc[slot]) {
-        infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
-              doh_type2name(p->dnstype), dohp->host);
+        CURL_TRC_DNS(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
+                     doh_type2name(p->dnstype), dohp->host);
       }
 #endif
     } /* next slot */

+ 6 - 6
Utilities/cmcurl/lib/doh.h

@@ -51,12 +51,12 @@ typedef enum {
 } DOHcode;
 
 typedef enum {
-  DNS_TYPE_A = 1,
-  DNS_TYPE_NS = 2,
-  DNS_TYPE_CNAME = 5,
-  DNS_TYPE_AAAA = 28,
-  DNS_TYPE_DNAME = 39,           /* RFC6672 */
-  DNS_TYPE_HTTPS = 65
+  CURL_DNS_TYPE_A = 1,
+  CURL_DNS_TYPE_NS = 2,
+  CURL_DNS_TYPE_CNAME = 5,
+  CURL_DNS_TYPE_AAAA = 28,
+  CURL_DNS_TYPE_DNAME = 39,           /* RFC6672 */
+  CURL_DNS_TYPE_HTTPS = 65
 } DNStype;
 
 enum doh_slot_num {

+ 61 - 34
Utilities/cmcurl/lib/easy.c

@@ -107,7 +107,7 @@ static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT;
  * ways, but at this point it must be defined as the system-supplied strdup
  * so the callback pointer is initialized correctly.
  */
-#if defined(UNDER_CE)
+#ifdef UNDER_CE
 #define system_strdup _strdup
 #elif !defined(HAVE_STRDUP)
 #define system_strdup Curl_strdup
@@ -465,8 +465,8 @@ static int events_socket(CURL *easy,      /* easy handle */
   bool found = FALSE;
   struct Curl_easy *data = easy;
 
-#if defined(CURL_DISABLE_VERBOSE_STRINGS)
-  (void) easy;
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
+  (void)easy;
 #endif
   (void)socketp;
 
@@ -567,6 +567,39 @@ static unsigned int populate_fds(struct pollfd *fds, struct events *ev)
   return numfds;
 }
 
+/* poll_fds()
+ *
+ * poll the fds[] array
+ */
+static CURLcode poll_fds(struct events *ev,
+                         struct pollfd *fds,
+                         const unsigned int numfds,
+                         int *pollrc)
+{
+  if(numfds) {
+    /* wait for activity or timeout */
+#if DEBUG_EV_POLL
+    fprintf(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);
+#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);
+#endif
+    *pollrc = 0;
+    if(ev->ms > 0)
+      curlx_wait_ms(ev->ms);
+  }
+  return CURLE_OK;
+}
+
 /* wait_or_timeout()
  *
  * waits for activity on any of the given sockets, or the timeout to trigger.
@@ -587,27 +620,9 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
     /* get the time stamp to use to figure out how long poll takes */
     before = curlx_now();
 
-    if(numfds) {
-      /* wait for activity or timeout */
-#if DEBUG_EV_POLL
-      fprintf(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);
-#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);
-#endif
-      pollrc = 0;
-      if(ev->ms > 0)
-        curlx_wait_ms(ev->ms);
-    }
+    result = poll_fds(ev, fds, numfds, &pollrc);
+    if(result)
+      return result;
 
     ev->msbump = FALSE; /* reset here */
 
@@ -822,7 +837,6 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
   return result;
 }
 
-
 /*
  * curl_easy_perform() is the external interface that performs a blocking
  * transfer as previously setup.
@@ -841,7 +855,6 @@ CURLcode curl_easy_perform_ev(struct Curl_easy *data)
 {
   return easy_perform(data, TRUE);
 }
-
 #endif
 
 /*
@@ -950,7 +963,11 @@ static void dupeasy_meta_freeentry(void *p)
 CURL *curl_easy_duphandle(CURL *d)
 {
   struct Curl_easy *data = d;
-  struct Curl_easy *outcurl = calloc(1, sizeof(struct Curl_easy));
+  struct Curl_easy *outcurl = NULL;
+
+  if(!GOOD_EASY_HANDLE(data))
+    goto fail;
+  outcurl = calloc(1, sizeof(struct Curl_easy));
   if(!outcurl)
     goto fail;
 
@@ -1074,6 +1091,9 @@ fail:
 void curl_easy_reset(CURL *d)
 {
   struct Curl_easy *data = d;
+  if(!GOOD_EASY_HANDLE(data))
+    return;
+
   Curl_req_hard_reset(&data->req, data);
   Curl_hash_clean(&data->meta_hash);
 
@@ -1097,6 +1117,7 @@ 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: */
   memset(&data->state.authhost, 0, sizeof(struct auth));
@@ -1154,13 +1175,15 @@ CURLcode curl_easy_pause(CURL *d, int action)
 
   /* If not completely pausing both directions now, run again in any case. */
   if(!Curl_xfer_is_blocked(data)) {
-    Curl_expire(data, 0, EXPIRE_RUN_NOW);
     /* reset the too-slow time keeper */
     data->state.keeps_speed.tv_sec = 0;
-    /* On changes, tell application to update its timers. */
-    if(changed && data->multi) {
-      if(Curl_update_timer(data->multi) && !result)
-        result = CURLE_ABORTED_BY_CALLBACK;
+    if(data->multi) {
+      Curl_multi_mark_dirty(data); /* make it run */
+      /* On changes, tell application to update its timers. */
+      if(changed) {
+        if(Curl_update_timer(data->multi) && !result)
+          result = CURLE_ABORTED_BY_CALLBACK;
+      }
     }
   }
 
@@ -1213,6 +1236,8 @@ CURLcode curl_easy_recv(CURL *d, void *buffer, size_t buflen, size_t *n)
   struct connectdata *c;
   struct Curl_easy *data = d;
 
+  if(!GOOD_EASY_HANDLE(data))
+    return CURLE_BAD_FUNCTION_ARGUMENT;
   if(Curl_is_in_callback(data))
     return CURLE_RECURSIVE_API_CALL;
 
@@ -1288,6 +1313,8 @@ CURLcode curl_easy_send(CURL *d, const void *buffer, size_t buflen, size_t *n)
   size_t written = 0;
   CURLcode result;
   struct Curl_easy *data = d;
+  if(!GOOD_EASY_HANDLE(data))
+    return CURLE_BAD_FUNCTION_ARGUMENT;
   if(Curl_is_in_callback(data))
     return CURLE_RECURSIVE_API_CALL;
 
@@ -1317,7 +1344,7 @@ CURLcode curl_easy_ssls_import(CURL *d, const char *session_key,
                                const unsigned char *shmac, size_t shmac_len,
                                const unsigned char *sdata, size_t sdata_len)
 {
-#ifdef USE_SSLS_EXPORT
+#if defined(USE_SSL) && defined(USE_SSLS_EXPORT)
   struct Curl_easy *data = d;
   if(!GOOD_EASY_HANDLE(data))
     return CURLE_BAD_FUNCTION_ARGUMENT;
@@ -1338,7 +1365,7 @@ CURLcode curl_easy_ssls_export(CURL *d,
                                curl_ssls_export_cb *export_fn,
                                void *userptr)
 {
-#ifdef USE_SSLS_EXPORT
+#if defined(USE_SSL) && defined(USE_SSLS_EXPORT)
   struct Curl_easy *data = d;
   if(!GOOD_EASY_HANDLE(data))
     return CURLE_BAD_FUNCTION_ARGUMENT;

+ 1 - 7
Utilities/cmcurl/lib/easy_lock.h

@@ -30,12 +30,6 @@
 
 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600
 
-#ifdef __MINGW32__
-#ifndef SRWLOCK_INIT
-#define SRWLOCK_INIT NULL
-#endif
-#endif /* __MINGW32__ */
-
 #define curl_simple_lock SRWLOCK
 #define CURL_SIMPLE_LOCK_INIT SRWLOCK_INIT
 
@@ -44,7 +38,7 @@
 
 #elif defined(HAVE_ATOMIC) && defined(HAVE_STDATOMIC_H)
 #include <stdatomic.h>
-#if defined(HAVE_SCHED_YIELD)
+#ifdef HAVE_SCHED_YIELD
 #include <sched.h>
 #endif
 

+ 1 - 1
Utilities/cmcurl/lib/easygetopt.c

@@ -82,7 +82,7 @@ const struct curl_easyoption *curl_easy_option_by_name(const char *name)
   return NULL;
 }
 
-const struct curl_easyoption *curl_easy_option_by_id (CURLoption id)
+const struct curl_easyoption *curl_easy_option_by_id(CURLoption id)
 {
   (void)id;
   return NULL;

+ 10 - 10
Utilities/cmcurl/lib/file.c

@@ -120,10 +120,10 @@ const struct Curl_handler Curl_handler_file = {
   file_connect,                         /* connect_it */
   ZERO_NULL,                            /* connecting */
   ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* proto_pollset */
+  ZERO_NULL,                            /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  ZERO_NULL,                            /* perform_pollset */
   file_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
@@ -292,8 +292,8 @@ static CURLcode file_done(struct Curl_easy *data,
                           CURLcode status, bool premature)
 {
   struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY);
-  (void)status; /* not used */
-  (void)premature; /* not used */
+  (void)status;
+  (void)premature;
 
   if(file)
     file_cleanup(file);
@@ -305,7 +305,7 @@ static CURLcode file_disconnect(struct Curl_easy *data,
                                 struct connectdata *conn,
                                 bool dead_connection)
 {
-  (void)dead_connection; /* not used */
+  (void)dead_connection;
   (void)conn;
   return file_done(data, CURLE_OK, FALSE);
 }
@@ -348,7 +348,7 @@ static CURLcode file_upload(struct Curl_easy *data,
     mode |= O_TRUNC;
 
 #if (defined(ANDROID) || defined(__ANDROID__)) && \
-    (defined(__i386__) || defined(__arm__))
+  (defined(__i386__) || defined(__arm__))
   fd = open(file->path, mode, (mode_t)data->set.new_file_perms);
 #else
   fd = open(file->path, mode, data->set.new_file_perms);
@@ -358,7 +358,7 @@ static CURLcode file_upload(struct Curl_easy *data,
     return CURLE_WRITE_ERROR;
   }
 
-  if(-1 != data->state.infilesize)
+  if(data->state.infilesize != -1)
     /* known size of data to "upload" */
     Curl_pgrsSetUploadSize(data, data->state.infilesize);
 
@@ -470,7 +470,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
   fd = file->fd;
 
   /* VMS: This only works reliable for STREAMLF files */
-  if(-1 != fstat(fd, &statbuf)) {
+  if(fstat(fd, &statbuf) != -1) {
     if(!S_ISDIR(statbuf.st_mode))
       expected_size = statbuf.st_size;
     /* and store the modification time */

+ 3 - 0
Utilities/cmcurl/lib/fileinfo.c

@@ -23,7 +23,9 @@
  ***************************************************************************/
 
 #include "curl_setup.h"
+
 #ifndef CURL_DISABLE_FTP
+
 #include "strdup.h"
 #include "fileinfo.h"
 #include "curl_memory.h"
@@ -43,4 +45,5 @@ void Curl_fileinfo_cleanup(struct fileinfo *finfo)
   curlx_dyn_free(&finfo->buf);
   free(finfo);
 }
+
 #endif

+ 1 - 1
Utilities/cmcurl/lib/fopen.c

@@ -136,7 +136,7 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
 
   result = CURLE_WRITE_ERROR;
 #if (defined(ANDROID) || defined(__ANDROID__)) && \
-    (defined(__i386__) || defined(__arm__))
+  (defined(__i386__) || defined(__arm__))
   fd = 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);

+ 14 - 12
Utilities/cmcurl/lib/formdata.c

@@ -186,22 +186,24 @@ static void free_formlist(struct FormInfo *ptr)
  * Examples:
  *
  * Simple name/value pair with copied contents:
- * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
- * CURLFORM_COPYCONTENTS, "value", CURLFORM_END);
+ * curl_formadd(&post, &last, CURLFORM_COPYNAME, "name",
+ *              CURLFORM_COPYCONTENTS, "value", CURLFORM_END);
  *
  * name/value pair where only the content pointer is remembered:
- * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
- * CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10, CURLFORM_END);
+ * curl_formadd(&post, &last, CURLFORM_COPYNAME, "name",
+ *              CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10,
+ *              CURLFORM_END);
  * (if CURLFORM_CONTENTSLENGTH is missing strlen () is used)
  *
  * storing a filename (CONTENTTYPE is optional!):
- * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
- * CURLFORM_FILE, "filename1", CURLFORM_CONTENTTYPE, "plain/text",
- * CURLFORM_END);
+ * curl_formadd(&post, &last, CURLFORM_COPYNAME, "name",
+ *              CURLFORM_FILE, "filename1", CURLFORM_CONTENTTYPE, "plain/text",
+ *              CURLFORM_END);
  *
  * storing multiple filenames:
- * curl_formadd (&post, &last, CURLFORM_COPYNAME, "name",
- * CURLFORM_FILE, "filename1", CURLFORM_FILE, "filename2", CURLFORM_END);
+ * curl_formadd(&post, &last, CURLFORM_COPYNAME, "name",
+ *              CURLFORM_FILE, "filename1", CURLFORM_FILE, "filename2",
+ *              CURLFORM_END);
  *
  * Returns:
  * CURL_FORMADD_OK             on success
@@ -926,9 +928,9 @@ CURLFORMcode curl_formadd(struct curl_httppost **httppost,
 int curl_formget(struct curl_httppost *form, void *arg,
                  curl_formget_callback append)
 {
-  (void) form;
-  (void) arg;
-  (void) append;
+  (void)form;
+  (void)arg;
+  (void)append;
   return CURL_FORMADD_DISABLED;
 }
 

Diferenças do arquivo suprimidas por serem muito extensas
+ 529 - 500
Utilities/cmcurl/lib/ftp.c


+ 8 - 2
Utilities/cmcurl/lib/ftp.h

@@ -120,6 +120,11 @@ struct FTP {
   curl_off_t downloadsize;
 };
 
+/* one struct entry for each path component (of 'rawpath') */
+struct pathcomp {
+  int start; /* start column */
+  int len;   /* length in bytes */
+};
 
 /* ftp_conn is used for struct connection-oriented data in the connectdata
    struct */
@@ -128,8 +133,9 @@ struct ftp_conn {
   char *account;
   char *alternative_to_user;
   char *entrypath; /* the PWD reply when we logged on */
-  char *file;    /* url-decoded filename (or path) */
-  char **dirs;   /* realloc()ed array for path components */
+  const char *file; /* url-decoded filename (or path), points into rawpath */
+  char *rawpath; /* URL decoded, allocated, version of the path */
+  struct pathcomp *dirs; /* allocated array for path components */
   char *newhost; /* the (allocated) IP addr or hostname to connect the data
                     connection to */
   char *prevpath;   /* url-decoded conn->path from the previous transfer */

+ 9 - 9
Utilities/cmcurl/lib/gopher.c

@@ -70,10 +70,10 @@ const struct Curl_handler Curl_handler_gopher = {
   ZERO_NULL,                            /* connect_it */
   ZERO_NULL,                            /* connecting */
   ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* proto_pollset */
+  ZERO_NULL,                            /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  ZERO_NULL,                            /* perform_pollset */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
@@ -96,10 +96,10 @@ const struct Curl_handler Curl_handler_gophers = {
   gopher_connect,                       /* connect_it */
   gopher_connecting,                    /* connecting */
   ZERO_NULL,                            /* doing */
-  ZERO_NULL,                            /* proto_getsock */
-  ZERO_NULL,                            /* doing_getsock */
-  ZERO_NULL,                            /* domore_getsock */
-  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* proto_pollset */
+  ZERO_NULL,                            /* doing_pollset */
+  ZERO_NULL,                            /* domore_pollset */
+  ZERO_NULL,                            /* perform_pollset */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp_hd */
@@ -240,7 +240,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
   if(result)
     return result;
 
-  Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE);
+  Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
   return CURLE_OK;
 }
 #endif /* CURL_DISABLE_GOPHER */

+ 5 - 3
Utilities/cmcurl/lib/hmac.c

@@ -73,7 +73,8 @@ Curl_HMAC_init(const struct HMAC_params *hashparams,
 
   /* If the key is too long, replace it by its hash digest. */
   if(keylen > hashparams->maxkeylen) {
-    hashparams->hinit(ctxt->hashctxt1);
+    if(hashparams->hinit(ctxt->hashctxt1))
+      return NULL;
     hashparams->hupdate(ctxt->hashctxt1, key, keylen);
     hkey = (unsigned char *) ctxt->hashctxt2 + hashparams->ctxtsize;
     hashparams->hfinal(hkey, ctxt->hashctxt1);
@@ -82,8 +83,9 @@ Curl_HMAC_init(const struct HMAC_params *hashparams,
   }
 
   /* Prime the two hash contexts with the modified key. */
-  hashparams->hinit(ctxt->hashctxt1);
-  hashparams->hinit(ctxt->hashctxt2);
+  if(hashparams->hinit(ctxt->hashctxt1) ||
+     hashparams->hinit(ctxt->hashctxt2))
+    return NULL;
 
   for(i = 0; i < keylen; i++) {
     b = (unsigned char)(*key ^ hmac_ipad);

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff