Ver Fonte

curl 2025-07-16 (cfbfb650)

Code extracted from:

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

at commit cfbfb65047e85e6b08af65fe9cdbcf68e9ad496a (curl-8_15_0).
Curl Upstream há 3 meses atrás
pai
commit
71e5adbcc9
100 ficheiros alterados com 3313 adições e 2920 exclusões
  1. 3 3
      CMake/CurlTests.c
  2. 0 58
      CMake/FindBearSSL.cmake
  3. 61 53
      CMake/FindGSS.cmake
  4. 6 6
      CMake/FindLibgsasl.cmake
  5. 15 10
      CMake/FindNGTCP2.cmake
  6. 78 0
      CMake/Macros.cmake
  7. 12 7
      CMake/OtherTests.cmake
  8. 4 3
      CMake/PickyWarnings.cmake
  9. 28 0
      CMake/Utilities.cmake
  10. 0 3
      CMake/unix-cache.cmake
  11. 0 5
      CMake/win32-cache.cmake
  12. 38 64
      CMakeLists.txt
  13. 15 27
      include/curl/curl.h
  14. 4 4
      include/curl/curlver.h
  15. 6 54
      include/curl/system.h
  16. 14 21
      lib/CMakeLists.txt
  17. 6 6
      lib/Makefile.inc
  18. 6 4
      lib/altsvc.c
  19. 4 3
      lib/asyn-ares.c
  20. 4 2
      lib/asyn-thrdd.c
  21. 101 209
      lib/bufq.c
  22. 28 38
      lib/bufq.h
  23. 11 16
      lib/cf-h1-proxy.c
  24. 123 177
      lib/cf-h2-proxy.c
  25. 6 7
      lib/cf-haproxy.c
  26. 29 20
      lib/cf-https-connect.c
  27. 51 76
      lib/cf-socket.c
  28. 4 4
      lib/cf-socket.h
  29. 194 99
      lib/cfilters.c
  30. 98 47
      lib/cfilters.h
  31. 0 1
      lib/conncache.c
  32. 1 1
      lib/conncache.h
  33. 27 29
      lib/connect.c
  34. 12 13
      lib/content_encoding.c
  35. 14 14
      lib/cookie.c
  36. 11 18
      lib/cshutdn.c
  37. 0 27
      lib/curl_config.h.cmake
  38. 0 1
      lib/curl_des.c
  39. 0 1
      lib/curl_des.h
  40. 25 10
      lib/curl_get_line.c
  41. 286 12
      lib/curl_gssapi.c
  42. 14 11
      lib/curl_gssapi.h
  43. 14 23
      lib/curl_memory.h
  44. 7 36
      lib/curl_ntlm_core.c
  45. 4 0
      lib/curl_ntlm_core.h
  46. 23 20
      lib/curl_rtmp.c
  47. 315 224
      lib/curl_sasl.c
  48. 0 4
      lib/curl_sasl.h
  49. 51 71
      lib/curl_setup.h
  50. 9 9
      lib/curl_setup_once.h
  51. 0 2
      lib/curl_sha512_256.c
  52. 8 43
      lib/curl_sspi.c
  53. 0 1
      lib/curl_sspi.h
  54. 1 2
      lib/curl_trc.c
  55. 14 9
      lib/curlx/binmode.h
  56. 9 0
      lib/curlx/curlx.h
  57. 20 6
      lib/curlx/inet_ntop.c
  58. 5 5
      lib/curlx/inet_ntop.h
  59. 1 1
      lib/curlx/inet_pton.h
  60. 9 6
      lib/curlx/strparse.c
  61. 3 3
      lib/curlx/strparse.h
  62. 97 0
      lib/curlx/wait.c
  63. 5 8
      lib/curlx/wait.h
  64. 0 23
      lib/curlx/warnless.c
  65. 3 16
      lib/curlx/warnless.h
  66. 2 3
      lib/cw-out.c
  67. 6 7
      lib/dict.c
  68. 2 4
      lib/doh.c
  69. 3 3
      lib/dynhds.c
  70. 27 65
      lib/easy.c
  71. 3 4
      lib/easygetopt.c
  72. 7 9
      lib/escape.c
  73. 1 2
      lib/escape.h
  74. 0 1
      lib/formdata.c
  75. 53 15
      lib/ftp.c
  76. 2 0
      lib/ftp.h
  77. 437 344
      lib/ftplistparser.c
  78. 6 10
      lib/getinfo.c
  79. 1 1
      lib/hash.c
  80. 1 1
      lib/hash.h
  81. 3 4
      lib/headers.c
  82. 8 6
      lib/hostip.c
  83. 3 1
      lib/hostip6.c
  84. 9 6
      lib/hsts.c
  85. 452 341
      lib/http.c
  86. 171 227
      lib/http2.c
  87. 2 2
      lib/http_aws_sigv4.c
  88. 26 18
      lib/http_negotiate.c
  89. 0 4
      lib/http_negotiate.h
  90. 13 18
      lib/http_ntlm.c
  91. 0 4
      lib/http_ntlm.h
  92. 15 17
      lib/http_proxy.c
  93. 4 6
      lib/http_proxy.h
  94. 5 6
      lib/if2ip.c
  95. 24 31
      lib/imap.c
  96. 54 54
      lib/krb5.c
  97. 7 8
      lib/ldap.c
  98. 2 1
      lib/llist.c
  99. 1 2
      lib/llist.h
  100. 26 19
      lib/memdebug.c

+ 3 - 3
CMake/CurlTests.c

@@ -203,7 +203,7 @@ int main(void)
 #ifdef HAVE_UNISTD_H
 #  include <unistd.h>
 #endif
-#ifdef HAVE_SYS_SOCKET_H
+#ifndef _WIN32
 #  include <sys/socket.h>
 #endif
 #ifdef HAVE_SYS_IOCTL_H
@@ -230,7 +230,7 @@ int main(void)
 #ifdef HAVE_UNISTD_H
 #  include <unistd.h>
 #endif
-#ifdef HAVE_SYS_SOCKET_H
+#ifndef _WIN32
 #  include <sys/socket.h>
 #endif
 #ifdef HAVE_SYS_IOCTL_H
@@ -257,7 +257,7 @@ int main(void)
 #ifdef HAVE_SYS_TYPES_H
 #  include <sys/types.h>
 #endif
-#ifdef HAVE_SYS_SOCKET_H
+#ifndef _WIN32
 #  include <sys/socket.h>
 #endif
 int main(void)

+ 0 - 58
CMake/FindBearSSL.cmake

@@ -1,58 +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 BearSSL library
-#
-# Input variables:
-#
-# - `BEARSSL_INCLUDE_DIR`:   The BearSSL include directory.
-# - `BEARSSL_LIBRARY`:       Path to `bearssl` library.
-#
-# Result variables:
-#
-# - `BEARSSL_FOUND`:         System has BearSSL.
-# - `BEARSSL_INCLUDE_DIRS`:  The BearSSL include directories.
-# - `BEARSSL_LIBRARIES`:     The BearSSL library names.
-
-if(DEFINED BEARSSL_INCLUDE_DIRS AND NOT DEFINED BEARSSL_INCLUDE_DIR)
-  message(WARNING "BEARSSL_INCLUDE_DIRS is deprecated, use BEARSSL_INCLUDE_DIR instead.")
-  set(BEARSSL_INCLUDE_DIR "${BEARSSL_INCLUDE_DIRS}")
-  unset(BEARSSL_INCLUDE_DIRS)
-endif()
-
-find_path(BEARSSL_INCLUDE_DIR NAMES "bearssl.h")
-find_library(BEARSSL_LIBRARY NAMES "bearssl")
-
-include(FindPackageHandleStandardArgs)
-find_package_handle_standard_args(BearSSL
-  REQUIRED_VARS
-    BEARSSL_INCLUDE_DIR
-    BEARSSL_LIBRARY
-)
-
-if(BEARSSL_FOUND)
-  set(BEARSSL_INCLUDE_DIRS ${BEARSSL_INCLUDE_DIR})
-  set(BEARSSL_LIBRARIES    ${BEARSSL_LIBRARY})
-endif()
-
-mark_as_advanced(BEARSSL_INCLUDE_DIR BEARSSL_LIBRARY)

+ 61 - 53
CMake/FindGSS.cmake

@@ -52,19 +52,23 @@ set(_gss_root_hints
   "$ENV{GSS_ROOT_DIR}"
 )
 
+set(_gss_CFLAGS "")
+set(_gss_LIBRARY_DIRS "")
+
 # Try to find library using system pkg-config if user did not specify root dir
 if(NOT GSS_ROOT_DIR AND NOT "$ENV{GSS_ROOT_DIR}")
   if(CURL_USE_PKGCONFIG)
     find_package(PkgConfig QUIET)
-    pkg_search_module(_GSS ${_gnu_modname} ${_mit_modname} ${_heimdal_modname})
-    list(APPEND _gss_root_hints "${_GSS_PREFIX}")
+    pkg_search_module(_gss ${_gnu_modname} ${_mit_modname} ${_heimdal_modname})
+    list(APPEND _gss_root_hints "${_gss_PREFIX}")
+    set(_gss_version "${_gss_VERSION}")
   endif()
   if(WIN32)
     list(APPEND _gss_root_hints "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos;InstallDir]")
   endif()
 endif()
 
-if(NOT _GSS_FOUND)  # Not found by pkg-config. Let us take more traditional approach.
+if(NOT _gss_FOUND)  # Not found by pkg-config. Let us take more traditional approach.
   find_file(_gss_configure_script
     NAMES
       "krb5-config"
@@ -85,25 +89,29 @@ if(NOT _GSS_FOUND)  # Not found by pkg-config. Let us take more traditional appr
   )
 
   if(_gss_configure_script)
+
+    set(_gss_INCLUDE_DIRS "")
+    set(_gss_LIBRARIES "")
+
     execute_process(
       COMMAND ${_gss_configure_script} "--cflags" "gssapi"
-      OUTPUT_VARIABLE _GSS_CFLAGS
+      OUTPUT_VARIABLE _gss_cflags_raw
       RESULT_VARIABLE _gss_configure_failed
       OUTPUT_STRIP_TRAILING_WHITESPACE
     )
-    message(STATUS "FindGSS krb5-config --cflags: ${_GSS_CFLAGS}")
+    message(STATUS "FindGSS krb5-config --cflags: ${_gss_cflags_raw}")
     if(NOT _gss_configure_failed)  # 0 means success
-      # Should also work in an odd case when multiple directories are given
-      string(STRIP "${_GSS_CFLAGS}" _GSS_CFLAGS)
-      string(REGEX REPLACE " +-I" ";" _GSS_CFLAGS "${_GSS_CFLAGS}")
-      string(REGEX REPLACE " +-([^I][^ \\t;]*)" ";-\\1" _GSS_CFLAGS "${_GSS_CFLAGS}")
+      # Should also work in an odd case when multiple directories are given.
+      string(STRIP "${_gss_cflags_raw}" _gss_cflags_raw)
+      string(REGEX REPLACE " +-(I)" ";-\\1" _gss_cflags_raw "${_gss_cflags_raw}")
+      string(REGEX REPLACE " +-([^I][^ \\t;]*)" ";-\\1" _gss_cflags_raw "${_gss_cflags_raw}")
 
-      foreach(_flag IN LISTS _GSS_CFLAGS)
+      foreach(_flag IN LISTS _gss_cflags_raw)
         if(_flag MATCHES "^-I")
-          string(REGEX REPLACE "^-I" "" _val "${_flag}")
-          list(APPEND _GSS_INCLUDE_DIRS "${_val}")
+          string(REGEX REPLACE "^-I" "" _flag "${_flag}")
+          list(APPEND _gss_INCLUDE_DIRS "${_flag}")
         else()
-          list(APPEND _GSS_CFLAGS "${_flag}")
+          list(APPEND _gss_CFLAGS "${_flag}")
         endif()
       endforeach()
     endif()
@@ -117,32 +125,32 @@ if(NOT _GSS_FOUND)  # Not found by pkg-config. Let us take more traditional appr
     message(STATUS "FindGSS krb5-config --libs: ${_gss_lib_flags}")
 
     if(NOT _gss_configure_failed)  # 0 means success
-      # This script gives us libraries and link directories. Blah. We have to deal with it.
+      # This script gives us libraries and link directories.
       string(STRIP "${_gss_lib_flags}" _gss_lib_flags)
       string(REGEX REPLACE " +-(L|l)" ";-\\1" _gss_lib_flags "${_gss_lib_flags}")
       string(REGEX REPLACE " +-([^Ll][^ \\t;]*)" ";-\\1" _gss_lib_flags "${_gss_lib_flags}")
 
       foreach(_flag IN LISTS _gss_lib_flags)
         if(_flag MATCHES "^-l")
-          string(REGEX REPLACE "^-l" "" _val "${_flag}")
-          list(APPEND _GSS_LIBRARIES "${_val}")
+          string(REGEX REPLACE "^-l" "" _flag "${_flag}")
+          list(APPEND _gss_LIBRARIES "${_flag}")
         elseif(_flag MATCHES "^-L")
-          string(REGEX REPLACE "^-L" "" _val "${_flag}")
-          list(APPEND _GSS_LIBRARY_DIRS "${_val}")
+          string(REGEX REPLACE "^-L" "" _flag "${_flag}")
+          list(APPEND _gss_LIBRARY_DIRS "${_flag}")
         endif()
       endforeach()
     endif()
 
     execute_process(
       COMMAND ${_gss_configure_script} "--version"
-      OUTPUT_VARIABLE _GSS_VERSION
+      OUTPUT_VARIABLE _gss_version
       RESULT_VARIABLE _gss_configure_failed
       OUTPUT_STRIP_TRAILING_WHITESPACE
     )
 
     # Older versions may not have the "--version" parameter. In this case we just do not care.
     if(_gss_configure_failed)
-      set(_GSS_VERSION 0)
+      set(_gss_version 0)
     endif()
 
     execute_process(
@@ -165,7 +173,7 @@ if(NOT _GSS_FOUND)  # Not found by pkg-config. Let us take more traditional appr
 
   else()  # Either there is no config script or we are on a platform that does not provide one (Windows?)
 
-    find_path(_GSS_INCLUDE_DIRS NAMES "gssapi/gssapi.h"
+    find_path(_gss_INCLUDE_DIRS NAMES "gssapi/gssapi.h"
       HINTS
         ${_gss_root_hints}
       PATH_SUFFIXES
@@ -173,9 +181,9 @@ if(NOT _GSS_FOUND)  # Not found by pkg-config. Let us take more traditional appr
         "inc"
     )
 
-    if(_GSS_INCLUDE_DIRS)  # jay, we have found something
+    if(_gss_INCLUDE_DIRS)  # jay, we have found something
       cmake_push_check_state()
-      list(APPEND CMAKE_REQUIRED_INCLUDES "${_GSS_INCLUDE_DIRS}")
+      list(APPEND CMAKE_REQUIRED_INCLUDES "${_gss_INCLUDE_DIRS}")
       check_include_files("gssapi/gssapi_generic.h;gssapi/gssapi_krb5.h" _gss_have_mit_headers)
 
       if(_gss_have_mit_headers)
@@ -193,7 +201,7 @@ if(NOT _GSS_FOUND)  # Not found by pkg-config. Let us take more traditional appr
       cmake_pop_check_state()
     else()
       # I am not convinced if this is the right way but this is what autotools do at the moment
-      find_path(_GSS_INCLUDE_DIRS NAMES "gssapi.h"
+      find_path(_gss_INCLUDE_DIRS NAMES "gssapi.h"
         HINTS
           ${_gss_root_hints}
         PATH_SUFFIXES
@@ -201,17 +209,17 @@ if(NOT _GSS_FOUND)  # Not found by pkg-config. Let us take more traditional appr
           "inc"
       )
 
-      if(_GSS_INCLUDE_DIRS)
+      if(_gss_INCLUDE_DIRS)
         set(GSS_FLAVOUR "Heimdal")
       else()
-        find_path(_GSS_INCLUDE_DIRS NAMES "gss.h"
+        find_path(_gss_INCLUDE_DIRS NAMES "gss.h"
           HINTS
             ${_gss_root_hints}
           PATH_SUFFIXES
             "include"
         )
 
-        if(_GSS_INCLUDE_DIRS)
+        if(_gss_INCLUDE_DIRS)
           set(GSS_FLAVOUR "GNU")
           set(GSS_PC_REQUIRES "gss")
         endif()
@@ -222,7 +230,7 @@ if(NOT _GSS_FOUND)  # Not found by pkg-config. Let us take more traditional appr
     if(GSS_FLAVOUR)
       set(_gss_libdir_suffixes "")
       set(_gss_libdir_hints ${_gss_root_hints})
-      get_filename_component(_gss_calculated_potential_root "${_GSS_INCLUDE_DIRS}" DIRECTORY)
+      get_filename_component(_gss_calculated_potential_root "${_gss_INCLUDE_DIRS}" DIRECTORY)
       list(APPEND _gss_libdir_hints ${_gss_calculated_potential_root})
 
       if(WIN32)
@@ -256,7 +264,7 @@ if(NOT _GSS_FOUND)  # Not found by pkg-config. Let us take more traditional appr
         endif()
       endif()
 
-      find_library(_GSS_LIBRARIES NAMES ${_gss_libname}
+      find_library(_gss_LIBRARIES NAMES ${_gss_libname}
         HINTS
           ${_gss_libdir_hints}
         PATH_SUFFIXES
@@ -265,36 +273,36 @@ if(NOT _GSS_FOUND)  # Not found by pkg-config. Let us take more traditional appr
     endif()
   endif()
 else()
-  # _GSS_MODULE_NAME set since CMake 3.16
-  if(_GSS_MODULE_NAME STREQUAL _gnu_modname OR _GSS_${_gnu_modname}_VERSION)
+  # _gss_MODULE_NAME set since CMake 3.16
+  if(_gss_MODULE_NAME STREQUAL _gnu_modname OR _gss_${_gnu_modname}_VERSION)
     set(GSS_FLAVOUR "GNU")
     set(GSS_PC_REQUIRES "gss")
-    if(NOT _GSS_VERSION)  # for old CMake versions?
-      set(_GSS_VERSION ${_GSS_${_gnu_modname}_VERSION})
+    if(NOT _gss_version)  # for old CMake versions?
+      set(_gss_version ${_gss_${_gnu_modname}_VERSION})
     endif()
-  elseif(_GSS_MODULE_NAME STREQUAL _mit_modname OR _GSS_${_mit_modname}_VERSION)
+  elseif(_gss_MODULE_NAME STREQUAL _mit_modname OR _gss_${_mit_modname}_VERSION)
     set(GSS_FLAVOUR "MIT")
     set(GSS_PC_REQUIRES "mit-krb5-gssapi")
-    if(NOT _GSS_VERSION)  # for old CMake versions?
-      set(_GSS_VERSION ${_GSS_${_mit_modname}_VERSION})
+    if(NOT _gss_version)  # for old CMake versions?
+      set(_gss_version ${_gss_${_mit_modname}_VERSION})
     endif()
   else()
     set(GSS_FLAVOUR "Heimdal")
     set(GSS_PC_REQUIRES "heimdal-gssapi")
-    if(NOT _GSS_VERSION)  # for old CMake versions?
-      set(_GSS_VERSION ${_GSS_${_heimdal_modname}_VERSION})
+    if(NOT _gss_version)  # for old CMake versions?
+      set(_gss_version ${_gss_${_heimdal_modname}_VERSION})
     endif()
   endif()
-  message(STATUS "Found GSS/${GSS_FLAVOUR} (via pkg-config): ${_GSS_INCLUDE_DIRS} (found version \"${_GSS_VERSION}\")")
+  message(STATUS "Found GSS/${GSS_FLAVOUR} (via pkg-config): ${_gss_INCLUDE_DIRS} (found version \"${_gss_version}\")")
 endif()
 
-string(REPLACE ";" " " _GSS_CFLAGS "${_GSS_CFLAGS}")
+string(REPLACE ";" " " _gss_CFLAGS "${_gss_CFLAGS}")
 
-set(GSS_INCLUDE_DIRS ${_GSS_INCLUDE_DIRS})
-set(GSS_LIBRARIES ${_GSS_LIBRARIES})
-set(GSS_LIBRARY_DIRS ${_GSS_LIBRARY_DIRS})
-set(GSS_CFLAGS ${_GSS_CFLAGS})
-set(GSS_VERSION ${_GSS_VERSION})
+set(GSS_INCLUDE_DIRS ${_gss_INCLUDE_DIRS})
+set(GSS_LIBRARIES ${_gss_LIBRARIES})
+set(GSS_LIBRARY_DIRS ${_gss_LIBRARY_DIRS})
+set(GSS_CFLAGS ${_gss_CFLAGS})
+set(GSS_VERSION ${_gss_version})
 
 if(GSS_FLAVOUR)
   if(NOT GSS_VERSION AND GSS_FLAVOUR STREQUAL "Heimdal")
@@ -346,12 +354,12 @@ find_package_handle_standard_args(GSS
 )
 
 mark_as_advanced(
-  _GSS_CFLAGS
-  _GSS_FOUND
-  _GSS_INCLUDE_DIRS
-  _GSS_LIBRARIES
-  _GSS_LIBRARY_DIRS
-  _GSS_MODULE_NAME
-  _GSS_PREFIX
-  _GSS_VERSION
+  _gss_CFLAGS
+  _gss_FOUND
+  _gss_INCLUDE_DIRS
+  _gss_LIBRARIES
+  _gss_LIBRARY_DIRS
+  _gss_MODULE_NAME
+  _gss_PREFIX
+  _gss_version
 )

+ 6 - 6
CMake/FindLibgsasl.cmake

@@ -66,12 +66,12 @@ else()
   endif()
 
   include(FindPackageHandleStandardArgs)
-    find_package_handle_standard_args(Libgsasl
-    REQUIRED_VARS
-      LIBGSASL_INCLUDE_DIR
-      LIBGSASL_LIBRARY
-    VERSION_VAR
-      LIBGSASL_VERSION
+  find_package_handle_standard_args(Libgsasl
+  REQUIRED_VARS
+    LIBGSASL_INCLUDE_DIR
+    LIBGSASL_LIBRARY
+  VERSION_VAR
+    LIBGSASL_VERSION
   )
 
   if(LIBGSASL_FOUND)

+ 15 - 10
CMake/FindNGTCP2.cmake

@@ -34,23 +34,28 @@
 #
 # Input variables:
 #
-# - `NGTCP2_INCLUDE_DIR`:   The ngtcp2 include directory.
-# - `NGTCP2_LIBRARY`:       Path to `ngtcp2` library.
+# - `NGTCP2_INCLUDE_DIR`:               The ngtcp2 include directory.
+# - `NGTCP2_LIBRARY`:                   Path to `ngtcp2` library.
+# - `NGTCP2_CRYPTO_BORINGSSL_LIBRARY`:  Path to `ngtcp2_crypto_boringssl` library.
+# - `NGTCP2_CRYPTO_GNUTLS_LIBRARY`:     Path to `ngtcp2_crypto_gnutls` library.
+# - `NGTCP2_CRYPTO_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.
 #
 # Result variables:
 #
-# - `NGTCP2_FOUND`:         System has ngtcp2.
-# - `NGTCP2_INCLUDE_DIRS`:  The ngtcp2 include directories.
-# - `NGTCP2_LIBRARIES`:     The ngtcp2 library names.
-# - `NGTCP2_LIBRARY_DIRS`:  The ngtcp2 library directories.
-# - `NGTCP2_PC_REQUIRES`:   The ngtcp2 pkg-config packages.
-# - `NGTCP2_CFLAGS`:        Required compiler flags.
-# - `NGTCP2_VERSION`:       Version of ngtcp2.
+# - `NGTCP2_FOUND`:                     System has ngtcp2.
+# - `NGTCP2_INCLUDE_DIRS`:              The ngtcp2 include directories.
+# - `NGTCP2_LIBRARIES`:                 The ngtcp2 library names.
+# - `NGTCP2_LIBRARY_DIRS`:              The ngtcp2 library directories.
+# - `NGTCP2_PC_REQUIRES`:               The ngtcp2 pkg-config packages.
+# - `NGTCP2_CFLAGS`:                    Required compiler flags.
+# - `NGTCP2_VERSION`:                   Version of ngtcp2.
 
 if(NGTCP2_FIND_COMPONENTS)
   set(_ngtcp2_crypto_backend "")
   foreach(_component IN LISTS NGTCP2_FIND_COMPONENTS)
-    if(_component MATCHES "^(BoringSSL|quictls|wolfSSL|GnuTLS|ossl)")
+    if(_component MATCHES "^(BoringSSL|GnuTLS|ossl|quictls|wolfSSL)")
       if(_ngtcp2_crypto_backend)
         message(FATAL_ERROR "NGTCP2: Only one crypto library can be selected")
       endif()

+ 78 - 0
CMake/Macros.cmake

@@ -65,6 +65,7 @@ macro(curl_internal_test _curl_test)
   endif()
 endmacro()
 
+# Option for dependencies that accepts an 'AUTO' value, which enables the dependency if detected.
 macro(curl_dependency_option _option_name _find_name _desc_name)
   set(${_option_name} "AUTO" CACHE STRING "Build curl with ${_desc_name} support (AUTO, ON or OFF)")
   set_property(CACHE ${_option_name} PROPERTY STRINGS "AUTO" "ON" "OFF")
@@ -94,3 +95,80 @@ macro(curl_prefill_type_size _type _size)
   set(SIZEOF_${_type} ${_size})
   set(SIZEOF_${_type}_CODE "#define SIZEOF_${_type} ${_size}")
 endmacro()
+
+# Internal: Recurse into target libraries and collect their include directories
+# and macro definitions.
+macro(curl_collect_target_options _target)
+  get_target_property(_val ${_target} INTERFACE_INCLUDE_DIRECTORIES)
+  if(_val)
+    list(APPEND _includes ${_val})
+  endif()
+  get_target_property(_val ${_target} INCLUDE_DIRECTORIES)
+  if(_val)
+    list(APPEND _includes ${_val})
+  endif()
+  get_target_property(_val ${_target} COMPILE_DEFINITIONS)
+  if(_val)
+    list(APPEND _definitions ${_val})
+  endif()
+  get_target_property(_val ${_target} LINK_LIBRARIES)
+  if(_val)
+    foreach(_lib IN LISTS _val)
+      if(TARGET "${_lib}")
+        curl_collect_target_options(${_lib})
+      endif()
+    endforeach()
+  endif()
+  unset(_val)
+endmacro()
+
+# Create a clang-tidy target for test targets
+macro(curl_add_clang_tidy_test_target _target_clang_tidy _target)
+  if(CURL_CLANG_TIDY)
+
+    set(_includes "")
+    set(_definitions "")
+
+    # Collect header directories and macro definitions applying to the directory
+    get_directory_property(_val INCLUDE_DIRECTORIES)
+    if(_val)
+      list(APPEND _includes ${_val})
+    endif()
+    get_directory_property(_val COMPILE_DEFINITIONS)
+    if(_val)
+      list(APPEND _definitions ${_val})
+    endif()
+    unset(_val)
+
+    # Collect header directories and macro definitions from lib dependencies
+    curl_collect_target_options(${_target})
+
+    list(REMOVE_ITEM _includes "")
+    string(REPLACE ";" ";-I" _includes ";${_includes}")
+    list(REMOVE_DUPLICATES _includes)
+
+    list(REMOVE_ITEM _definitions "")
+    string(REPLACE ";" ";-D" _definitions ";${_definitions}")
+    list(REMOVE_DUPLICATES _definitions)
+    list(SORT _definitions)  # Sort like CMake does
+
+    # Assemble source list
+    set(_sources "")
+    foreach(_source IN ITEMS ${ARGN})
+      if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${_source}")  # if not in source tree
+        set(_source "${CMAKE_CURRENT_BINARY_DIR}/${_source}")  # look in the build tree, for generated files, e.g. lib1521.c
+      endif()
+      list(APPEND _sources "${_source}")
+    endforeach()
+
+    add_custom_target(${_target_clang_tidy} USES_TERMINAL
+      WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+      COMMAND ${CMAKE_C_CLANG_TIDY} ${_sources} -- ${_includes} ${_definitions}
+      DEPENDS ${_sources})
+    add_dependencies(tests-clang-tidy ${_target_clang_tidy})
+
+    unset(_includes)
+    unset(_definitions)
+    unset(_sources)
+  endif()
+endmacro()

+ 12 - 7
CMake/OtherTests.cmake

@@ -25,6 +25,7 @@ include(CheckCSourceCompiles)
 include(CheckCSourceRuns)
 include(CheckTypeSize)
 
+# #include header if condition is true
 macro(curl_add_header_include _check _header)
   if(${_check})
     set(_source_epilogue "${_source_epilogue}
@@ -41,7 +42,7 @@ if(NOT DEFINED HAVE_STRUCT_SOCKADDR_STORAGE)
   if(WIN32)
     set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h")
     list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32")
-  elseif(HAVE_SYS_SOCKET_H)
+  else()
     set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h")
   endif()
   check_type_size("struct sockaddr_storage" SIZEOF_STRUCT_SOCKADDR_STORAGE)
@@ -52,8 +53,8 @@ endif()
 if(NOT WIN32)
   set(_source_epilogue "#undef inline")
   curl_add_header_include(HAVE_SYS_TYPES_H "sys/types.h")
-  curl_add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h")
   check_c_source_compiles("${_source_epilogue}
+    #include <sys/socket.h>
     int main(void)
     {
       int flag = MSG_NOSIGNAL;
@@ -63,11 +64,13 @@ if(NOT WIN32)
 endif()
 
 set(_source_epilogue "#undef inline")
-curl_add_header_include(HAVE_SYS_TIME_H "sys/time.h")
 check_c_source_compiles("${_source_epilogue}
   #ifdef _MSC_VER
   #include <winsock2.h>
   #endif
+  #ifndef _WIN32
+  #include <sys/time.h>
+  #endif
   #include <time.h>
   int main(void)
   {
@@ -100,9 +103,11 @@ elseif(BSD OR CMAKE_SYSTEM_NAME MATCHES "BSD")
 endif()
 
 if(NOT DEFINED HAVE_GETADDRINFO_THREADSAFE)
-  set(_source_epilogue "#undef inline")
-  curl_add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h")
-  curl_add_header_include(HAVE_SYS_TIME_H "sys/time.h")
+  set(_source_epilogue "#undef inline
+    #ifndef _WIN32
+    #include <sys/socket.h>
+    #include <sys/time.h>
+    #endif")
   curl_add_header_include(HAVE_NETDB_H "netdb.h")
   check_c_source_compiles("${_source_epilogue}
     int main(void)
@@ -143,8 +148,8 @@ endif()
 if(NOT WIN32 AND NOT DEFINED HAVE_CLOCK_GETTIME_MONOTONIC_RAW)
   set(_source_epilogue "#undef inline")
   curl_add_header_include(HAVE_SYS_TYPES_H "sys/types.h")
-  curl_add_header_include(HAVE_SYS_TIME_H "sys/time.h")
   check_c_source_compiles("${_source_epilogue}
+    #include <sys/time.h>
     #include <time.h>
     int main(void)
     {

+ 4 - 3
CMake/PickyWarnings.cmake

@@ -316,8 +316,9 @@ if(PICKY_COMPILER)
     list(APPEND _picky "-wd4668")  # 'M' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' (in winbase.h)
     list(APPEND _picky "-wd4710")  # 'snprintf': function not inlined
     list(APPEND _picky "-wd4711")  # function 'A' selected for automatic inline expansion
-    list(APPEND _picky "-wd4746")  # volatile access of '<expression>' is subject to /volatile:<iso|ms> setting;
-                                   #   consider using __iso_volatile_load/store intrinsic functions (ARM64)
+    # volatile access of '<expression>' is subject to /volatile:<iso|ms> setting;
+    #   consider using __iso_volatile_load/store intrinsic functions (ARM64)
+    list(APPEND _picky "-wd4746")
     list(APPEND _picky "-wd4774")  # 'snprintf': format string expected in argument 3 is not a string literal
     list(APPEND _picky "-wd4820")  # 'A': 'N' bytes padding added after data member 'B'
     if(MSVC_VERSION GREATER_EQUAL 1900)
@@ -340,7 +341,7 @@ if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND MSVC)
         list(APPEND _picky_tmp "-clang:${_ccopt}")
       endif()
     endforeach()
-    set("${_wlist}" ${_picky_tmp})
+    set("${_wlist}" ${_picky_tmp})  # cmake-lint: disable=C0103
   endforeach()
 endif()
 

+ 28 - 0
CMake/Utilities.cmake

@@ -51,3 +51,31 @@ function(curl_dumpvars)
   endforeach()
   message("::endgroup::")
 endfunction()
+
+# Dump all target properties
+function(curl_dumptargetprops _target)
+  if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19 AND TARGET "${_target}")
+    execute_process(COMMAND "${CMAKE_COMMAND}" "--help-property-list" OUTPUT_VARIABLE _cmake_property_list)
+    string(REPLACE "\n" ";" _cmake_property_list "${_cmake_property_list}")
+    list(REMOVE_DUPLICATES _cmake_property_list)
+    list(REMOVE_ITEM _cmake_property_list "")
+    foreach(_prop IN LISTS _cmake_property_list)
+      if(_prop MATCHES "<CONFIG>")
+        foreach(_config IN ITEMS "DEBUG" "RELEASE" "MINSIZEREL" "RELWITHDEBINFO")
+          string(REPLACE "<CONFIG>" "${_config}" _propconfig "${_prop}")
+          get_property(_is_set TARGET "${_target}" PROPERTY "${_propconfig}" SET)
+          if(_is_set)
+            get_target_property(_val "${_target}" "${_propconfig}")
+            message("${_target}.${_propconfig} = '${_val}'")
+          endif()
+        endforeach()
+      else()
+        get_property(_is_set TARGET "${_target}" PROPERTY "${_prop}" SET)
+        if(_is_set)
+          get_target_property(_val "${_target}" "${_prop}")
+          message("${_target}.${_prop} = '${_val}'")
+        endif()
+      endif()
+    endforeach()
+  endif()
+endfunction()

+ 0 - 3
CMake/unix-cache.cmake

@@ -280,15 +280,12 @@ set(HAVE_SYS_PARAM_H 1)
 set(HAVE_SYS_POLL_H 1)
 set(HAVE_SYS_RESOURCE_H 1)
 set(HAVE_SYS_SELECT_H 1)
-set(HAVE_SYS_SOCKET_H 1)
 if(CYGWIN OR
    CMAKE_SYSTEM_NAME STREQUAL "Linux")
   set(HAVE_SYS_SOCKIO_H 0)
 else()
   set(HAVE_SYS_SOCKIO_H 1)
 endif()
-set(HAVE_SYS_STAT_H 1)
-set(HAVE_SYS_TIME_H 1)
 set(HAVE_SYS_TYPES_H 1)
 set(HAVE_SYS_UN_H 1)
 if(CYGWIN)

+ 0 - 5
CMake/win32-cache.cmake

@@ -39,7 +39,6 @@ if(MINGW)
   set(HAVE_STDINT_H 1)  # detected by CMake internally in check_type_size()
   set(HAVE_STRINGS_H 1)  # wrapper to string.h
   set(HAVE_SYS_PARAM_H 1)
-  set(HAVE_SYS_TIME_H 1)
   set(HAVE_UNISTD_H 1)
   set(HAVE_UTIME_H 1)  # wrapper to sys/utime.h
 else()
@@ -50,7 +49,6 @@ else()
   set(HAVE_OPENDIR 0)
   set(HAVE_STRINGS_H 0)
   set(HAVE_SYS_PARAM_H 0)
-  set(HAVE_SYS_TIME_H 0)
   set(HAVE_UTIME_H 0)
   if(MSVC)
     set(HAVE_UNISTD_H 0)
@@ -118,7 +116,6 @@ set(HAVE_GETSOCKNAME 1)
 set(HAVE_GLIBC_STRERROR_R 0)
 set(HAVE_GMTIME_R 0)
 set(HAVE_IFADDRS_H 0)
-set(HAVE_IF_NAMETOINDEX 0)
 set(HAVE_INET_NTOP 0)
 set(HAVE_INET_PTON 0)
 set(HAVE_IOCTLSOCKET 1)
@@ -171,9 +168,7 @@ set(HAVE_SYS_IOCTL_H 0)
 set(HAVE_SYS_POLL_H 0)
 set(HAVE_SYS_RESOURCE_H 0)
 set(HAVE_SYS_SELECT_H 0)
-set(HAVE_SYS_SOCKET_H 0)
 set(HAVE_SYS_SOCKIO_H 0)
-set(HAVE_SYS_STAT_H 1)
 set(HAVE_SYS_TYPES_H 1)
 set(HAVE_SYS_UN_H 0)
 set(HAVE_SYS_UTIME_H 1)

+ 38 - 64
CMakeLists.txt

@@ -277,7 +277,6 @@ if(ENABLE_DEBUG)
   message(WARNING "This curl build is Debug-enabled and insecure, do not use in production.")
 endif()
 option(ENABLE_CURLDEBUG "Enable TrackMemory debug feature" ${ENABLE_DEBUG})
-option(ENABLE_SERVER_DEBUG "Apply curl debug options to test servers" OFF)
 
 set(CURL_DEBUG_MACROS "")
 if(ENABLE_DEBUG)
@@ -287,15 +286,11 @@ if(ENABLE_CURLDEBUG)
   list(APPEND CURL_DEBUG_MACROS "CURLDEBUG")
 endif()
 
-option(CURL_TEST_BUNDLES "Build tests into single-binary bundles" OFF)
-
 option(CURL_CLANG_TIDY "Run the build through clang-tidy" OFF)
 if(CURL_CLANG_TIDY)
-  # clang-tidy is not looking into #included sources, thus not compatible with
-  # unity builds and test bundles.
-  set(CMAKE_UNITY_BUILD OFF)
-  set(CURL_TEST_BUNDLES OFF)
+  set(CMAKE_UNITY_BUILD OFF)  # clang-tidy is not looking into #included sources, thus not compatible with unity builds.
   set(_tidy_checks "")
+  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.insecureAPI.DeprecatedOrUnsafeBufferHandling")
@@ -376,6 +371,7 @@ if(ENABLE_ARES)
   list(APPEND CURL_LIBS ${CARES_LIBRARIES})
   list(APPEND CURL_LIBDIRS ${CARES_LIBRARY_DIRS})
   list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${CARES_PC_REQUIRES})
+  include_directories(SYSTEM ${CARES_INCLUDE_DIRS})
   link_directories(${CARES_LIBRARY_DIRS})
   if(CARES_CFLAGS)
     string(APPEND CMAKE_C_FLAGS " ${CARES_CFLAGS}")
@@ -520,7 +516,7 @@ if(PERL_EXECUTABLE)
     DEPENDS "${PROJECT_SOURCE_DIR}/scripts/mk-ca-bundle.pl"
   )
   add_custom_target(curl-ca-firefox
-    COMMENT "generating a fresh ca-bundle.crt" VERBATIM USES_TERMINAL
+    COMMENT "Generating a fresh ca-bundle.crt" VERBATIM USES_TERMINAL
     COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/firefox-db2pem.sh" "lib/ca-bundle.crt"
     DEPENDS "${PROJECT_SOURCE_DIR}/scripts/firefox-db2pem.sh"
   )
@@ -605,10 +601,12 @@ if(WIN32)
     set(_win32_winsock "ws2_32")
   endif()
   set(_win32_crypt32 "crypt32")
+  set(_win32_secur32 "secur32")
 
   if(MINGW32CE)  # FIXME upstream: must specify the full path to avoid CMake converting "ws2" to "ws2.lib"
     set(_win32_winsock "${MINGW32CE_LIBRARY_DIR}/lib${_win32_winsock}.a")
     set(_win32_crypt32 "${MINGW32CE_LIBRARY_DIR}/lib${_win32_crypt32}.a")
+    set(_win32_secur32 "${MINGW32CE_LIBRARY_DIR}/lib${_win32_secur32}.a")
   endif()
 elseif(DOS)
   if(WATT_ROOT)
@@ -677,24 +675,18 @@ if(CURL_DEFAULT_SSL_BACKEND)
   set(_valid_default_ssl_backend FALSE)
 endif()
 
-if(APPLE)
-  cmake_dependent_option(CURL_USE_SECTRANSP "Enable Apple OS native SSL/TLS (Secure Transport)" OFF CURL_ENABLE_SSL OFF)
-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})
 endif()
 cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
-cmake_dependent_option(CURL_USE_BEARSSL "Enable BearSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
 cmake_dependent_option(CURL_USE_WOLFSSL "Enable wolfSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
 cmake_dependent_option(CURL_USE_GNUTLS "Enable GnuTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
 cmake_dependent_option(CURL_USE_RUSTLS "Enable Rustls for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
 
 if(WIN32 OR
-   CURL_USE_SECTRANSP OR
    CURL_USE_SCHANNEL OR
    CURL_USE_MBEDTLS OR
-   CURL_USE_BEARSSL OR
    CURL_USE_WOLFSSL OR
    CURL_USE_GNUTLS OR
    CURL_USE_RUSTLS)
@@ -712,10 +704,8 @@ option(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG "Disable automatic loading of OpenS
 
 curl_count_true(_enabled_ssl_options_count
   CURL_USE_SCHANNEL
-  CURL_USE_SECTRANSP
   CURL_USE_OPENSSL
   CURL_USE_MBEDTLS
-  CURL_USE_BEARSSL
   CURL_USE_WOLFSSL
   CURL_USE_GNUTLS
   CURL_USE_RUSTLS
@@ -739,26 +729,6 @@ if(CURL_WINDOWS_SSPI)
   set(USE_WINDOWS_SSPI ON)
 endif()
 
-if(CURL_USE_SECTRANSP)
-  set(_use_core_foundation_and_core_services ON)
-
-  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 CURL_LIBS "-framework Security")
-
-  set(_ssl_enabled ON)
-  set(USE_SECTRANSP ON)
-
-  if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "secure-transport")
-    set(_valid_default_ssl_backend TRUE)
-  endif()
-
-  message(WARNING "Secure Transport does not support TLS 1.3.")
-endif()
-
 if(_use_core_foundation_and_core_services)
   find_library(COREFOUNDATION_FRAMEWORK NAMES "CoreFoundation")
   mark_as_advanced(COREFOUNDATION_FRAMEWORK)
@@ -842,21 +812,6 @@ if(CURL_USE_MBEDTLS)
   set(_curl_ca_bundle_supported TRUE)
 endif()
 
-if(CURL_USE_BEARSSL)
-  find_package(BearSSL REQUIRED)
-  set(_ssl_enabled ON)
-  set(USE_BEARSSL ON)
-  list(APPEND CURL_LIBS ${BEARSSL_LIBRARIES})
-  include_directories(SYSTEM ${BEARSSL_INCLUDE_DIRS})
-
-  if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "bearssl")
-    set(_valid_default_ssl_backend TRUE)
-  endif()
-  set(_curl_ca_bundle_supported TRUE)
-
-  message(WARNING "BearSSL does not support TLS 1.3.")
-endif()
-
 if(CURL_USE_WOLFSSL)
   find_package(WolfSSL REQUIRED)
   set(_ssl_enabled ON)
@@ -1651,6 +1606,23 @@ if(WIN32)
     # Windows XP is required for freeaddrinfo, getaddrinfo
     message(FATAL_ERROR "Building for Windows XP or newer is required.")
   endif()
+
+  # Pre-fill detection results based on target OS version
+  if(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)
+  endif()
+  unset(HAVE_IF_NAMETOINDEX CACHE)
+endif()
+
+if(NOT WIN32)
+  list(APPEND CURL_INCLUDES "sys/socket.h")
+endif()
+if(NOT WIN32 OR MINGW)
+  list(APPEND CURL_INCLUDES "sys/time.h")
 endif()
 
 # Detect headers
@@ -1665,10 +1637,7 @@ check_include_file("sys/param.h"      HAVE_SYS_PARAM_H)
 check_include_file("sys/poll.h"       HAVE_SYS_POLL_H)
 check_include_file("sys/resource.h"   HAVE_SYS_RESOURCE_H)
 check_include_file_concat_curl("sys/select.h"     HAVE_SYS_SELECT_H)
-check_include_file_concat_curl("sys/socket.h"     HAVE_SYS_SOCKET_H)
 check_include_file("sys/sockio.h"     HAVE_SYS_SOCKIO_H)
-check_include_file("sys/stat.h"       HAVE_SYS_STAT_H)
-check_include_file_concat_curl("sys/time.h"       HAVE_SYS_TIME_H)
 check_include_file_concat_curl("sys/types.h"      HAVE_SYS_TYPES_H)
 check_include_file("sys/un.h"         HAVE_SYS_UN_H)
 check_include_file_concat_curl("sys/utime.h"      HAVE_SYS_UTIME_H)  # sys/types.h (AmigaOS)
@@ -1710,10 +1679,9 @@ foreach(_variable IN ITEMS
     HAVE_STDBOOL_H
     HAVE_STROPTS_H
     HAVE_SYS_IOCTL_H
-    HAVE_SYS_SOCKET_H
     HAVE_SYS_TYPES_H
     HAVE_UNISTD_H
-    )
+)
   if(${_variable})
     string(APPEND CURL_TEST_DEFINES " -D${_variable}")
   endif()
@@ -1749,6 +1717,9 @@ endif()
 # Apply to all feature checks
 if(WIN32)
   list(APPEND CMAKE_REQUIRED_LIBRARIES "${_win32_winsock}")
+  if(NOT WINCE AND NOT WINDOWS_STORE)
+    list(APPEND CMAKE_REQUIRED_LIBRARIES "iphlpapi")
+  endif()
 elseif(HAVE_LIBSOCKET)
   list(APPEND CMAKE_REQUIRED_LIBRARIES "socket")
 elseif(DOS)
@@ -1800,12 +1771,12 @@ 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)
-  check_function_exists("if_nametoindex"  HAVE_IF_NAMETOINDEX)  # iphlpapi.h (Windows non-UWP), 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)
@@ -1852,7 +1823,7 @@ 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})
-elseif(HAVE_SYS_SOCKET_H)
+else()
   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})
@@ -1875,7 +1846,7 @@ foreach(_curl_test IN ITEMS
     HAVE_BOOL_T
     STDC_HEADERS
     HAVE_ATOMIC
-    )
+)
   curl_internal_test(${_curl_test})
 endforeach()
 
@@ -1984,6 +1955,9 @@ set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "HAVE_CONFIG_H")
 
 if(WIN32)
   list(APPEND CURL_LIBS "${_win32_winsock}")
+  if(NOT WINCE AND NOT WINDOWS_STORE)
+    list(APPEND CURL_LIBS "iphlpapi")
+  endif()
   if(NOT WINCE)
     list(APPEND CURL_LIBS "bcrypt")
   endif()
@@ -2004,6 +1978,9 @@ if(WIN32)
     endif()
     list(APPEND CURL_LIBS "${_win32_crypt32}")
   endif()
+  if(USE_WINDOWS_SSPI)
+    list(APPEND CURL_LIBS "${_win32_secur32}")
+  endif()
 endif()
 
 if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")  # MSVC but exclude clang-cl
@@ -2031,6 +2008,7 @@ endif()
 # (= regenerate it).
 function(curl_transform_makefile_inc _input_file _output_file)
   file(READ ${_input_file} _makefile_inc_text)
+  # cmake-lint: disable=W0106
   string(REPLACE "$(top_srcdir)"   "\${PROJECT_SOURCE_DIR}" _makefile_inc_text ${_makefile_inc_text})
   string(REPLACE "$(top_builddir)" "\${PROJECT_BINARY_DIR}" _makefile_inc_text ${_makefile_inc_text})
 
@@ -2102,7 +2080,6 @@ if(NOT CURL_DISABLE_NTLM AND
    (USE_OPENSSL OR
     USE_MBEDTLS OR
     USE_GNUTLS OR
-    USE_SECTRANSP OR
     USE_WIN32_CRYPTO OR
     (USE_WOLFSSL AND HAVE_WOLFSSL_DES_ECB_ENCRYPT)))
   set(_use_curl_ntlm_core ON)
@@ -2183,8 +2160,7 @@ curl_add_if("HTTP2"         USE_NGHTTP2)
 curl_add_if("HTTP3"         USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC)
 curl_add_if("MultiSSL"      CURL_WITH_MULTI_SSL)
 curl_add_if("HTTPS-proxy"   NOT CURL_DISABLE_PROXY AND _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS
-                            OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR
-                            USE_MBEDTLS OR USE_SECTRANSP OR
+                            OR USE_SCHANNEL OR USE_RUSTLS OR USE_MBEDTLS OR
                             (USE_WOLFSSL AND HAVE_WOLFSSL_BIO_NEW)))
 curl_add_if("Unicode"       ENABLE_UNICODE)
 curl_add_if("threadsafe"    HAVE_ATOMIC OR
@@ -2213,9 +2189,7 @@ set(_items "")
 curl_add_if("Schannel"         _ssl_enabled AND USE_SCHANNEL)
 curl_add_if("${_openssl}"      _ssl_enabled AND USE_OPENSSL AND OPENSSL_VERSION VERSION_LESS 3.0.0)
 curl_add_if("${_openssl} v3+"  _ssl_enabled AND USE_OPENSSL AND OPENSSL_VERSION VERSION_GREATER_EQUAL 3.0.0)
-curl_add_if("Secure Transport" _ssl_enabled AND USE_SECTRANSP)
 curl_add_if("mbedTLS"          _ssl_enabled AND USE_MBEDTLS)
-curl_add_if("BearSSL"          _ssl_enabled AND USE_BEARSSL)
 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)

+ 15 - 27
include/curl/curl.h

@@ -158,11 +158,11 @@ typedef enum {
   CURLSSLBACKEND_POLARSSL               CURL_DEPRECATED(7.69.0, "") = 6,
   CURLSSLBACKEND_WOLFSSL = 7,
   CURLSSLBACKEND_SCHANNEL = 8,
-  CURLSSLBACKEND_SECURETRANSPORT = 9,
+  CURLSSLBACKEND_SECURETRANSPORT        CURL_DEPRECATED(8.15.0, "") = 9,
   CURLSSLBACKEND_AXTLS                  CURL_DEPRECATED(7.61.0, "") = 10,
   CURLSSLBACKEND_MBEDTLS = 11,
   CURLSSLBACKEND_MESALINK               CURL_DEPRECATED(7.82.0, "") = 12,
-  CURLSSLBACKEND_BEARSSL = 13,
+  CURLSSLBACKEND_BEARSSL                CURL_DEPRECATED(8.15.0, "") = 13,
   CURLSSLBACKEND_RUSTLS = 14
 } curl_sslbackend;
 
@@ -645,20 +645,7 @@ typedef enum {
   CURLE_UNRECOVERABLE_POLL,      /* 99 - poll/select returned fatal error */
   CURLE_TOO_LARGE,               /* 100 - a value/data met its maximum */
   CURLE_ECH_REQUIRED,            /* 101 - ECH tried but failed */
-  CURL_LAST, /* never use! */
-
-  CURLE_RESERVED115 = 115,       /* 115-126 - used in tests */
-  CURLE_RESERVED116 = 116,
-  CURLE_RESERVED117 = 117,
-  CURLE_RESERVED118 = 118,
-  CURLE_RESERVED119 = 119,
-  CURLE_RESERVED120 = 120,
-  CURLE_RESERVED121 = 121,
-  CURLE_RESERVED122 = 122,
-  CURLE_RESERVED123 = 123,
-  CURLE_RESERVED124 = 124,
-  CURLE_RESERVED125 = 125,
-  CURLE_RESERVED126 = 126
+  CURL_LAST /* never use! */
 } CURLcode;
 
 #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
@@ -943,31 +930,31 @@ typedef enum {
    have introduced work-arounds for this flaw but those work-arounds sometimes
    make the SSL communication fail. To regain functionality with those broken
    servers, a user can this way allow the vulnerability back. */
-#define CURLSSLOPT_ALLOW_BEAST (1<<0)
+#define CURLSSLOPT_ALLOW_BEAST (1L<<0)
 
 /* - NO_REVOKE tells libcurl to disable certificate revocation checks for those
    SSL backends where such behavior is present. */
-#define CURLSSLOPT_NO_REVOKE (1<<1)
+#define CURLSSLOPT_NO_REVOKE (1L<<1)
 
 /* - NO_PARTIALCHAIN tells libcurl to *NOT* accept a partial certificate chain
    if possible. The OpenSSL backend has this ability. */
-#define CURLSSLOPT_NO_PARTIALCHAIN (1<<2)
+#define CURLSSLOPT_NO_PARTIALCHAIN (1L<<2)
 
 /* - REVOKE_BEST_EFFORT tells libcurl to ignore certificate revocation offline
    checks and ignore missing revocation list for those SSL backends where such
    behavior is present. */
-#define CURLSSLOPT_REVOKE_BEST_EFFORT (1<<3)
+#define CURLSSLOPT_REVOKE_BEST_EFFORT (1L<<3)
 
 /* - CURLSSLOPT_NATIVE_CA tells libcurl to use standard certificate store of
    operating system. Currently implemented under MS-Windows. */
-#define CURLSSLOPT_NATIVE_CA (1<<4)
+#define CURLSSLOPT_NATIVE_CA (1L<<4)
 
 /* - CURLSSLOPT_AUTO_CLIENT_CERT tells libcurl to automatically locate and use
    a client certificate for authentication. (Schannel) */
-#define CURLSSLOPT_AUTO_CLIENT_CERT (1<<5)
+#define CURLSSLOPT_AUTO_CLIENT_CERT (1L<<5)
 
 /* If possible, send data using TLS 1.3 early data */
-#define CURLSSLOPT_EARLYDATA (1<<6)
+#define CURLSSLOPT_EARLYDATA (1L<<6)
 
 /* The default connection attempt delay in milliseconds for happy eyeballs.
    CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.3 and happy-eyeballs-timeout-ms.d document
@@ -1967,7 +1954,8 @@ typedef enum {
   CURLOPT(CURLOPT_SSL_VERIFYSTATUS, CURLOPTTYPE_LONG, 232),
 
   /* Set if we should enable TLS false start. */
-  CURLOPT(CURLOPT_SSL_FALSESTART, CURLOPTTYPE_LONG, 233),
+  CURLOPTDEPRECATED(CURLOPT_SSL_FALSESTART, CURLOPTTYPE_LONG, 233,
+                    8.15.0, "Has no function"),
 
   /* Do not squash dot-dot sequences */
   CURLOPT(CURLOPT_PATH_AS_IS, CURLOPTTYPE_LONG, 234),
@@ -2301,10 +2289,10 @@ typedef enum {
   /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host
      name resolves addresses using more than one IP protocol version, this
      option might be handy to force libcurl to use a specific IP version. */
-#define CURL_IPRESOLVE_WHATEVER 0 /* default, uses addresses to all IP
+#define CURL_IPRESOLVE_WHATEVER 0L /* default, uses addresses to all IP
                                      versions that your system allows */
-#define CURL_IPRESOLVE_V4       1 /* uses only IPv4 addresses/connections */
-#define CURL_IPRESOLVE_V6       2 /* uses only IPv6 addresses/connections */
+#define CURL_IPRESOLVE_V4       1L /* uses only IPv4 addresses/connections */
+#define CURL_IPRESOLVE_V6       2L /* uses only IPv6 addresses/connections */
 
   /* Convenient "aliases" */
 #define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER

+ 4 - 4
include/curl/curlver.h

@@ -32,13 +32,13 @@
 
 /* This is the version number of the libcurl package from which this header
    file origins: */
-#define LIBCURL_VERSION "8.14.1-DEV"
+#define LIBCURL_VERSION "8.15.0-DEV"
 
 /* The numeric version number is also available "in parts" by using these
    defines: */
 #define LIBCURL_VERSION_MAJOR 8
-#define LIBCURL_VERSION_MINOR 14
-#define LIBCURL_VERSION_PATCH 1
+#define LIBCURL_VERSION_MINOR 15
+#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
@@ -59,7 +59,7 @@
    CURL_VERSION_BITS() macro since curl's own configure script greps for it
    and needs it to contain the full number.
 */
-#define LIBCURL_VERSION_NUM 0x080e01
+#define LIBCURL_VERSION_NUM 0x080f00
 
 /*
  * This is the date and time when the full source package was created. The

+ 6 - 54
include/curl/system.h

@@ -329,7 +329,7 @@
    defined(__ppc__) || defined(__powerpc__) || defined(__arm__) ||      \
    defined(__sparc__) || defined(__mips__) || defined(__sh__) ||        \
    defined(__XTENSA__) ||                                               \
-   (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4)  ||               \
+   (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4) ||                \
    (defined(__LONG_MAX__) && __LONG_MAX__ == 2147483647L))
 #    define CURL_TYPEOF_CURL_OFF_T     long long
 #    define CURL_FORMAT_CURL_OFF_T     "lld"
@@ -357,11 +357,11 @@
 
 #else
 /* generic "safe guess" on old 32-bit style */
-#  define CURL_TYPEOF_CURL_OFF_T     long
-#  define CURL_FORMAT_CURL_OFF_T     "ld"
-#  define CURL_FORMAT_CURL_OFF_TU    "lu"
-#  define CURL_SUFFIX_CURL_OFF_T     L
-#  define CURL_SUFFIX_CURL_OFF_TU    UL
+#  define CURL_TYPEOF_CURL_OFF_T     long long
+#  define CURL_FORMAT_CURL_OFF_T     "lld"
+#  define CURL_FORMAT_CURL_OFF_TU    "llu"
+#  define CURL_SUFFIX_CURL_OFF_T     LL
+#  define CURL_SUFFIX_CURL_OFF_TU    ULL
 #  define CURL_TYPEOF_CURL_SOCKLEN_T int
 #endif
 
@@ -399,52 +399,4 @@
   typedef CURL_TYPEOF_CURL_OFF_T curl_off_t;
 #endif
 
-/*
- * CURL_ISOCPP and CURL_OFF_T_C definitions are done here in order to allow
- * these to be visible and exported by the external libcurl interface API,
- * while also making them visible to the library internals, simply including
- * curl_setup.h, without actually needing to include curl.h internally.
- * If some day this section would grow big enough, all this should be moved
- * to its own header file.
- */
-
-/*
- * Figure out if we can use the ## preprocessor operator, which is supported
- * by ISO/ANSI C and C++. Some compilers support it without setting __STDC__
- * or  __cplusplus so we need to carefully check for them too.
- */
-
-#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \
-  defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \
-  defined(__POCC__) || defined(__HIGHC__) || \
-  defined(__ILEC400__)
-  /* This compiler is believed to have an ISO compatible preprocessor */
-#define CURL_ISOCPP
-#else
-  /* This compiler is believed NOT to have an ISO compatible preprocessor */
-#undef CURL_ISOCPP
-#endif
-
-/*
- * Macros for minimum-width signed and unsigned curl_off_t integer constants.
- */
-
-#if defined(__BORLANDC__) && (__BORLANDC__ == 0x0551)
-#  define CURLINC_OFF_T_C_HLPR2(x) x
-#  define CURLINC_OFF_T_C_HLPR1(x) CURLINC_OFF_T_C_HLPR2(x)
-#  define CURL_OFF_T_C(Val)  CURLINC_OFF_T_C_HLPR1(Val) ## \
-                             CURLINC_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T)
-#  define CURL_OFF_TU_C(Val) CURLINC_OFF_T_C_HLPR1(Val) ## \
-                             CURLINC_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU)
-#else
-#  ifdef CURL_ISOCPP
-#    define CURLINC_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix
-#  else
-#    define CURLINC_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix
-#  endif
-#  define CURLINC_OFF_T_C_HLPR1(Val,Suffix) CURLINC_OFF_T_C_HLPR2(Val,Suffix)
-#  define CURL_OFF_T_C(Val)  CURLINC_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T)
-#  define CURL_OFF_TU_C(Val) CURLINC_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU)
-#endif
-
 #endif /* CURLINC_SYSTEM_H */

+ 14 - 21
lib/CMakeLists.txt

@@ -29,7 +29,7 @@ set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "${CURL_DEBUG_MACROS}
 
 configure_file("curl_config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h")
 
-# Get 'CSOURCES', 'HHEADERS' variables
+# Get CSOURCES, HHEADERS, LIB_RCFILES variables
 curl_transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
 include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
 
@@ -41,28 +41,22 @@ set_property(DIRECTORY APPEND PROPERTY INCLUDE_DIRECTORIES
   "${PROJECT_BINARY_DIR}/lib"        # for "curl_config.h"
 )
 
-if(USE_ARES)
-  include_directories(SYSTEM ${CARES_INCLUDE_DIRS})
-endif()
-
 if(CURL_BUILD_TESTING)
-  add_library(
-    curlu  # special libcurlu library just for unittests
-    STATIC
-    EXCLUDE_FROM_ALL
-    ${HHEADERS} ${CSOURCES}
-  )
+  # special libcurlu library just for unittests
+  add_library(curlu STATIC EXCLUDE_FROM_ALL ${HHEADERS} ${CSOURCES})
   target_compile_definitions(curlu PUBLIC "CURL_STATICLIB" "UNITTESTS")
   target_link_libraries(curlu PRIVATE ${CURL_LIBS})
   # There is plenty of parallelism when building the testdeps target.
   # Override the curlu batch size with the maximum to optimize performance.
-  set_target_properties(curlu PROPERTIES UNITY_BUILD_BATCH_SIZE 0)
-endif()
+  set_target_properties(curlu PROPERTIES UNITY_BUILD_BATCH_SIZE 0 C_CLANG_TIDY "")
 
-if(ENABLE_CURLDEBUG)
-  # We must compile this source separately to avoid memdebug.h redefinitions
-  # applying to it.
-  set_source_files_properties("memdebug.c" PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
+  add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/unitprotos.h"
+    WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
+    COMMAND ${PERL_EXECUTABLE} "${PROJECT_SOURCE_DIR}/scripts/extract-unit-protos"
+      ${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")
 endif()
 
 ## Library definition
@@ -181,7 +175,7 @@ if(BUILD_SHARED_LIBS)
   add_library(${PROJECT_NAME}::${LIB_SHARED} ALIAS ${LIB_SHARED})
   if(WIN32)
     set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "dllmain.c")
-    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "libcurl.rc")
+    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES ${LIB_RCFILES})
     if(CURL_HIDES_PRIVATE_SYMBOLS)
       set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "${PROJECT_SOURCE_DIR}/lib/libcurl.def")
     endif()
@@ -217,6 +211,7 @@ if(BUILD_SHARED_LIBS)
     CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
     CMAKE_SYSTEM_NAME STREQUAL "SunOS" OR
     CMAKE_SYSTEM_NAME STREQUAL "Haiku" OR
+    CMAKE_SYSTEM_NAME STREQUAL "OHOS" OR  # OpenHarmony
     CMAKE_SYSTEM_NAME STREQUAL "GNU/kFreeBSD" OR
     # FreeBSD comes with the a.out and ELF flavours but a.out was supported
     # up to v3.x and ELF from v3.x. I cannot imagine someone running CMake
@@ -231,7 +226,7 @@ if(BUILD_SHARED_LIBS)
   option(CURL_LIBCURL_VERSIONED_SYMBOLS "Enable libcurl versioned symbols" OFF)
 
   if(CURL_LIBCURL_SOVERSION OR CURL_LIBCURL_VERSIONED_SYMBOLS)
-    # Get 'VERSIONCHANGE', 'VERSIONADD', 'VERSIONDEL', 'VERSIONINFO' variables
+    # Get VERSIONCHANGE, VERSIONADD, VERSIONDEL, VERSIONINFO variables
     curl_transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")
     include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")
 
@@ -255,8 +250,6 @@ if(BUILD_SHARED_LIBS)
         set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "OPENSSL_")
       elseif(CURL_USE_MBEDTLS)
         set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "MBEDTLS_")
-      elseif(CURL_USE_BEARSSL)
-        set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "BEARSSL_")
       elseif(CURL_USE_WOLFSSL)
         set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "WOLFSSL_")
       elseif(CURL_USE_GNUTLS)

+ 6 - 6
lib/Makefile.inc

@@ -21,10 +21,12 @@
 # SPDX-License-Identifier: curl
 #
 ###########################################################################
+# Shared between CMakeLists.txt and Makefile.am
 
 LIB_CURLX_CFILES = \
   curlx/base64.c   \
   curlx/dynbuf.c   \
+  curlx/inet_ntop.c \
   curlx/inet_pton.c \
   curlx/multibyte.c \
   curlx/nonblock.c \
@@ -32,13 +34,16 @@ LIB_CURLX_CFILES = \
   curlx/timediff.c \
   curlx/timeval.c  \
   curlx/version_win32.c \
+  curlx/wait.c     \
   curlx/warnless.c \
   curlx/winapi.c
 
 LIB_CURLX_HFILES = \
+  curlx/binmode.h  \
   curlx/base64.h   \
   curlx/curlx.h    \
   curlx/dynbuf.h   \
+  curlx/inet_ntop.h \
   curlx/inet_pton.h \
   curlx/multibyte.h \
   curlx/nonblock.h \
@@ -46,6 +51,7 @@ LIB_CURLX_HFILES = \
   curlx/timediff.h \
   curlx/timeval.h  \
   curlx/version_win32.h \
+  curlx/wait.h     \
   curlx/warnless.h \
   curlx/winapi.h
 
@@ -69,7 +75,6 @@ LIB_VAUTH_HFILES =      \
   vauth/vauth.h
 
 LIB_VTLS_CFILES =           \
-  vtls/bearssl.c            \
   vtls/cipher_suite.c       \
   vtls/gtls.c               \
   vtls/hostcheck.c          \
@@ -80,7 +85,6 @@ LIB_VTLS_CFILES =           \
   vtls/rustls.c             \
   vtls/schannel.c           \
   vtls/schannel_verify.c    \
-  vtls/sectransp.c          \
   vtls/vtls.c               \
   vtls/vtls_scache.c        \
   vtls/vtls_spack.c         \
@@ -88,7 +92,6 @@ LIB_VTLS_CFILES =           \
   vtls/x509asn1.c
 
 LIB_VTLS_HFILES =           \
-  vtls/bearssl.h            \
   vtls/cipher_suite.h       \
   vtls/gtls.h               \
   vtls/hostcheck.h          \
@@ -99,7 +102,6 @@ LIB_VTLS_HFILES =           \
   vtls/rustls.h             \
   vtls/schannel.h           \
   vtls/schannel_int.h       \
-  vtls/sectransp.h          \
   vtls/vtls.h               \
   vtls/vtls_int.h           \
   vtls/vtls_scache.h        \
@@ -208,7 +210,6 @@ LIB_CFILES =         \
   idn.c              \
   if2ip.c            \
   imap.c             \
-  inet_ntop.c        \
   krb5.c             \
   ldap.c             \
   llist.c            \
@@ -345,7 +346,6 @@ LIB_HFILES =         \
   idn.h              \
   if2ip.h            \
   imap.h             \
-  inet_ntop.h        \
   llist.h            \
   macos.h            \
   memdebug.h         \

+ 6 - 4
lib/altsvc.c

@@ -32,7 +32,6 @@
 #include "urldata.h"
 #include "altsvc.h"
 #include "curl_get_line.h"
-#include "strcase.h"
 #include "parsedate.h"
 #include "sendf.h"
 #include "curlx/warnless.h"
@@ -416,7 +415,7 @@ static bool hostcompare(const char *host, const char *check)
   if(hlen != clen)
     /* they cannot match if they have different lengths */
     return FALSE;
-  return strncasecompare(host, check, hlen);
+  return curl_strnequal(host, check, hlen);
 }
 
 /* altsvc_flush() removes all alternatives for this source origin from the
@@ -487,8 +486,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
   DEBUGASSERT(asi);
 
   /* initial check for "clear" */
-  if(!curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, ';') &&
-     !curlx_str_single(&p, ';')) {
+  if(!curlx_str_cspn(&p, &alpn, ";\n\r")) {
     curlx_str_trimblanks(&alpn);
     /* "clear" is a magic keyword */
     if(curlx_str_casecompare(&alpn, "clear")) {
@@ -666,4 +664,8 @@ bool Curl_altsvc_lookup(struct altsvcinfo *asi,
   return FALSE;
 }
 
+#if defined(DEBUGBUILD) || defined(UNITTESTS)
+#undef time
+#endif
+
 #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_ALTSVC */

+ 4 - 3
lib/asyn-ares.c

@@ -48,6 +48,7 @@
 #endif
 
 #include "urldata.h"
+#include "cfilters.h"
 #include "sendf.h"
 #include "hostip.h"
 #include "hash.h"
@@ -451,8 +452,7 @@ CURLcode Curl_async_await(struct Curl_easy *data,
   /* Operation complete, if the lookup was successful we now have the entry
      in the cache. */
   data->state.async.done = TRUE;
-  if(entry)
-    *entry = data->state.async.dns;
+  *entry = data->state.async.dns;
 
   if(result)
     ares_cancel(ares->channel);
@@ -758,7 +758,8 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
                  (pf == PF_UNSPEC) ? "A+AAAA" :
                  ((pf == PF_INET) ? "A" : "AAAA"));
     hints.ai_family = pf;
-    hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ?
+    hints.ai_socktype =
+      (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ?
       SOCK_STREAM : SOCK_DGRAM;
     /* Since the service is a numerical one, set the hint flags
      * accordingly to save a call to getservbyname in inside C-Ares

+ 4 - 2
lib/asyn-thrdd.c

@@ -55,13 +55,13 @@
 #endif
 
 #include "urldata.h"
+#include "cfilters.h"
 #include "sendf.h"
 #include "hostip.h"
 #include "hash.h"
 #include "share.h"
 #include "url.h"
 #include "multiif.h"
-#include "inet_ntop.h"
 #include "curl_threads.h"
 #include "strdup.h"
 
@@ -423,6 +423,7 @@ static bool async_thrdd_init(struct Curl_easy *data,
   data->state.async.done = FALSE;
   data->state.async.port = port;
   data->state.async.ip_version = ip_version;
+  free(data->state.async.hostname);
   data->state.async.hostname = strdup(hostname);
   if(!data->state.async.hostname)
     goto err_exit;
@@ -741,7 +742,8 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
 
   memset(&hints, 0, sizeof(hints));
   hints.ai_family = pf;
-  hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ?
+  hints.ai_socktype =
+    (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ?
     SOCK_STREAM : SOCK_DGRAM;
 
   /* fire up a new resolver thread! */

+ 101 - 209
lib/bufq.c

@@ -86,44 +86,26 @@ static size_t chunk_read(struct buf_chunk *chunk,
   }
 }
 
-static size_t chunk_unwrite(struct buf_chunk *chunk, size_t len)
-{
-  size_t n = chunk->w_offset - chunk->r_offset;
-  DEBUGASSERT(chunk->w_offset >= chunk->r_offset);
-  if(!n) {
-    return 0;
-  }
-  else if(n <= len) {
-    chunk->r_offset = chunk->w_offset = 0;
-    return n;
-  }
-  else {
-    chunk->w_offset -= len;
-    return len;
-  }
-}
-
-static ssize_t chunk_slurpn(struct buf_chunk *chunk, size_t max_len,
-                            Curl_bufq_reader *reader,
-                            void *reader_ctx, CURLcode *err)
+static CURLcode chunk_slurpn(struct buf_chunk *chunk, size_t max_len,
+                             Curl_bufq_reader *reader,
+                             void *reader_ctx, size_t *pnread)
 {
   unsigned char *p = &chunk->x.data[chunk->w_offset];
   size_t n = chunk->dlen - chunk->w_offset; /* free amount */
-  ssize_t nread;
+  CURLcode result;
 
+  *pnread = 0;
   DEBUGASSERT(chunk->dlen >= chunk->w_offset);
-  if(!n) {
-    *err = CURLE_AGAIN;
-    return -1;
-  }
+  if(!n)
+    return CURLE_AGAIN;
   if(max_len && n > max_len)
     n = max_len;
-  nread = reader(reader_ctx, p, n, err);
-  if(nread > 0) {
-    DEBUGASSERT((size_t)nread <= n);
-    chunk->w_offset += nread;
+  result = reader(reader_ctx, p, n, pnread);
+  if(!result) {
+    DEBUGASSERT(*pnread <= n);
+    chunk->w_offset += *pnread;
   }
-  return nread;
+  return result;
 }
 
 static void chunk_peek(const struct buf_chunk *chunk,
@@ -357,49 +339,6 @@ static void prune_head(struct bufq *q)
   }
 }
 
-static struct buf_chunk *chunk_prev(struct buf_chunk *head,
-                                    struct buf_chunk *chunk)
-{
-  while(head) {
-    if(head == chunk)
-      return NULL;
-    if(head->next == chunk)
-      return head;
-    head = head->next;
-  }
-  return NULL;
-}
-
-static void prune_tail(struct bufq *q)
-{
-  struct buf_chunk *chunk;
-
-  while(q->tail && chunk_is_empty(q->tail)) {
-    chunk = q->tail;
-    q->tail = chunk_prev(q->head, chunk);
-    if(q->tail)
-      q->tail->next = NULL;
-    if(q->head == chunk)
-      q->head = q->tail;
-    if(q->pool) {
-      bufcp_put(q->pool, chunk);
-      --q->chunk_count;
-    }
-    else if((q->chunk_count > q->max_chunks) ||
-       (q->opts & BUFQ_OPT_NO_SPARES)) {
-      /* SOFT_LIMIT allowed us more than max. free spares until
-       * we are at max again. Or free them if we are configured
-       * to not use spares. */
-      free(chunk);
-      --q->chunk_count;
-    }
-    else {
-      chunk->next = q->spare;
-      q->spare = chunk;
-    }
-  }
-}
-
 static struct buf_chunk *get_non_full_tail(struct bufq *q)
 {
   struct buf_chunk *chunk;
@@ -421,90 +360,60 @@ static struct buf_chunk *get_non_full_tail(struct bufq *q)
   return chunk;
 }
 
-ssize_t Curl_bufq_write(struct bufq *q,
-                        const unsigned char *buf, size_t len,
-                        CURLcode *err)
+CURLcode Curl_bufq_write(struct bufq *q,
+                         const unsigned char *buf, size_t len,
+                         size_t *pnwritten)
 {
   struct buf_chunk *tail;
-  ssize_t nwritten = 0;
   size_t n;
 
   DEBUGASSERT(q->max_chunks > 0);
+  *pnwritten = 0;
   while(len) {
     tail = get_non_full_tail(q);
     if(!tail) {
-      if((q->chunk_count < q->max_chunks) || (q->opts & BUFQ_OPT_SOFT_LIMIT)) {
-        *err = CURLE_OUT_OF_MEMORY;
-        return -1;
-      }
+      if((q->chunk_count < q->max_chunks) || (q->opts & BUFQ_OPT_SOFT_LIMIT))
+        /* should have gotten a tail, but did not */
+        return CURLE_OUT_OF_MEMORY;
       break;
     }
     n = chunk_append(tail, buf, len);
     if(!n)
       break;
-    nwritten += n;
+    *pnwritten += n;
     buf += n;
     len -= n;
   }
-  if(nwritten == 0 && len) {
-    *err = CURLE_AGAIN;
-    return -1;
-  }
-  *err = CURLE_OK;
-  return nwritten;
+  return (!*pnwritten && len) ? CURLE_AGAIN : CURLE_OK;
 }
 
 CURLcode Curl_bufq_cwrite(struct bufq *q,
                           const char *buf, size_t len,
                           size_t *pnwritten)
 {
-  ssize_t n;
-  CURLcode result;
-  n = Curl_bufq_write(q, (const unsigned char *)buf, len, &result);
-  *pnwritten = (n < 0) ? 0 : (size_t)n;
-  return result;
-}
-
-CURLcode Curl_bufq_unwrite(struct bufq *q, size_t len)
-{
-  while(len && q->tail) {
-    len -= chunk_unwrite(q->tail, len);
-    prune_tail(q);
-  }
-  return len ? CURLE_AGAIN : CURLE_OK;
+  return Curl_bufq_write(q, (const unsigned char *)buf, len, pnwritten);
 }
 
-ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
-                       CURLcode *err)
+CURLcode Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
+                        size_t *pnread)
 {
-  ssize_t nread = 0;
-  size_t n;
-
-  *err = CURLE_OK;
+  *pnread = 0;
   while(len && q->head) {
-    n = chunk_read(q->head, buf, len);
+    size_t n = chunk_read(q->head, buf, len);
     if(n) {
-      nread += n;
+      *pnread += n;
       buf += n;
       len -= n;
     }
     prune_head(q);
   }
-  if(nread == 0) {
-    *err = CURLE_AGAIN;
-    return -1;
-  }
-  return nread;
+  return (!*pnread) ? CURLE_AGAIN : CURLE_OK;
 }
 
 CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len,
                          size_t *pnread)
 {
-  ssize_t n;
-  CURLcode result;
-  n = Curl_bufq_read(q, (unsigned char *)buf, len, &result);
-  *pnread = (n < 0) ? 0 : (size_t)n;
-  return result;
+  return Curl_bufq_read(q, (unsigned char *)buf, len, pnread);
 }
 
 bool Curl_bufq_peek(struct bufq *q,
@@ -556,156 +465,139 @@ void Curl_bufq_skip(struct bufq *q, size_t amount)
   }
 }
 
-ssize_t Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer,
-                       void *writer_ctx, CURLcode *err)
+CURLcode Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer,
+                        void *writer_ctx, size_t *pwritten)
 {
   const unsigned char *buf;
   size_t blen;
-  ssize_t nwritten = 0;
+  CURLcode result = CURLE_OK;
 
+  *pwritten = 0;
   while(Curl_bufq_peek(q, &buf, &blen)) {
-    ssize_t chunk_written;
+    size_t chunk_written;
 
-    chunk_written = writer(writer_ctx, buf, blen, err);
-    if(chunk_written < 0) {
-      if(!nwritten || *err != CURLE_AGAIN) {
-        /* blocked on first write or real error, fail */
-        nwritten = -1;
+    result = writer(writer_ctx, buf, blen, &chunk_written);
+    if(result) {
+      if((result == CURLE_AGAIN) && *pwritten) {
+        /* blocked on subsequent write, report success */
+        result = CURLE_OK;
       }
       break;
     }
     if(!chunk_written) {
-      if(!nwritten) {
+      if(!*pwritten) {
         /* treat as blocked */
-        *err = CURLE_AGAIN;
-        nwritten = -1;
+        result = CURLE_AGAIN;
       }
       break;
     }
-    Curl_bufq_skip(q, (size_t)chunk_written);
-    nwritten += chunk_written;
+    *pwritten += chunk_written;
+    Curl_bufq_skip(q, chunk_written);
   }
-  return nwritten;
+  return result;
 }
 
-ssize_t Curl_bufq_write_pass(struct bufq *q,
-                             const unsigned char *buf, size_t len,
-                             Curl_bufq_writer *writer, void *writer_ctx,
-                             CURLcode *err)
+CURLcode Curl_bufq_write_pass(struct bufq *q,
+                              const unsigned char *buf, size_t len,
+                              Curl_bufq_writer *writer, void *writer_ctx,
+                              size_t *pwritten)
 {
-  ssize_t nwritten = 0, n;
+  CURLcode result = CURLE_OK;
+  size_t n;
 
-  *err = CURLE_OK;
+  *pwritten = 0;
   while(len) {
     if(Curl_bufq_is_full(q)) {
       /* try to make room in case we are full */
-      n = Curl_bufq_pass(q, writer, writer_ctx, err);
-      if(n < 0) {
-        if(*err != CURLE_AGAIN) {
+      result = Curl_bufq_pass(q, writer, writer_ctx, &n);
+      if(result) {
+        if(result != CURLE_AGAIN) {
           /* real error, fail */
-          return -1;
+          return result;
         }
         /* would block, bufq is full, give up */
         break;
       }
     }
 
-    /* Add whatever is remaining now to bufq */
-    n = Curl_bufq_write(q, buf, len, err);
-    if(n < 0) {
-      if(*err != CURLE_AGAIN) {
+    /* Add to bufq as much as there is room for */
+    result = Curl_bufq_write(q, buf, len, &n);
+    if(result) {
+      if(result != CURLE_AGAIN)
         /* real error, fail */
-        return -1;
-      }
-      /* no room in bufq */
-      break;
+        return result;
+      if((result == CURLE_AGAIN) && *pwritten)
+        /* we did write successfully before */
+        result = CURLE_OK;
+      return result;
     }
-    /* edge case of writer returning 0 (and len is >0)
-     * break or we might enter an infinite loop here */
-    if(n == 0)
+    else if(n == 0)
+      /* edge case of writer returning 0 (and len is >0)
+       * break or we might enter an infinite loop here */
       break;
 
-    /* Maybe only part of `data` has been added, continue to loop */
-    buf += (size_t)n;
-    len -= (size_t)n;
-    nwritten += (size_t)n;
+    /* Track what we added to bufq */
+    buf += n;
+    len -= n;
+    *pwritten += n;
   }
 
-  if(!nwritten && len) {
-    *err = CURLE_AGAIN;
-    return -1;
-  }
-  *err = CURLE_OK;
-  return nwritten;
+  return (!*pwritten && len) ? CURLE_AGAIN : CURLE_OK;
 }
 
-ssize_t Curl_bufq_sipn(struct bufq *q, size_t max_len,
-                       Curl_bufq_reader *reader, void *reader_ctx,
-                       CURLcode *err)
+CURLcode Curl_bufq_sipn(struct bufq *q, size_t max_len,
+                        Curl_bufq_reader *reader, void *reader_ctx,
+                        size_t *pnread)
 {
   struct buf_chunk *tail = NULL;
-  ssize_t nread;
 
-  *err = CURLE_AGAIN;
+  *pnread = 0;
   tail = get_non_full_tail(q);
   if(!tail) {
-    if(q->chunk_count < q->max_chunks) {
-      *err = CURLE_OUT_OF_MEMORY;
-      return -1;
-    }
+    if(q->chunk_count < q->max_chunks)
+      return CURLE_OUT_OF_MEMORY;
     /* full, blocked */
-    *err = CURLE_AGAIN;
-    return -1;
+    return CURLE_AGAIN;
   }
 
-  nread = chunk_slurpn(tail, max_len, reader, reader_ctx, err);
-  if(nread < 0) {
-    return -1;
-  }
-  else if(nread == 0) {
-    /* eof */
-    *err = CURLE_OK;
-  }
-  return nread;
+  return chunk_slurpn(tail, max_len, reader, reader_ctx, pnread);
 }
 
 /**
  * Read up to `max_len` bytes and append it to the end of the buffer queue.
  * if `max_len` is 0, no limit is imposed and the call behaves exactly
  * the same as `Curl_bufq_slurp()`.
- * Returns the total amount of buf read (may be 0) or -1 on other
- * reader errors.
- * Note that even in case of a -1 chunks may have been read and
+ * Returns the total amount of buf read (may be 0) in `pnread` or error
+ * Note that even in case of an error chunks may have been read and
  * the buffer queue will have different length than before.
  */
-static ssize_t bufq_slurpn(struct bufq *q, size_t max_len,
-                           Curl_bufq_reader *reader, void *reader_ctx,
-                           CURLcode *err)
+static CURLcode bufq_slurpn(struct bufq *q, size_t max_len,
+                            Curl_bufq_reader *reader, void *reader_ctx,
+                            size_t *pnread)
 {
-  ssize_t nread = 0, n;
+  CURLcode result;
 
-  *err = CURLE_AGAIN;
+  *pnread = 0;
   while(1) {
-
-    n = Curl_bufq_sipn(q, max_len, reader, reader_ctx, err);
-    if(n < 0) {
-      if(!nread || *err != CURLE_AGAIN) {
+    size_t n;
+    result = Curl_bufq_sipn(q, max_len, reader, reader_ctx, &n);
+    if(result) {
+      if(!*pnread || result != CURLE_AGAIN) {
         /* blocked on first read or real error, fail */
-        nread = -1;
+        return result;
       }
-      else
-        *err = CURLE_OK;
+      result = CURLE_OK;
       break;
     }
     else if(n == 0) {
       /* eof */
-      *err = CURLE_OK;
+      result = CURLE_OK;
       break;
     }
-    nread += (size_t)n;
+    *pnread += n;
     if(max_len) {
-      DEBUGASSERT((size_t)n <= max_len);
-      max_len -= (size_t)n;
+      DEBUGASSERT(n <= max_len);
+      max_len -= n;
       if(!max_len)
         break;
     }
@@ -713,11 +605,11 @@ static ssize_t bufq_slurpn(struct bufq *q, size_t max_len,
     if(q->tail && !chunk_is_full(q->tail))
       break;
   }
-  return nread;
+  return result;
 }
 
-ssize_t Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader,
-                        void *reader_ctx, CURLcode *err)
+CURLcode Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader,
+                         void *reader_ctx, size_t *pnread)
 {
-  return bufq_slurpn(q, 0, reader, reader_ctx, err);
+  return bufq_slurpn(q, 0, reader, reader_ctx, pnread);
 }

+ 28 - 38
lib/bufq.h

@@ -163,31 +163,22 @@ bool Curl_bufq_is_full(const struct bufq *q);
 /**
  * Write buf to the end of the buffer queue. The buf is copied
  * and the amount of copied bytes is returned.
- * A return code of -1 indicates an error, setting `err` to the
- * cause. An err of CURLE_AGAIN is returned if the buffer queue is full.
+ * CURLE_AGAIN is returned if the buffer queue is full.
  */
-ssize_t Curl_bufq_write(struct bufq *q,
-                        const unsigned char *buf, size_t len,
-                        CURLcode *err);
-
-CURLcode Curl_bufq_cwrite(struct bufq *q,
-                         const char *buf, size_t len,
+CURLcode Curl_bufq_write(struct bufq *q,
+                         const unsigned char *buf, size_t len,
                          size_t *pnwritten);
 
-/**
- * Remove `len` bytes from the end of the buffer queue again.
- * Returns CURLE_AGAIN if less than `len` bytes were in the queue.
- */
-CURLcode Curl_bufq_unwrite(struct bufq *q, size_t len);
+CURLcode Curl_bufq_cwrite(struct bufq *q,
+                          const char *buf, size_t len,
+                          size_t *pnwritten);
 
 /**
  * Read buf from the start of the buffer queue. The buf is copied
  * and the amount of copied bytes is returned.
- * A return code of -1 indicates an error, setting `err` to the
- * cause. An err of CURLE_AGAIN is returned if the buffer queue is empty.
  */
-ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
-                        CURLcode *err);
+CURLcode Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
+                        size_t *pnread);
 
 CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len,
                          size_t *pnread);
@@ -214,9 +205,9 @@ bool Curl_bufq_peek_at(struct bufq *q, size_t offset,
  */
 void Curl_bufq_skip(struct bufq *q, size_t amount);
 
-typedef ssize_t Curl_bufq_writer(void *writer_ctx,
-                                 const unsigned char *buf, size_t len,
-                                 CURLcode *err);
+typedef CURLcode Curl_bufq_writer(void *writer_ctx,
+                                  const unsigned char *buf, size_t len,
+                                  size_t *pwritten);
 /**
  * Passes the chunks in the buffer queue to the writer and returns
  * the amount of buf written. A writer may return -1 and CURLE_AGAIN
@@ -226,24 +217,23 @@ typedef ssize_t Curl_bufq_writer(void *writer_ctx,
  * Note that in case of a -1 chunks may have been written and
  * the buffer queue will have different length than before.
  */
-ssize_t Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer,
-                       void *writer_ctx, CURLcode *err);
+CURLcode Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer,
+                        void *writer_ctx, size_t *pwritten);
 
-typedef ssize_t Curl_bufq_reader(void *reader_ctx,
-                                 unsigned char *buf, size_t len,
-                                 CURLcode *err);
+typedef CURLcode Curl_bufq_reader(void *reader_ctx,
+                                  unsigned char *buf, size_t len,
+                                  size_t *pnread);
 
 /**
  * Read date and append it to the end of the buffer queue until the
  * reader returns blocking or the queue is full. A reader returns
- * -1 and CURLE_AGAIN to indicate blocking.
- * Returns the total amount of buf read (may be 0) or -1 on other
- * reader errors.
- * Note that in case of a -1 chunks may have been read and
+ * CURLE_AGAIN to indicate blocking.
+ * Returns the total amount of buf read (may be 0) in `pnread` on success.
+ * Note that in case of an error chunks may have been read and
  * the buffer queue will have different length than before.
  */
-ssize_t Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader,
-                        void *reader_ctx, CURLcode *err);
+CURLcode Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader,
+                         void *reader_ctx, size_t *pnread);
 
 /**
  * Read *once* up to `max_len` bytes and append it to the buffer.
@@ -251,9 +241,9 @@ ssize_t Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader,
  * Returns the total amount of buf read (may be 0) or -1 on other
  * reader errors.
  */
-ssize_t Curl_bufq_sipn(struct bufq *q, size_t max_len,
-                       Curl_bufq_reader *reader, void *reader_ctx,
-                       CURLcode *err);
+CURLcode Curl_bufq_sipn(struct bufq *q, size_t max_len,
+                        Curl_bufq_reader *reader, void *reader_ctx,
+                        size_t *pnread);
 
 /**
  * Write buf to the end of the buffer queue.
@@ -262,9 +252,9 @@ ssize_t Curl_bufq_sipn(struct bufq *q, size_t max_len,
  * on or is placed into the buffer, depending on `len` and current
  * amount buffered, chunk size, etc.
  */
-ssize_t Curl_bufq_write_pass(struct bufq *q,
-                             const unsigned char *buf, size_t len,
-                             Curl_bufq_writer *writer, void *writer_ctx,
-                             CURLcode *err);
+CURLcode Curl_bufq_write_pass(struct bufq *q,
+                              const unsigned char *buf, size_t len,
+                              Curl_bufq_writer *writer, void *writer_ctx,
+                              size_t *pwritten);
 
 #endif /* HEADER_CURL_BUFQ_H */

+ 11 - 16
lib/cf-h1-proxy.c

@@ -252,7 +252,7 @@ static CURLcode send_CONNECT(struct Curl_cfilter *cf,
   size_t request_len = curlx_dyn_len(&ts->request_data);
   size_t blen = request_len;
   CURLcode result = CURLE_OK;
-  ssize_t nwritten;
+  size_t nwritten;
 
   if(blen <= ts->nsent)
     goto out;  /* we are done */
@@ -260,16 +260,15 @@ static CURLcode send_CONNECT(struct Curl_cfilter *cf,
   blen -= ts->nsent;
   buf += ts->nsent;
 
-  nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, FALSE, &result);
-  if(nwritten < 0) {
-    if(result == CURLE_AGAIN) {
+  result = cf->next->cft->do_send(cf->next, data, buf, blen, FALSE, &nwritten);
+  if(result) {
+    if(result == CURLE_AGAIN)
       result = CURLE_OK;
-    }
     goto out;
   }
 
-  DEBUGASSERT(blen >= (size_t)nwritten);
-  ts->nsent += (size_t)nwritten;
+  DEBUGASSERT(blen >= nwritten);
+  ts->nsent += nwritten;
   Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)nwritten);
 
 out:
@@ -375,11 +374,8 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
   error = SELECT_OK;
   *done = FALSE;
 
-  if(!Curl_conn_data_pending(data, cf->sockindex))
-    return CURLE_OK;
-
   while(ts->keepon) {
-    ssize_t nread;
+    size_t nread;
     char byte;
 
     /* Read one byte at a time to avoid a race condition. Wait at most one
@@ -397,7 +393,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
       break;
     }
 
-    if(nread <= 0) {
+    if(!nread) {
       if(data->set.proxyauth && data->state.authproxy.avail &&
          data->state.aptr.proxyuserpwd) {
         /* proxy auth was requested and there was proxy auth available,
@@ -687,8 +683,8 @@ out:
 }
 
 static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
-                                        struct Curl_easy *data,
-                                        struct easy_pollset *ps)
+                                       struct Curl_easy *data,
+                                       struct easy_pollset *ps)
 {
   struct h1_tunnel_state *ts = cf->ctx;
 
@@ -741,7 +737,6 @@ struct Curl_cftype Curl_cft_h1_proxy = {
   cf_h1_proxy_connect,
   cf_h1_proxy_close,
   Curl_cf_def_shutdown,
-  Curl_cf_http_proxy_get_host,
   cf_h1_proxy_adjust_pollset,
   Curl_cf_def_data_pending,
   Curl_cf_def_send,
@@ -749,7 +744,7 @@ struct Curl_cftype Curl_cft_h1_proxy = {
   Curl_cf_def_cntrl,
   Curl_cf_def_conn_is_alive,
   Curl_cf_def_conn_keep_alive,
-  Curl_cf_def_query,
+  Curl_cf_http_proxy_query,
 };
 
 CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at,

+ 123 - 177
lib/cf-h2-proxy.c

@@ -28,6 +28,7 @@
 
 #include <nghttp2/nghttp2.h>
 #include "urldata.h"
+#include "url.h"
 #include "cfilters.h"
 #include "connect.h"
 #include "curl_trc.h"
@@ -80,7 +81,7 @@ struct tunnel_stream {
 };
 
 static CURLcode tunnel_stream_init(struct Curl_cfilter *cf,
-                                    struct tunnel_stream *ts)
+                                   struct tunnel_stream *ts)
 {
   const char *hostname;
   int port;
@@ -218,58 +219,28 @@ static void drain_tunnel(struct Curl_cfilter *cf,
                          struct tunnel_stream *tunnel)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
-  unsigned char bits;
-
   (void)cf;
-  bits = CURL_CSELECT_IN;
   if(!tunnel->closed && !tunnel->reset &&
      !Curl_bufq_is_empty(&ctx->tunnel.sendbuf))
-    bits |= CURL_CSELECT_OUT;
-  if(data->state.select_bits != bits) {
-    CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x",
-                tunnel->stream_id, bits);
-    data->state.select_bits = bits;
-    Curl_expire(data, 0, EXPIRE_RUN_NOW);
-  }
-}
-
-static ssize_t proxy_nw_in_reader(void *reader_ctx,
-                                  unsigned char *buf, size_t buflen,
-                                  CURLcode *err)
-{
-  struct Curl_cfilter *cf = reader_ctx;
-  ssize_t nread;
-
-  if(cf) {
-    struct Curl_easy *data = CF_DATA_CURRENT(cf);
-    nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err);
-    CURL_TRC_CF(data, cf, "[0] nw_in_reader(len=%zu) -> %zd, %d",
-                buflen, nread, *err);
-  }
-  else {
-    nread = 0;
-  }
-  return nread;
+    Curl_multi_mark_dirty(data);
 }
 
-static ssize_t proxy_h2_nw_out_writer(void *writer_ctx,
-                                      const unsigned char *buf, size_t buflen,
-                                      CURLcode *err)
+static CURLcode proxy_h2_nw_out_writer(void *writer_ctx,
+                                       const unsigned char *buf, size_t buflen,
+                                       size_t *pnwritten)
 {
   struct Curl_cfilter *cf = writer_ctx;
-  ssize_t nwritten;
-
+  *pnwritten = 0;
   if(cf) {
     struct Curl_easy *data = CF_DATA_CURRENT(cf);
-    nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen,
-                                 FALSE, err);
-    CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %zd, %d",
-                buflen, nwritten, *err);
-  }
-  else {
-    nwritten = 0;
+    CURLcode result;
+    result = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen,
+                               FALSE, pnwritten);
+    CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %d, %zu",
+                buflen, result, *pnwritten);
+    return result;
   }
-  return nwritten;
+  return CURLE_FAILED_INIT;
 }
 
 static int proxy_h2_client_new(struct Curl_cfilter *cf,
@@ -297,8 +268,8 @@ static int proxy_h2_client_new(struct Curl_cfilter *cf,
 }
 
 static ssize_t on_session_send(nghttp2_session *h2,
-                              const uint8_t *buf, size_t blen,
-                              int flags, void *userp);
+                               const uint8_t *buf, size_t blen,
+                               int flags, void *userp);
 static int proxy_h2_on_frame_recv(nghttp2_session *session,
                                   const nghttp2_frame *frame,
                                   void *userp);
@@ -414,16 +385,16 @@ static CURLcode proxy_h2_nw_out_flush(struct Curl_cfilter *cf,
                                       struct Curl_easy *data)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
-  ssize_t nwritten;
+  size_t nwritten;
   CURLcode result;
 
   (void)data;
   if(Curl_bufq_is_empty(&ctx->outbufq))
     return CURLE_OK;
 
-  nwritten = Curl_bufq_pass(&ctx->outbufq, proxy_h2_nw_out_writer, cf,
-                            &result);
-  if(nwritten < 0) {
+  result = Curl_bufq_pass(&ctx->outbufq, proxy_h2_nw_out_writer, cf,
+                          &nwritten);
+  if(result) {
     if(result == CURLE_AGAIN) {
       CURL_TRC_CF(data, cf, "[0] flush nw send buffer(%zu) -> EAGAIN",
                   Curl_bufq_len(&ctx->outbufq));
@@ -479,7 +450,7 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf,
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
-  ssize_t nread;
+  size_t nread;
 
   /* Process network input buffer fist */
   if(!Curl_bufq_is_empty(&ctx->inbufq)) {
@@ -496,10 +467,10 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf,
         Curl_bufq_is_empty(&ctx->inbufq) && /* and we consumed our input */
         !Curl_bufq_is_full(&ctx->tunnel.recvbuf)) {
 
-    nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result);
-    CURL_TRC_CF(data, cf, "[0] read %zu bytes nw data -> %zd, %d",
-                Curl_bufq_len(&ctx->inbufq), nread, result);
-    if(nread < 0) {
+    result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread);
+    CURL_TRC_CF(data, cf, "[0] read %zu bytes nw data -> %d, %zu",
+                Curl_bufq_len(&ctx->inbufq), result, nread);
+    if(result) {
       if(result != CURLE_AGAIN) {
         failf(data, "Failed receiving HTTP2 data");
         return result;
@@ -547,17 +518,18 @@ static ssize_t on_session_send(nghttp2_session *h2,
   struct Curl_cfilter *cf = userp;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
-  ssize_t nwritten;
+  size_t nwritten;
   CURLcode result = CURLE_OK;
 
   (void)h2;
   (void)flags;
   DEBUGASSERT(data);
 
-  nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen,
-                                  proxy_h2_nw_out_writer, cf, &result);
-  if(nwritten < 0) {
+  result = Curl_bufq_write_pass(&ctx->outbufq, buf, blen,
+                                proxy_h2_nw_out_writer, cf, &nwritten);
+  if(result) {
     if(result == CURLE_AGAIN) {
+      ctx->nw_out_blocked = 1;
       return NGHTTP2_ERR_WOULDBLOCK;
     }
     failf(data, "Failed sending HTTP2 data");
@@ -567,7 +539,8 @@ static ssize_t on_session_send(nghttp2_session *h2,
   if(!nwritten)
     return NGHTTP2_ERR_WOULDBLOCK;
 
-  return nwritten;
+  return (nwritten  > SSIZE_T_MAX) ?
+    NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nwritten;
 }
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
@@ -817,7 +790,7 @@ static ssize_t tunnel_send_callback(nghttp2_session *session,
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   struct tunnel_stream *ts;
   CURLcode result;
-  ssize_t nread;
+  size_t nread;
 
   (void)source;
   (void)data;
@@ -831,8 +804,8 @@ static ssize_t tunnel_send_callback(nghttp2_session *session,
     return NGHTTP2_ERR_CALLBACK_FAILURE;
   DEBUGASSERT(ts == &ctx->tunnel);
 
-  nread = Curl_bufq_read(&ts->sendbuf, buf, length, &result);
-  if(nread < 0) {
+  result = Curl_bufq_read(&ts->sendbuf, buf, length, &nread);
+  if(result) {
     if(result != CURLE_AGAIN)
       return NGHTTP2_ERR_CALLBACK_FAILURE;
     return NGHTTP2_ERR_DEFERRED;
@@ -842,7 +815,8 @@ 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;
+  return (nread  > SSIZE_T_MAX) ?
+    NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nread;
 }
 
 static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags,
@@ -851,7 +825,7 @@ static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags,
 {
   struct Curl_cfilter *cf = userp;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
-  ssize_t nwritten;
+  size_t nwritten;
   CURLcode result;
 
   (void)flags;
@@ -861,14 +835,15 @@ static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags,
   if(stream_id != ctx->tunnel.stream_id)
     return NGHTTP2_ERR_CALLBACK_FAILURE;
 
-  nwritten = Curl_bufq_write(&ctx->tunnel.recvbuf, mem, len, &result);
-  if(nwritten < 0) {
+  result = Curl_bufq_write(&ctx->tunnel.recvbuf, mem, len, &nwritten);
+  if(result) {
     if(result != CURLE_AGAIN)
       return NGHTTP2_ERR_CALLBACK_FAILURE;
 #ifdef DEBUGBUILD
     nwritten = 0;
 #endif
   }
+  /* tunnel.recbuf has soft limit, any success MUST add all data */
   DEBUGASSERT((size_t)nwritten == len);
   return 0;
 }
@@ -1277,212 +1252,179 @@ static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf,
   }
 }
 
-static ssize_t h2_handle_tunnel_close(struct Curl_cfilter *cf,
-                                      struct Curl_easy *data,
-                                      CURLcode *err)
+static CURLcode h2_handle_tunnel_close(struct Curl_cfilter *cf,
+                                       struct Curl_easy *data,
+                                       size_t *pnread)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
-  ssize_t rv = 0;
 
+  *pnread = 0;
   if(ctx->tunnel.error == NGHTTP2_REFUSED_STREAM) {
     CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new "
                 "connection", ctx->tunnel.stream_id);
     connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */
-    *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
-    return -1;
+    return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
   }
   else if(ctx->tunnel.error != NGHTTP2_NO_ERROR) {
     failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)",
           ctx->tunnel.stream_id, nghttp2_http2_strerror(ctx->tunnel.error),
           ctx->tunnel.error);
-    *err = CURLE_HTTP2_STREAM;
-    return -1;
+    return CURLE_HTTP2_STREAM;
   }
   else if(ctx->tunnel.reset) {
     failf(data, "HTTP/2 stream %u was reset", ctx->tunnel.stream_id);
-    *err = CURLE_RECV_ERROR;
-    return -1;
+    return CURLE_RECV_ERROR;
   }
 
-  *err = CURLE_OK;
-  rv = 0;
-  CURL_TRC_CF(data, cf, "[%d] handle_tunnel_close -> %zd, %d",
-              ctx->tunnel.stream_id, rv, *err);
-  return rv;
+  CURL_TRC_CF(data, cf, "[%d] handle_tunnel_close -> 0",
+              ctx->tunnel.stream_id);
+  return CURLE_OK;
 }
 
-static ssize_t tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
-                           char *buf, size_t len, CURLcode *err)
+static CURLcode tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+                            char *buf, size_t len, size_t *pnread)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
-  ssize_t nread = -1;
-
-  *err = CURLE_AGAIN;
-  if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) {
-    nread = Curl_bufq_read(&ctx->tunnel.recvbuf,
-                           (unsigned char *)buf, len, err);
-    if(nread < 0)
-      goto out;
-    DEBUGASSERT(nread > 0);
-  }
+  CURLcode result = CURLE_AGAIN;
 
-  if(nread < 0) {
+  *pnread = 0;
+  if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf))
+    result = Curl_bufq_cread(&ctx->tunnel.recvbuf, buf, len, pnread);
+  else {
     if(ctx->tunnel.closed) {
-      nread = h2_handle_tunnel_close(cf, data, err);
+      result = h2_handle_tunnel_close(cf, data, pnread);
     }
     else if(ctx->tunnel.reset ||
             (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) ||
             (ctx->rcvd_goaway &&
              ctx->last_stream_id < ctx->tunnel.stream_id)) {
-      *err = CURLE_RECV_ERROR;
-      nread = -1;
+      result = CURLE_RECV_ERROR;
     }
-  }
-  else if(nread == 0) {
-    *err = CURLE_AGAIN;
-    nread = -1;
+    else
+      result = CURLE_AGAIN;
   }
 
-out:
-  CURL_TRC_CF(data, cf, "[%d] tunnel_recv(len=%zu) -> %zd, %d",
-              ctx->tunnel.stream_id, len, nread, *err);
-  return nread;
+  CURL_TRC_CF(data, cf, "[%d] tunnel_recv(len=%zu) -> %d, %zu",
+              ctx->tunnel.stream_id, len, result, *pnread);
+  return result;
 }
 
-static ssize_t cf_h2_proxy_recv(struct Curl_cfilter *cf,
-                                struct Curl_easy *data,
-                                char *buf, size_t len, CURLcode *err)
+static CURLcode cf_h2_proxy_recv(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 char *buf, size_t len,
+                                 size_t *pnread)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
-  ssize_t nread = -1;
   struct cf_call_data save;
   CURLcode result;
 
+  *pnread = 0;
+  CF_DATA_SAVE(save, cf, data);
+
   if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) {
-    *err = CURLE_RECV_ERROR;
-    return -1;
+    result = CURLE_RECV_ERROR;
+    goto out;
   }
-  CF_DATA_SAVE(save, cf, data);
 
   if(Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) {
-    *err = proxy_h2_progress_ingress(cf, data);
-    if(*err)
+    result = proxy_h2_progress_ingress(cf, data);
+    if(result)
       goto out;
   }
 
-  nread = tunnel_recv(cf, data, buf, len, err);
+  result = tunnel_recv(cf, data, buf, len, pnread);
 
-  if(nread > 0) {
-    CURL_TRC_CF(data, cf, "[%d] increase window by %zd",
-                ctx->tunnel.stream_id, nread);
-    nghttp2_session_consume(ctx->h2, ctx->tunnel.stream_id, (size_t)nread);
+  if(!result) {
+    CURL_TRC_CF(data, cf, "[%d] increase window by %zu",
+                ctx->tunnel.stream_id, *pnread);
+    nghttp2_session_consume(ctx->h2, ctx->tunnel.stream_id, *pnread);
   }
 
-  result = proxy_h2_progress_egress(cf, data);
-  if(result && (result != CURLE_AGAIN)) {
-    *err = result;
-    nread = -1;
-  }
+  result = Curl_1st_fatal(result, proxy_h2_progress_egress(cf, data));
 
 out:
   if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) &&
-     (nread >= 0 || *err == CURLE_AGAIN)) {
+     (!result || (result == CURLE_AGAIN))) {
     /* data pending and no fatal error to report. Need to trigger
      * draining to avoid stalling when no socket events happen. */
     drain_tunnel(cf, data, &ctx->tunnel);
   }
-  CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d",
-              ctx->tunnel.stream_id, len, nread, *err);
+  CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %d, %zu",
+              ctx->tunnel.stream_id, len, result, *pnread);
   CF_DATA_RESTORE(cf, save);
-  return nread;
+  return result;
 }
 
-static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf,
-                                struct Curl_easy *data,
-                                const void *buf, size_t len, bool eos,
-                                CURLcode *err)
+static CURLcode cf_h2_proxy_send(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 const void *buf, size_t len, bool eos,
+                                 size_t *pnwritten)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   struct cf_call_data save;
   int rv;
-  ssize_t nwritten;
   CURLcode result;
 
   (void)eos;
+  *pnwritten = 0;
+  CF_DATA_SAVE(save, cf, data);
+
   if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) {
-    *err = CURLE_SEND_ERROR;
-    return -1;
+    result = CURLE_SEND_ERROR;
+    goto out;
   }
-  CF_DATA_SAVE(save, cf, data);
 
   if(ctx->tunnel.closed) {
-    nwritten = -1;
-    *err = CURLE_SEND_ERROR;
+    result = CURLE_SEND_ERROR;
     goto out;
   }
-  else {
-    nwritten = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, err);
-    if(nwritten < 0 && (*err != CURLE_AGAIN))
-      goto out;
-  }
+
+  result = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, pnwritten);
+  CURL_TRC_CF(data, cf, "cf_send(), bufq_write %d, %zd", result, *pnwritten);
+  if(result && (result != CURLE_AGAIN))
+    goto out;
 
   if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) {
     /* req body data is buffered, resume the potentially suspended stream */
     rv = nghttp2_session_resume_data(ctx->h2, ctx->tunnel.stream_id);
     if(nghttp2_is_fatal(rv)) {
-      *err = CURLE_SEND_ERROR;
-      nwritten = -1;
+      result = CURLE_SEND_ERROR;
       goto out;
     }
   }
 
-  result = proxy_h2_progress_ingress(cf, data);
-  if(result) {
-    *err = result;
-    nwritten = -1;
-    goto out;
-  }
-
-  /* Call the nghttp2 send loop and flush to write ALL buffered data,
-   * headers and/or request body completely out to the network */
-  result = proxy_h2_progress_egress(cf, data);
-  if(result && (result != CURLE_AGAIN)) {
-    *err = result;
-    nwritten = -1;
-    goto out;
-  }
+  result = Curl_1st_fatal(result, proxy_h2_progress_ingress(cf, data));
+  result = Curl_1st_fatal(result, proxy_h2_progress_egress(cf, data));
 
-  if(proxy_h2_should_close_session(ctx)) {
+  if(!result && proxy_h2_should_close_session(ctx)) {
     /* nghttp2 thinks this session is done. If the stream has not been
      * closed, this is an error state for out transfer */
     if(ctx->tunnel.closed) {
-      *err = CURLE_SEND_ERROR;
-      nwritten = -1;
+      result = CURLE_SEND_ERROR;
     }
     else {
       CURL_TRC_CF(data, cf, "[0] send: nothing to do in this session");
-      *err = CURLE_HTTP2;
-      nwritten = -1;
+      result = CURLE_HTTP2;
     }
   }
 
 out:
   if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) &&
-     (nwritten >= 0 || *err == CURLE_AGAIN)) {
+     (!result || (result == CURLE_AGAIN))) {
     /* data pending and no fatal error to report. Need to trigger
      * draining to avoid stalling when no socket events happen. */
     drain_tunnel(cf, data, &ctx->tunnel);
   }
-  CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, "
+  CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %d, %zu, "
               "h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)",
-              ctx->tunnel.stream_id, len, nwritten, *err,
+              ctx->tunnel.stream_id, len, result, *pnwritten,
               nghttp2_session_get_stream_remote_window_size(
                   ctx->h2, ctx->tunnel.stream_id),
               nghttp2_session_get_remote_window_size(ctx->h2),
               Curl_bufq_len(&ctx->tunnel.sendbuf),
               Curl_bufq_len(&ctx->outbufq));
   CF_DATA_RESTORE(cf, save);
-  return nwritten;
+  return result;
 }
 
 static CURLcode cf_h2_proxy_flush(struct Curl_cfilter *cf,
@@ -1533,11 +1475,11 @@ static bool proxy_h2_connisalive(struct Curl_cfilter *cf,
        not in use by any other transfer, there should not be any data here,
        only "protocol frames" */
     CURLcode result;
-    ssize_t nread = -1;
+    size_t nread;
 
     *input_pending = FALSE;
-    nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result);
-    if(nread != -1) {
+    result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread);
+    if(!result) {
       if(proxy_h2_process_pending_input(cf, data, &result) < 0)
         /* immediate error, considered dead */
         alive = FALSE;
@@ -1559,15 +1501,16 @@ static bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf,
                                  bool *input_pending)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
-  CURLcode result;
+  bool alive;
   struct cf_call_data save;
 
+  *input_pending = FALSE;
   CF_DATA_SAVE(save, cf, data);
-  result = (ctx && ctx->h2 && proxy_h2_connisalive(cf, data, input_pending));
+  alive = (ctx && ctx->h2 && proxy_h2_connisalive(cf, data, input_pending));
   CURL_TRC_CF(data, cf, "[0] conn alive -> %d, input_pending=%d",
-              result, *input_pending);
+              alive, *input_pending);
   CF_DATA_RESTORE(cf, save);
-  return result;
+  return alive;
 }
 
 static CURLcode cf_h2_proxy_query(struct Curl_cfilter *cf,
@@ -1577,6 +1520,10 @@ static CURLcode cf_h2_proxy_query(struct Curl_cfilter *cf,
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
 
   switch(query) {
+  case CF_QUERY_HOST_PORT:
+    *pres1 = (int)cf->conn->http_proxy.port;
+    *((const char **)pres2) = cf->conn->http_proxy.host.name;
+    return CURLE_OK;
   case CF_QUERY_NEED_FLUSH: {
     if(!Curl_bufq_is_empty(&ctx->outbufq) ||
        !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) {
@@ -1624,7 +1571,6 @@ struct Curl_cftype Curl_cft_h2_proxy = {
   cf_h2_proxy_connect,
   cf_h2_proxy_close,
   cf_h2_proxy_shutdown,
-  Curl_cf_http_proxy_get_host,
   cf_h2_proxy_adjust_pollset,
   cf_h2_proxy_data_pending,
   cf_h2_proxy_send,

+ 6 - 7
lib/cf-haproxy.c

@@ -131,17 +131,17 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
   case HAPROXY_SEND:
     len = curlx_dyn_len(&ctx->data_out);
     if(len > 0) {
-      ssize_t nwritten;
-      nwritten = Curl_conn_cf_send(cf->next, data,
-                                   curlx_dyn_ptr(&ctx->data_out), len, FALSE,
-                                   &result);
-      if(nwritten < 0) {
+      size_t nwritten;
+      result = Curl_conn_cf_send(cf->next, data,
+                                 curlx_dyn_ptr(&ctx->data_out), len, FALSE,
+                                 &nwritten);
+      if(result) {
         if(result != CURLE_AGAIN)
           goto out;
         result = CURLE_OK;
         nwritten = 0;
       }
-      curlx_dyn_tail(&ctx->data_out, len - (size_t)nwritten);
+      curlx_dyn_tail(&ctx->data_out, len - nwritten);
       if(curlx_dyn_len(&ctx->data_out) > 0) {
         result = CURLE_OK;
         goto out;
@@ -197,7 +197,6 @@ struct Curl_cftype Curl_cft_haproxy = {
   cf_haproxy_connect,
   cf_haproxy_close,
   Curl_cf_def_shutdown,
-  Curl_cf_def_get_host,
   cf_haproxy_adjust_pollset,
   Curl_cf_def_data_pending,
   Curl_cf_def_send,

+ 29 - 20
lib/cf-https-connect.c

@@ -55,6 +55,7 @@ struct cf_hc_baller {
   CURLcode result;
   struct curltime started;
   int reply_ms;
+  unsigned char transport;
   enum alpnid alpn_id;
   BIT(shutdown);
 };
@@ -117,17 +118,20 @@ struct cf_hc_ctx {
   CURLcode result;          /* overall result */
   struct cf_hc_baller ballers[2];
   size_t baller_count;
-  unsigned int soft_eyeballs_timeout_ms;
-  unsigned int hard_eyeballs_timeout_ms;
+  timediff_t soft_eyeballs_timeout_ms;
+  timediff_t hard_eyeballs_timeout_ms;
 };
 
 static void cf_hc_baller_assign(struct cf_hc_baller *b,
-                                enum alpnid alpn_id)
+                                enum alpnid alpn_id,
+                                unsigned char def_transport)
 {
   b->alpn_id = alpn_id;
+  b->transport = def_transport;
   switch(b->alpn_id) {
   case ALPN_h3:
     b->name = "h3";
+    b->transport = TRNSPRT_QUIC;
     break;
   case ALPN_h2:
     b->name = "h2";
@@ -218,6 +222,7 @@ static CURLcode baller_connected(struct Curl_cfilter *cf,
                 winner->name, (int)curlx_timediff(curlx_now(),
                                                   winner->started));
 
+  /* install the winning filter below this one. */
   cf->next = winner->cf;
   winner->cf = NULL;
 
@@ -268,15 +273,16 @@ static bool time_to_start_next(struct Curl_cfilter *cf,
   }
   elapsed_ms = curlx_timediff(now, ctx->started);
   if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) {
-    CURL_TRC_CF(data, cf, "hard timeout of %dms reached, starting %s",
+    CURL_TRC_CF(data, cf, "hard timeout of %" FMT_TIMEDIFF_T "ms reached, "
+                "starting %s",
                 ctx->hard_eyeballs_timeout_ms, ctx->ballers[idx].name);
     return TRUE;
   }
 
   if((idx > 0) && (elapsed_ms >= ctx->soft_eyeballs_timeout_ms)) {
     if(cf_hc_baller_reply_ms(&ctx->ballers[idx - 1], data) < 0) {
-      CURL_TRC_CF(data, cf, "soft timeout of %dms reached, %s has not "
-                  "seen any data, starting %s",
+      CURL_TRC_CF(data, cf, "soft timeout of %" FMT_TIMEDIFF_T "ms reached, "
+                  "%s has not seen any data, starting %s",
                   ctx->soft_eyeballs_timeout_ms,
                   ctx->ballers[idx - 1].name, ctx->ballers[idx].name);
       return TRUE;
@@ -311,11 +317,11 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
       DEBUGASSERT(!ctx->ballers[i].cf);
     CURL_TRC_CF(data, cf, "connect, init");
     ctx->started = now;
-    cf_hc_baller_init(&ctx->ballers[0], cf, data, cf->conn->transport);
+    cf_hc_baller_init(&ctx->ballers[0], cf, data, ctx->ballers[0].transport);
     if(ctx->baller_count > 1) {
       Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS);
-      CURL_TRC_CF(data, cf, "set next attempt to start in %ums",
-                  ctx->soft_eyeballs_timeout_ms);
+      CURL_TRC_CF(data, cf, "set next attempt to start in %" FMT_TIMEDIFF_T
+                  "ms", ctx->soft_eyeballs_timeout_ms);
     }
     ctx->state = CF_HC_CONNECT;
     FALLTHROUGH();
@@ -330,7 +336,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
     }
 
     if(time_to_start_next(cf, data, 1, now)) {
-      cf_hc_baller_init(&ctx->ballers[1], cf, data, cf->conn->transport);
+      cf_hc_baller_init(&ctx->ballers[1], cf, data, ctx->ballers[1].transport);
     }
 
     if((ctx->baller_count > 1) && cf_hc_baller_is_active(&ctx->ballers[1])) {
@@ -423,8 +429,8 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf,
 }
 
 static void cf_hc_adjust_pollset(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data,
-                                  struct easy_pollset *ps)
+                                 struct Curl_easy *data,
+                                 struct easy_pollset *ps)
 {
   if(!cf->connected) {
     struct cf_hc_ctx *ctx = cf->ctx;
@@ -561,7 +567,6 @@ struct Curl_cftype Curl_cft_http_connect = {
   cf_hc_connect,
   cf_hc_close,
   cf_hc_shutdown,
-  Curl_cf_def_get_host,
   cf_hc_adjust_pollset,
   cf_hc_data_pending,
   Curl_cf_def_send,
@@ -574,7 +579,8 @@ struct Curl_cftype Curl_cft_http_connect = {
 
 static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
                              struct Curl_easy *data,
-                             enum alpnid *alpnids, size_t alpn_count)
+                             enum alpnid *alpnids, size_t alpn_count,
+                             unsigned char def_transport)
 {
   struct Curl_cfilter *cf = NULL;
   struct cf_hc_ctx *ctx;
@@ -596,7 +602,7 @@ static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
     goto out;
   }
   for(i = 0; i < alpn_count; ++i)
-    cf_hc_baller_assign(&ctx->ballers[i], alpnids[i]);
+    cf_hc_baller_assign(&ctx->ballers[i], alpnids[i], def_transport);
   for(; i < CURL_ARRAYSIZE(ctx->ballers); ++i)
     ctx->ballers[i].alpn_id = ALPN_none;
   ctx->baller_count = alpn_count;
@@ -616,13 +622,14 @@ out:
 static CURLcode cf_http_connect_add(struct Curl_easy *data,
                                     struct connectdata *conn,
                                     int sockindex,
-                                    enum alpnid *alpn_ids, size_t alpn_count)
+                                    enum alpnid *alpn_ids, size_t alpn_count,
+                                    unsigned char def_transport)
 {
   struct Curl_cfilter *cf;
   CURLcode result = CURLE_OK;
 
   DEBUGASSERT(data);
-  result = cf_hc_create(&cf, data, alpn_ids, alpn_count);
+  result = cf_hc_create(&cf, data, alpn_ids, alpn_count, def_transport);
   if(result)
     goto out;
   Curl_conn_cf_add(data, conn, sockindex, cf);
@@ -679,7 +686,7 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data,
           continue;
         switch(alpn) {
         case ALPN_h3:
-          if(Curl_conn_may_http3(data, conn))
+          if(Curl_conn_may_http3(data, conn, conn->transport_wanted))
             break;  /* not possible */
           if(data->state.http_neg.allowed & CURL_HTTP_V3x) {
             CURL_TRC_CF(data, cf, "adding h3 via HTTPS-RR");
@@ -708,7 +715,7 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data,
     if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
        (data->state.http_neg.wanted & CURL_HTTP_V3x) &&
        !cf_https_alpns_contain(ALPN_h3, alpn_ids, alpn_count)) {
-      result = Curl_conn_may_http3(data, conn);
+      result = Curl_conn_may_http3(data, conn, conn->transport_wanted);
       if(!result) {
         CURL_TRC_CF(data, cf, "adding wanted h3");
         alpn_ids[alpn_count++] = ALPN_h3;
@@ -733,7 +740,9 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data,
   /* If we identified ALPNs to use, install our filter. Otherwise,
    * install nothing, so our call will use a default connect setup. */
   if(alpn_count) {
-    result = cf_http_connect_add(data, conn, sockindex, alpn_ids, alpn_count);
+    result = cf_http_connect_add(data, conn, sockindex,
+                                 alpn_ids, alpn_count,
+                                 conn->transport_wanted);
   }
 
 out:

+ 51 - 76
lib/cf-socket.c

@@ -73,7 +73,6 @@
 #include "url.h" /* for Curl_safefree() */
 #include "multiif.h"
 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
-#include "inet_ntop.h"
 #include "curlx/inet_pton.h"
 #include "progress.h"
 #include "curlx/warnless.h"
@@ -395,10 +394,10 @@ static CURLcode socket_open(struct Curl_easy *data,
  *
  */
 CURLcode Curl_socket_open(struct Curl_easy *data,
-                            const struct Curl_addrinfo *ai,
-                            struct Curl_sockaddr_ex *addr,
-                            int transport,
-                            curl_socket_t *sockfd)
+                          const struct Curl_addrinfo *ai,
+                          struct Curl_sockaddr_ex *addr,
+                          int transport,
+                          curl_socket_t *sockfd)
 {
   struct Curl_sockaddr_ex dummy;
   CURLcode result;
@@ -996,8 +995,6 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
       cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
     socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
     ctx->sock = CURL_SOCKET_BAD;
-    if(ctx->active && cf->sockindex == FIRSTSOCKET)
-      cf->conn->remote_addr = NULL;
     ctx->active = FALSE;
     memset(&ctx->started_at, 0, sizeof(ctx->started_at));
     memset(&ctx->connected_at, 0, sizeof(ctx->connected_at));
@@ -1095,7 +1092,7 @@ static CURLcode set_remote_ip(struct Curl_cfilter *cf,
 }
 
 static CURLcode cf_socket_open(struct Curl_cfilter *cf,
-                              struct Curl_easy *data)
+                               struct Curl_easy *data)
 {
   struct cf_socket_ctx *ctx = cf->ctx;
   int error = 0;
@@ -1378,22 +1375,9 @@ out:
   return result;
 }
 
-static void cf_socket_get_host(struct Curl_cfilter *cf,
-                               struct Curl_easy *data,
-                               const char **phost,
-                               const char **pdisplay_host,
-                               int *pport)
-{
-  struct cf_socket_ctx *ctx = cf->ctx;
-  (void)data;
-  *phost = cf->conn->host.name;
-  *pdisplay_host = cf->conn->host.dispname;
-  *pport = ctx->ip.remote_port;
-}
-
 static void cf_socket_adjust_pollset(struct Curl_cfilter *cf,
-                                      struct Curl_easy *data,
-                                      struct easy_pollset *ps)
+                                     struct Curl_easy *data,
+                                     struct easy_pollset *ps)
 {
   struct cf_socket_ctx *ctx = cf->ctx;
 
@@ -1420,17 +1404,6 @@ static void cf_socket_adjust_pollset(struct Curl_cfilter *cf,
   }
 }
 
-static bool cf_socket_data_pending(struct Curl_cfilter *cf,
-                                   const struct Curl_easy *data)
-{
-  struct cf_socket_ctx *ctx = cf->ctx;
-  int readable;
-
-  (void)data;
-  readable = SOCKET_READABLE(ctx->sock, 0);
-  return readable > 0 && (readable & CURL_CSELECT_IN);
-}
-
 #ifdef USE_WINSOCK
 
 #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
@@ -1457,17 +1430,18 @@ static void win_update_sndbuf_size(struct cf_socket_ctx *ctx)
 
 #endif /* USE_WINSOCK */
 
-static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
-                              const void *buf, size_t len, bool eos,
-                              CURLcode *err)
+static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+                               const void *buf, size_t len, bool eos,
+                               size_t *pnwritten)
 {
   struct cf_socket_ctx *ctx = cf->ctx;
   curl_socket_t fdsave;
   ssize_t nwritten;
   size_t orig_len = len;
+  CURLcode result = CURLE_OK;
 
   (void)eos; /* unused */
-  *err = CURLE_OK;
+  *pnwritten = 0;
   fdsave = cf->conn->sock[cf->sockindex];
   cf->conn->sock[cf->sockindex] = ctx->sock;
 
@@ -1478,10 +1452,8 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
     Curl_rand_bytes(data, FALSE, &c, 1);
     if(c >= ((100-ctx->wblock_percent)*256/100)) {
       CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len);
-      *err = CURLE_AGAIN;
-      nwritten = -1;
       cf->conn->sock[cf->sockindex] = fdsave;
-      return nwritten;
+      return CURLE_AGAIN;
     }
   }
   if(cf->cft != &Curl_cft_udp && ctx->wpartial_percent > 0 && len > 8) {
@@ -1496,15 +1468,14 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
 #if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
   if(cf->conn->bits.tcp_fastopen) {
     nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN,
-                      &cf->conn->remote_addr->curl_sa_addr,
-                      cf->conn->remote_addr->addrlen);
+                      &ctx->addr.curl_sa_addr, ctx->addr.addrlen);
     cf->conn->bits.tcp_fastopen = FALSE;
   }
   else
 #endif
     nwritten = swrite(ctx->sock, buf, len);
 
-  if(-1 == nwritten) {
+  if(nwritten < 0) {
     int sockerr = SOCKERRNO;
 
     if(
@@ -1521,36 +1492,38 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
 #endif
       ) {
       /* this is just a case of EWOULDBLOCK */
-      *err = CURLE_AGAIN;
+      result = CURLE_AGAIN;
     }
     else {
       char buffer[STRERROR_LEN];
       failf(data, "Send failure: %s",
             Curl_strerror(sockerr, buffer, sizeof(buffer)));
       data->state.os_errno = sockerr;
-      *err = CURLE_SEND_ERROR;
+      result = CURLE_SEND_ERROR;
     }
   }
+  else
+    *pnwritten = (size_t)nwritten;
 
 #if defined(USE_WINSOCK)
-  if(!*err)
+  if(!result)
     win_update_sndbuf_size(ctx);
 #endif
 
-  CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, err=%d",
-              orig_len, (int)nwritten, *err);
+  CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, %zu",
+              orig_len, result, *pnwritten);
   cf->conn->sock[cf->sockindex] = fdsave;
-  return nwritten;
+  return result;
 }
 
-static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
-                              char *buf, size_t len, CURLcode *err)
+static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+                               char *buf, size_t len, size_t *pnread)
 {
   struct cf_socket_ctx *ctx = cf->ctx;
+  CURLcode result = CURLE_OK;
   ssize_t nread;
 
-  *err = CURLE_OK;
-
+  *pnread = 0;
 #ifdef DEBUGBUILD
   /* simulate network blocking/partial reads */
   if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) {
@@ -1558,8 +1531,7 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
     Curl_rand(data, &c, 1);
     if(c >= ((100-ctx->rblock_percent)*256/100)) {
       CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len);
-      *err = CURLE_AGAIN;
-      return -1;
+      return CURLE_AGAIN;
     }
   }
   if(cf->cft != &Curl_cft_udp && ctx->recv_max && ctx->recv_max < len) {
@@ -1570,10 +1542,9 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
   }
 #endif
 
-  *err = CURLE_OK;
   nread = sread(ctx->sock, buf, len);
 
-  if(-1 == nread) {
+  if(nread < 0) {
     int sockerr = SOCKERRNO;
 
     if(
@@ -1589,25 +1560,25 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
 #endif
       ) {
       /* this is just a case of EWOULDBLOCK */
-      *err = CURLE_AGAIN;
+      result = CURLE_AGAIN;
     }
     else {
       char buffer[STRERROR_LEN];
-
       failf(data, "Recv failure: %s",
             Curl_strerror(sockerr, buffer, sizeof(buffer)));
       data->state.os_errno = sockerr;
-      *err = CURLE_RECV_ERROR;
+      result = CURLE_RECV_ERROR;
     }
   }
+  else
+    *pnread = (size_t)nread;
 
-  CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
-              *err);
-  if(nread > 0 && !ctx->got_first_byte) {
+  CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, %zu", len, result, *pnread);
+  if(!result && !ctx->got_first_byte) {
     ctx->first_byte_at = curlx_now();
     ctx->got_first_byte = TRUE;
   }
-  return nread;
+  return result;
 }
 
 static void cf_socket_update_data(struct Curl_cfilter *cf,
@@ -1631,7 +1602,6 @@ static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
   set_local_ip(cf, data);
   if(cf->sockindex == FIRSTSOCKET) {
     cf->conn->primary = ctx->ip;
-    cf->conn->remote_addr = &ctx->addr;
   #ifdef USE_IPV6
     cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6);
   #endif
@@ -1713,6 +1683,15 @@ static CURLcode cf_socket_query(struct Curl_cfilter *cf,
     DEBUGASSERT(pres2);
     *((curl_socket_t *)pres2) = ctx->sock;
     return CURLE_OK;
+  case CF_QUERY_TRANSPORT:
+    DEBUGASSERT(pres1);
+    *pres1 = ctx->transport;
+    return CURLE_OK;
+  case CF_QUERY_REMOTE_ADDR:
+    DEBUGASSERT(pres2);
+    *((const struct Curl_sockaddr_ex **)pres2) = cf->connected ?
+                                                 &ctx->addr : NULL;
+    return CURLE_OK;
   case CF_QUERY_CONNECT_REPLY_MS:
     if(ctx->got_first_byte) {
       timediff_t ms = curlx_timediff(ctx->first_byte_at, ctx->started_at);
@@ -1763,9 +1742,8 @@ struct Curl_cftype Curl_cft_tcp = {
   cf_tcp_connect,
   cf_socket_close,
   cf_socket_shutdown,
-  cf_socket_get_host,
   cf_socket_adjust_pollset,
-  cf_socket_data_pending,
+  Curl_cf_def_data_pending,
   cf_socket_send,
   cf_socket_recv,
   cf_socket_cntrl,
@@ -1918,9 +1896,8 @@ struct Curl_cftype Curl_cft_udp = {
   cf_udp_connect,
   cf_socket_close,
   cf_socket_shutdown,
-  cf_socket_get_host,
   cf_socket_adjust_pollset,
-  cf_socket_data_pending,
+  Curl_cf_def_data_pending,
   cf_socket_send,
   cf_socket_recv,
   cf_socket_cntrl,
@@ -1973,9 +1950,8 @@ struct Curl_cftype Curl_cft_unix = {
   cf_tcp_connect,
   cf_socket_close,
   cf_socket_shutdown,
-  cf_socket_get_host,
   cf_socket_adjust_pollset,
-  cf_socket_data_pending,
+  Curl_cf_def_data_pending,
   cf_socket_send,
   cf_socket_recv,
   cf_socket_cntrl,
@@ -2020,7 +1996,7 @@ out:
 }
 
 static timediff_t cf_tcp_accept_timeleft(struct Curl_cfilter *cf,
-                                          struct Curl_easy *data)
+                                         struct Curl_easy *data)
 {
   struct cf_socket_ctx *ctx = cf->ctx;
   timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
@@ -2194,9 +2170,8 @@ struct Curl_cftype Curl_cft_tcp_accept = {
   cf_tcp_accept_connect,
   cf_socket_close,
   cf_socket_shutdown,
-  cf_socket_get_host,
   cf_socket_adjust_pollset,
-  cf_socket_data_pending,
+  Curl_cf_def_data_pending,
   cf_socket_send,
   cf_socket_recv,
   cf_socket_cntrl,
@@ -2222,7 +2197,7 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
     result = CURLE_OUT_OF_MEMORY;
     goto out;
   }
-  ctx->transport = conn->transport;
+  ctx->transport = TRNSPRT_TCP;
   ctx->sock = *s;
   ctx->listening = TRUE;
   ctx->accepted = FALSE;

+ 4 - 4
lib/cf-socket.h

@@ -68,10 +68,10 @@ CURLcode Curl_parse_interface(const char *input,
  *
  */
 CURLcode Curl_socket_open(struct Curl_easy *data,
-                            const struct Curl_addrinfo *ai,
-                            struct Curl_sockaddr_ex *addr,
-                            int transport,
-                            curl_socket_t *sockfd);
+                          const struct Curl_addrinfo *ai,
+                          struct Curl_sockaddr_ex *addr,
+                          int transport,
+                          curl_socket_t *sockfd);
 
 int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
                       curl_socket_t sock);

+ 194 - 99
lib/cfilters.c

@@ -67,22 +67,9 @@ 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_get_host(struct Curl_cfilter *cf, struct Curl_easy *data,
-                          const char **phost, const char **pdisplay_host,
-                          int *pport)
-{
-  if(cf->next)
-    cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
-  else {
-    *phost = cf->conn->host.name;
-    *pdisplay_host = cf->conn->host.dispname;
-    *pport = cf->conn->primary.remote_port;
-  }
-}
-
 void Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data,
-                                 struct easy_pollset *ps)
+                                struct Curl_easy *data,
+                                struct easy_pollset *ps)
 {
   /* NOP */
   (void)cf;
@@ -97,21 +84,23 @@ bool Curl_cf_def_data_pending(struct Curl_cfilter *cf,
     cf->next->cft->has_data_pending(cf->next, data) : FALSE;
 }
 
-ssize_t  Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+CURLcode Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                           const void *buf, size_t len, bool eos,
-                          CURLcode *err)
+                          size_t *pnwritten)
 {
-  return cf->next ?
-    cf->next->cft->do_send(cf->next, data, buf, len, eos, err) :
-    CURLE_RECV_ERROR;
+  if(cf->next)
+    return cf->next->cft->do_send(cf->next, data, buf, len, eos, pnwritten);
+  *pnwritten = 0;
+  return CURLE_RECV_ERROR;
 }
 
-ssize_t  Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
-                          char *buf, size_t len, CURLcode *err)
+CURLcode Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+                          char *buf, size_t len, size_t *pnread)
 {
-  return cf->next ?
-    cf->next->cft->do_recv(cf->next, data, buf, len, err) :
-    CURLE_SEND_ERROR;
+  if(cf->next)
+    return cf->next->cft->do_recv(cf->next, data, buf, len, pnread);
+  *pnread = 0;
+  return CURLE_SEND_ERROR;
 }
 
 bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf,
@@ -234,52 +223,102 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done)
   return result;
 }
 
-ssize_t Curl_cf_recv(struct Curl_easy *data, int num, char *buf,
-                     size_t len, CURLcode *code)
+CURLcode Curl_cf_recv(struct Curl_easy *data, int num, char *buf,
+                      size_t len, size_t *pnread)
 {
   struct Curl_cfilter *cf;
 
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
-  *code = CURLE_OK;
   cf = data->conn->cfilter[num];
-  while(cf && !cf->connected) {
+  while(cf && !cf->connected)
     cf = cf->next;
-  }
-  if(cf) {
-    ssize_t nread = cf->cft->do_recv(cf, data, buf, len, code);
-    DEBUGASSERT(nread >= 0 || *code);
-    DEBUGASSERT(nread < 0 || !*code);
-    return nread;
-  }
+  if(cf)
+    return cf->cft->do_recv(cf, data, buf, len, pnread);
   failf(data, "recv: no filter connected");
-  *code = CURLE_FAILED_INIT;
-  return -1;
+  DEBUGASSERT(0);
+  *pnread = 0;
+  return CURLE_FAILED_INIT;
 }
 
-ssize_t Curl_cf_send(struct Curl_easy *data, int num,
-                     const void *mem, size_t len, bool eos,
-                     CURLcode *code)
+CURLcode Curl_cf_send(struct Curl_easy *data, int num,
+                      const void *mem, size_t len, bool eos,
+                      size_t *pnwritten)
 {
   struct Curl_cfilter *cf;
 
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
-  *code = CURLE_OK;
   cf = data->conn->cfilter[num];
-  while(cf && !cf->connected) {
+  while(cf && !cf->connected)
     cf = cf->next;
-  }
   if(cf) {
-    ssize_t nwritten = cf->cft->do_send(cf, data, mem, len, eos, code);
-    DEBUGASSERT(nwritten >= 0 || *code);
-    DEBUGASSERT(nwritten < 0 || !*code || !len);
-    return nwritten;
+    return cf->cft->do_send(cf, data, mem, len, eos, pnwritten);
   }
   failf(data, "send: no filter connected");
   DEBUGASSERT(0);
-  *code = CURLE_FAILED_INIT;
-  return -1;
+  *pnwritten = 0;
+  return CURLE_FAILED_INIT;
+}
+
+struct cf_io_ctx {
+  struct Curl_easy *data;
+  struct Curl_cfilter *cf;
+};
+
+static CURLcode cf_bufq_reader(void *writer_ctx,
+                               unsigned char *buf, size_t blen,
+                               size_t *pnread)
+{
+  struct cf_io_ctx *io = writer_ctx;
+  return Curl_conn_cf_recv(io->cf, io->data, (char *)buf, blen, pnread);
+}
+
+CURLcode Curl_cf_recv_bufq(struct Curl_cfilter *cf,
+                           struct Curl_easy *data,
+                           struct bufq *bufq,
+                           size_t maxlen,
+                           size_t *pnread)
+{
+  struct cf_io_ctx io;
+
+  if(!cf || !data) {
+    *pnread = 0;
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+  io.data = data;
+  io.cf = cf;
+  return Curl_bufq_sipn(bufq, maxlen, cf_bufq_reader, &io, pnread);
+}
+
+static CURLcode cf_bufq_writer(void *writer_ctx,
+                               const unsigned char *buf, size_t buflen,
+                               size_t *pnwritten)
+{
+  struct cf_io_ctx *io = writer_ctx;
+  return Curl_conn_cf_send(io->cf, io->data, (const char *)buf,
+                           buflen, FALSE, pnwritten);
+}
+
+CURLcode Curl_cf_send_bufq(struct Curl_cfilter *cf,
+                           struct Curl_easy *data,
+                           struct bufq *bufq,
+                           const unsigned char *buf, size_t blen,
+                           size_t *pnwritten)
+{
+  struct cf_io_ctx io;
+
+  if(!cf || !data) {
+    *pnwritten = 0;
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+  }
+  io.data = data;
+  io.cf = cf;
+  if(buf && blen)
+    return Curl_bufq_write_pass(bufq, buf, blen, cf_bufq_writer, &io,
+                                pnwritten);
+  else
+    return Curl_bufq_pass(bufq, cf_bufq_writer, &io, pnwritten);
 }
 
 CURLcode Curl_cf_create(struct Curl_cfilter **pcf,
@@ -381,23 +420,23 @@ void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
     cf->cft->do_close(cf, data);
 }
 
-ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
-                          const void *buf, size_t len, bool eos,
-                          CURLcode *err)
+CURLcode Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+                           const void *buf, size_t len, bool eos,
+                           size_t *pnwritten)
 {
   if(cf)
-    return cf->cft->do_send(cf, data, buf, len, eos, err);
-  *err = CURLE_SEND_ERROR;
-  return -1;
+    return cf->cft->do_send(cf, data, buf, len, eos, pnwritten);
+  *pnwritten = 0;
+  return CURLE_SEND_ERROR;
 }
 
-ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
-                          char *buf, size_t len, CURLcode *err)
+CURLcode Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+                           char *buf, size_t len, size_t *pnread)
 {
   if(cf)
-    return cf->cft->do_recv(cf, data, buf, len, err);
-  *err = CURLE_RECV_ERROR;
-  return -1;
+    return cf->cft->do_recv(cf, data, buf, len, pnread);
+  *pnread = 0;
+  return CURLE_RECV_ERROR;
 }
 
 CURLcode Curl_conn_connect(struct Curl_easy *data,
@@ -541,6 +580,19 @@ bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex)
   return conn ? Curl_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(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,
+                               NULL, (void *)info) : CURLE_UNKNOWN_OPTION;
+    return !result;
+  }
+  return FALSE;
+}
+
 bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex)
 {
   struct Curl_cfilter *cf = conn ? conn->cfilter[sockindex] : NULL;
@@ -554,6 +606,13 @@ bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex)
   return FALSE;
 }
 
+unsigned char Curl_conn_get_transport(struct Curl_easy *data,
+                                      struct connectdata *conn)
+{
+  struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET];
+  return Curl_conn_cf_get_transport(cf, data);
+}
+
 unsigned char Curl_conn_http_version(struct Curl_easy *data,
                                      struct connectdata *conn)
 {
@@ -678,31 +737,35 @@ int Curl_conn_cf_poll(struct Curl_cfilter *cf,
   return Curl_poll(pfds, npfds, timeout_ms);
 }
 
-void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
-                        const char **phost, const char **pdisplay_host,
-                        int *pport)
+void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex,
+                                const char **phost, int *pport)
 {
-  struct Curl_cfilter *cf;
+  struct Curl_cfilter *cf, *cf_proxy = NULL;
 
   DEBUGASSERT(data->conn);
   cf = data->conn->cfilter[sockindex];
-  if(cf) {
-    cf->cft->get_host(cf, data, phost, pdisplay_host, pport);
+  /* 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)) ==
+       (CF_TYPE_IP_CONNECT|CF_TYPE_PROXY))
+       cf_proxy = cf;
+    cf = cf->next;
   }
-  else {
-    /* Some filter ask during shutdown for this, mainly for debugging
-     * purposes. We hand out the defaults, however this is not always
-     * accurate, as the connection might be tunneled, etc. But all that
-     * state is already gone here. */
+  /* cf_proxy (!= NULL) is not connected yet. It is talking
+   * to an interim host and any authentication or other things apply
+   * to this interim host and port. */
+  if(!cf_proxy || cf_proxy->cft->query(cf_proxy, data, CF_QUERY_HOST_PORT,
+                                       pport, CURL_UNCONST(phost))) {
+    /* Everything connected or query unsuccessful, the overall
+     * connection's destination is the answer */
     *phost = data->conn->host.name;
-    *pdisplay_host = data->conn->host.dispname;
     *pport = data->conn->remote_port;
   }
 }
 
 CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf,
-                                struct Curl_easy *data,
-                                int event, int arg1, void *arg2)
+                           struct Curl_easy *data,
+                           int event, int arg1, void *arg2)
 {
   (void)cf;
   (void)data;
@@ -738,6 +801,26 @@ curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf,
   return CURL_SOCKET_BAD;
 }
 
+unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf,
+                                         struct Curl_easy *data)
+{
+  int transport = 0;
+  if(cf && !cf->cft->query(cf, data, CF_QUERY_TRANSPORT, &transport, NULL))
+    return (unsigned char)transport;
+  return (unsigned char)(data->conn ? data->conn->transport_wanted : 0);
+}
+
+static const struct Curl_sockaddr_ex *
+cf_get_remote_addr(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+  const struct Curl_sockaddr_ex *remote_addr = NULL;
+  if(cf &&
+     !cf->cft->query(cf, data, CF_QUERY_REMOTE_ADDR, NULL,
+                     CURL_UNCONST(&remote_addr)))
+    return remote_addr;
+  return NULL;
+}
+
 CURLcode Curl_conn_cf_get_ip_info(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
                                   int *is_ipv6, struct ip_quadruple *ipquad)
@@ -760,6 +843,13 @@ curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex)
   return data->conn ? data->conn->sock[sockindex] : CURL_SOCKET_BAD;
 }
 
+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;
+  return cf ? cf_get_remote_addr(cf, data) : NULL;
+}
+
 void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex)
 {
   if(data->conn) {
@@ -878,8 +968,8 @@ CURLcode Curl_conn_keep_alive(struct Curl_easy *data,
 }
 
 size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
-                                     struct connectdata *conn,
-                                     int sockindex)
+                                    struct connectdata *conn,
+                                    int sockindex)
 {
   CURLcode result;
   int n = 0;
@@ -912,17 +1002,14 @@ int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd)
 }
 
 CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex,
-                        char *buf, size_t blen, ssize_t *n)
+                        char *buf, size_t blen, size_t *pnread)
 {
-  CURLcode result = CURLE_OK;
-  ssize_t nread;
-
+  DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
-  nread = data->conn->recv[sockindex](data, sockindex, buf, blen, &result);
-  DEBUGASSERT(nread >= 0 || result);
-  DEBUGASSERT(nread < 0 || !result);
-  *n = (nread >= 0) ? (size_t)nread : 0;
-  return result;
+  if(data && data->conn && data->conn->recv[sockindex])
+    return data->conn->recv[sockindex](data, sockindex, buf, blen, pnread);
+  *pnread = 0;
+  return CURLE_FAILED_INIT;
 }
 
 CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
@@ -930,15 +1017,10 @@ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
                         size_t *pnwritten)
 {
   size_t write_len = blen;
-  ssize_t nwritten;
-  CURLcode result = CURLE_OK;
-  struct connectdata *conn;
 
-  DEBUGASSERT(sockindex >= 0 && sockindex < 2);
-  DEBUGASSERT(pnwritten);
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
-  conn = data->conn;
+  DEBUGASSERT(sockindex >= 0 && sockindex < 2);
 #ifdef DEBUGBUILD
   if(write_len) {
     /* Allow debug builds to override this logic to force short sends
@@ -953,11 +1035,11 @@ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
 #endif
   if(write_len != blen)
     eos = FALSE;
-  nwritten = conn->send[sockindex](data, sockindex, buf, write_len, eos,
-                                   &result);
-  DEBUGASSERT((nwritten >= 0) || result);
-  *pnwritten = (nwritten < 0) ? 0 : (size_t)nwritten;
-  return result;
+  if(data && data->conn && data->conn->send[sockindex])
+    return data->conn->send[sockindex](data, sockindex, buf, write_len, eos,
+                                       pnwritten);
+  *pnwritten = 0;
+  return CURLE_FAILED_INIT;
 }
 
 void Curl_pollset_reset(struct Curl_easy *data,
@@ -974,8 +1056,8 @@ void Curl_pollset_reset(struct Curl_easy *data,
  *
  */
 void Curl_pollset_change(struct Curl_easy *data,
-                       struct easy_pollset *ps, curl_socket_t sock,
-                       int add_flags, int remove_flags)
+                         struct easy_pollset *ps, curl_socket_t sock,
+                         int add_flags, int remove_flags)
 {
   unsigned int i;
 
@@ -1085,3 +1167,16 @@ void Curl_pollset_check(struct Curl_easy *data,
   }
   *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;
+}

+ 98 - 47
lib/cfilters.h

@@ -26,11 +26,13 @@
 
 #include "curlx/timediff.h"
 
+struct bufq;
 struct Curl_cfilter;
 struct Curl_easy;
 struct Curl_dns_entry;
 struct connectdata;
 struct ip_quadruple;
+struct curl_tlssessioninfo;
 
 /* Callback to destroy resources held by this filter instance.
  * Implementations MUST NOT chain calls to cf->next.
@@ -53,23 +55,6 @@ typedef CURLcode Curl_cft_connect(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
                                   bool *done);
 
-/* Return the hostname and port the connection goes to.
- * This may change with the connection state of filters when tunneling
- * is involved.
- * @param cf     the filter to ask
- * @param data   the easy handle currently active
- * @param phost  on return, points to the relevant, real hostname.
- *               this is owned by the connection.
- * @param pdisplay_host  on return, points to the printable hostname.
- *               this is owned by the connection.
- * @param pport  on return, contains the port number
- */
-typedef void     Curl_cft_get_host(struct Curl_cfilter *cf,
-                                   struct Curl_easy *data,
-                                   const char **phost,
-                                   const char **pdisplay_host,
-                                   int *pport);
-
 struct easy_pollset;
 
 /* Passing in an easy_pollset for monitoring of sockets, let
@@ -102,18 +87,18 @@ typedef void     Curl_cft_adjust_pollset(struct Curl_cfilter *cf,
 typedef bool     Curl_cft_data_pending(struct Curl_cfilter *cf,
                                        const struct Curl_easy *data);
 
-typedef ssize_t  Curl_cft_send(struct Curl_cfilter *cf,
+typedef CURLcode Curl_cft_send(struct Curl_cfilter *cf,
                                struct Curl_easy *data, /* transfer */
                                const void *buf,        /* data to write */
                                size_t len,             /* amount to write */
                                bool eos,               /* last chunk */
-                               CURLcode *err);         /* error to return */
+                               size_t *pnwritten);     /* how much sent */
 
-typedef ssize_t  Curl_cft_recv(struct Curl_cfilter *cf,
+typedef CURLcode Curl_cft_recv(struct Curl_cfilter *cf,
                                struct Curl_easy *data, /* transfer */
                                char *buf,              /* store data here */
                                size_t len,             /* amount to read */
-                               CURLcode *err);         /* error to return */
+                               size_t *pnread);        /* how much received */
 
 typedef bool     Curl_cft_conn_is_alive(struct Curl_cfilter *cf,
                                         struct Curl_easy *data,
@@ -166,6 +151,13 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
  * - CF_QUERY_NEED_FLUSH: TRUE iff any of the filters have unsent data
  * - CF_QUERY_IP_INFO: res1 says if connection used IPv6, res2 is the
  *                   ip quadruple
+ * - CF_QUERY_HOST_PORT: the remote hostname and port a filter talks to
+ * - CF_QUERY_SSL_INFO: fill out the passed curl_tlssessioninfo with the
+ *                      internal from the SSL secured connection when
+ *                      available.
+ * - 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.
  */
 /*      query                             res1       res2     */
 #define CF_QUERY_MAX_CONCURRENT     1  /* number     -        */
@@ -177,6 +169,13 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
 #define CF_QUERY_NEED_FLUSH         7  /* TRUE/FALSE - */
 #define CF_QUERY_IP_INFO            8  /* TRUE/FALSE struct ip_quadruple */
 #define CF_QUERY_HTTP_VERSION       9  /* number (10/11/20/30)   -  */
+/* pass in a `const struct Curl_sockaddr_ex **` as `pres2`. Gets set
+ * to NULL when not connected. */
+#define CF_QUERY_REMOTE_ADDR       10  /* -          `Curl_sockaddr_ex *` */
+#define CF_QUERY_HOST_PORT         11  /* port       const char * */
+#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_*  - * */
 
 /**
  * Query the cfilter for properties. Filters ignorant of a query will
@@ -213,7 +212,6 @@ struct Curl_cftype {
   Curl_cft_connect *do_connect;           /* establish connection */
   Curl_cft_close *do_close;               /* close conn */
   Curl_cft_shutdown *do_shutdown;         /* shutdown conn */
-  Curl_cft_get_host *get_host;            /* host filter talks to */
   Curl_cft_adjust_pollset *adjust_pollset; /* adjust transfer poll set */
   Curl_cft_data_pending *has_data_pending;/* conn has data pending */
   Curl_cft_send *do_send;                 /* send data */
@@ -241,19 +239,16 @@ 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_get_host(struct Curl_cfilter *cf, struct Curl_easy *data,
-                              const char **phost, const char **pdisplay_host,
-                              int *pport);
 void     Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf,
                                     struct Curl_easy *data,
                                     struct easy_pollset *ps);
 bool     Curl_cf_def_data_pending(struct Curl_cfilter *cf,
                                   const struct Curl_easy *data);
-ssize_t  Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+CURLcode Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                           const void *buf, size_t len, bool eos,
-                          CURLcode *err);
-ssize_t  Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
-                          char *buf, size_t len, CURLcode *err);
+                          size_t *pnwritten);
+CURLcode Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+                          char *buf, size_t len, size_t *pnread);
 CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf,
                            struct Curl_easy *data,
                            int event, int arg1, void *arg2);
@@ -326,11 +321,11 @@ CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
                               struct Curl_easy *data,
                               bool *done);
 void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data);
-ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
-                          const void *buf, size_t len, bool eos,
-                          CURLcode *err);
-ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
-                          char *buf, size_t len, CURLcode *err);
+CURLcode Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+                           const void *buf, size_t len, bool eos,
+                           size_t *pnwritten);
+CURLcode Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+                           char *buf, size_t len, size_t *pnread);
 CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf,
                             struct Curl_easy *data,
                             bool ignore_result,
@@ -356,6 +351,9 @@ CURLcode Curl_conn_cf_get_ip_info(struct Curl_cfilter *cf,
 bool Curl_conn_cf_needs_flush(struct Curl_cfilter *cf,
                               struct Curl_easy *data);
 
+unsigned char Curl_conn_cf_get_transport(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
@@ -395,6 +393,15 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex);
  */
 bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex);
 
+/*
+ * Fill `info` with information about the TLS instance securing
+ * the connection when available, otherwise e.g. when
+ * Curl_conn_is_ssl() is FALSE, return FALSE.
+ */
+bool Curl_conn_get_ssl_info(struct Curl_easy *data,
+                            struct connectdata *conn, int sockindex,
+                            struct curl_tlssessioninfo *info);
+
 /**
  * Connection provides multiplexing of easy handles at `socketindex`.
  */
@@ -407,6 +414,10 @@ bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex);
 unsigned char Curl_conn_http_version(struct Curl_easy *data,
                                      struct connectdata *conn);
 
+/* Get the TRNSPRT_* the connection is using */
+unsigned char Curl_conn_get_transport(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.
@@ -444,13 +455,17 @@ CURLcode Curl_conn_flush(struct Curl_easy *data, int sockindex);
  */
 curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex);
 
+/* Return a pointer to the connected socket address or NULL. */
+const struct Curl_sockaddr_ex *
+Curl_conn_get_remote_addr(struct Curl_easy *data, int sockindex);
+
 /**
  * Tell filters to forget about the socket at sockindex.
  */
 void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex);
 
 /**
- * Adjust the pollset for the filter chain startgin at `cf`.
+ * Adjust the pollset for the filter chain starting at `cf`.
  */
 void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
@@ -475,20 +490,41 @@ int Curl_conn_cf_poll(struct Curl_cfilter *cf,
 /**
  * Receive data through the filter chain at `sockindex` for connection
  * `data->conn`. Copy at most `len` bytes into `buf`. Return the
- * actual number of bytes copied or a negative value on error.
- * The error code is placed into `*code`.
+ * actual number of bytes copied in `*pnread`or an error.
  */
-ssize_t Curl_cf_recv(struct Curl_easy *data, int sockindex, char *buf,
-                     size_t len, CURLcode *code);
+CURLcode Curl_cf_recv(struct Curl_easy *data, int sockindex, char *buf,
+                      size_t len, size_t *pnread);
 
 /**
  * Send `len` bytes of data from `buf` through the filter chain `sockindex`
  * at connection `data->conn`. Return the actual number of bytes written
- * or a negative value on error.
- * The error code is placed into `*code`.
+ * in `*pnwritten` or on error.
  */
-ssize_t Curl_cf_send(struct Curl_easy *data, int sockindex,
-                     const void *buf, size_t len, bool eos, CURLcode *code);
+CURLcode Curl_cf_send(struct Curl_easy *data, int sockindex,
+                      const void *buf, size_t len, bool eos,
+                      size_t *pnwritten);
+
+/**
+ * Receive bytes from connection filter `cf` into `bufq`.
+ * Convenience wrappter around `Curl_bufq_sipn()`,
+ * so users do not have to implement a callback.
+ */
+CURLcode Curl_cf_recv_bufq(struct Curl_cfilter *cf,
+                           struct Curl_easy *data,
+                           struct bufq *bufq,
+                           size_t maxlen,
+                           size_t *pnread);
+
+/**
+ * Send bytes in `bufq` using connection filter `cf`.
+ * A convenience wrapper around `Curl_bufq_write_pass()`,
+ * so users do not have to implement a callback.
+ */
+CURLcode Curl_cf_send_bufq(struct Curl_cfilter *cf,
+                           struct Curl_easy *data,
+                           struct bufq *bufq,
+                           const unsigned char *buf, size_t blen,
+                           size_t *pnwritten);
 
 /**
  * Notify connection filters that they need to setup data for
@@ -535,9 +571,17 @@ CURLcode Curl_conn_keep_alive(struct Curl_easy *data,
 #ifdef UNITTESTS
 void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data);
 #endif
-void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
-                        const char **phost, const char **pdisplay_host,
-                        int *pport);
+
+/**
+ * Get the remote hostname and port that the connection is currently
+ * talking to (or will talk to).
+ * Once connected or before connect starts,
+ * it is `conn->host.name` and `conn->remote_port`.
+ * During connect, when tunneling proxies are involved (http or socks),
+ * it will be the name and port the proxy currently negotiates with.
+ */
+void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex,
+                                const char **phost, int *pport);
 
 /**
  * Get the maximum number of parallel transfers the connection
@@ -567,7 +611,7 @@ int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd);
  */
 CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex,
                         char *buf, size_t buffersize,
-                        ssize_t *pnread);
+                        size_t *pnread);
 
 /*
  * Send data on the connection, using FIRSTSOCKET/SECONDARYSOCKET.
@@ -621,6 +665,13 @@ 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.

+ 0 - 1
lib/conncache.c

@@ -42,7 +42,6 @@
 #include "sigpipe.h"
 #include "connect.h"
 #include "select.h"
-#include "strcase.h"
 #include "curlx/strparse.h"
 #include "uint-table.h"
 

+ 1 - 1
lib/conncache.h

@@ -106,7 +106,7 @@ typedef bool Curl_cpool_done_match_cb(bool result, void *userdata);
  * Find a connection in the pool matching `destination`.
  * All callbacks are invoked while the pool's lock is held.
  * @param data        current transfer
- * @param destination match agaonst `conn->destination` in pool
+ * @param destination match against `conn->destination` in pool
  * @param conn_cb     must be present, called for each connection in the
  *                    bundle until it returns TRUE
  * @return combined result of last conn_db and result_cb or FALSE if no

+ 27 - 29
lib/connect.c

@@ -66,7 +66,7 @@
 #include "url.h" /* for Curl_safefree() */
 #include "multiif.h"
 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
-#include "inet_ntop.h"
+#include "curlx/inet_ntop.h"
 #include "curlx/inet_pton.h"
 #include "vtls/vtls.h" /* for vtsl cfilters */
 #include "progress.h"
@@ -78,7 +78,6 @@
 #include "vquic/vquic.h" /* for quic cfilters */
 #include "http_proxy.h"
 #include "socks.h"
-#include "strcase.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -90,15 +89,15 @@
 enum alpnid Curl_alpn2alpnid(const char *name, size_t len)
 {
   if(len == 2) {
-    if(strncasecompare(name, "h1", 2))
+    if(curl_strnequal(name, "h1", 2))
       return ALPN_h1;
-    if(strncasecompare(name, "h2", 2))
+    if(curl_strnequal(name, "h2", 2))
       return ALPN_h2;
-    if(strncasecompare(name, "h3", 2))
+    if(curl_strnequal(name, "h3", 2))
       return ALPN_h3;
   }
   else if(len == 8) {
-    if(strncasecompare(name, "http/1.1", 8))
+    if(curl_strnequal(name, "http/1.1", 8))
       return ALPN_h1;
   }
   return ALPN_none; /* unknown, probably rubbish input */
@@ -172,11 +171,11 @@ void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
   }
   data->conn->shutdown.start[sockindex] = *nowp;
   data->conn->shutdown.timeout_ms = (timeout_ms > 0) ?
-    (unsigned int)timeout_ms :
+    (timediff_t)timeout_ms :
     ((data->set.shutdowntimeout > 0) ?
      data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS);
   /* Set a timer, unless we operate on the admin handle */
-  if(data->mid && data->conn->shutdown.timeout_ms)
+  if(data->mid && (data->conn->shutdown.timeout_ms > 0))
     Curl_expire_ex(data, nowp, data->conn->shutdown.timeout_ms,
                    EXPIRE_SHUTDOWN);
 }
@@ -187,7 +186,8 @@ timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
   struct curltime now;
   timediff_t left_ms;
 
-  if(!conn->shutdown.start[sockindex].tv_sec || !conn->shutdown.timeout_ms)
+  if(!conn->shutdown.start[sockindex].tv_sec ||
+     (conn->shutdown.timeout_ms <= 0))
     return 0; /* not started or no limits */
 
   if(!nowp) {
@@ -254,8 +254,8 @@ addr_next_match(const struct Curl_addrinfo *addr, int family)
   return NULL;
 }
 
-/* retrieves ip address and port from a sockaddr structure.
-   note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */
+/* 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,
                       char *addr, int *port)
 {
@@ -272,7 +272,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
   switch(sa->sa_family) {
     case AF_INET:
       si = (struct sockaddr_in *)(void *) sa;
-      if(Curl_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) {
+      if(curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) {
         unsigned short us_port = ntohs(si->sin_port);
         *port = us_port;
         return TRUE;
@@ -281,7 +281,8 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
 #ifdef USE_IPV6
     case AF_INET6:
       si6 = (struct sockaddr_in6 *)(void *) sa;
-      if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr, addr, MAX_IPADR_LEN)) {
+      if(curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr,
+                         MAX_IPADR_LEN)) {
         unsigned short us_port = ntohs(si6->sin6_port);
         *port = us_port;
         return TRUE;
@@ -389,7 +390,6 @@ struct eyeballer {
   expire_id timeout_id;              /* ID for Curl_expire() */
   CURLcode result;
   int error;
-  BIT(rewinded);                     /* if we rewinded the addr list */
   BIT(has_started);                  /* attempts have started */
   BIT(is_done);                      /* out of addresses/time */
   BIT(connected);                    /* cf has connected */
@@ -455,7 +455,7 @@ static CURLcode eyeballer_new(struct eyeballer **pballer,
 }
 
 static void baller_close(struct eyeballer *baller,
-                          struct Curl_easy *data)
+                         struct Curl_easy *data)
 {
   if(baller && baller->cf) {
     Curl_conn_cf_discard_chain(&baller->cf, data);
@@ -463,7 +463,7 @@ static void baller_close(struct eyeballer *baller,
 }
 
 static void baller_free(struct eyeballer *baller,
-                         struct Curl_easy *data)
+                        struct Curl_easy *data)
 {
   if(baller) {
     baller_close(baller, data);
@@ -473,7 +473,6 @@ static void baller_free(struct eyeballer *baller,
 
 static void baller_rewind(struct eyeballer *baller)
 {
-  baller->rewinded = TRUE;
   baller->addr = baller->first;
   baller->inconclusive = FALSE;
 }
@@ -682,7 +681,7 @@ evaluate:
         /* next attempt was started */
         CURL_TRC_CF(data, cf, "%s trying next", baller->name);
         ++ongoing;
-        Curl_expire(data, 0, EXPIRE_RUN_NOW);
+        Curl_multi_mark_dirty(data);
       }
     }
   }
@@ -832,7 +831,7 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
     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) {
+    if(!addr1 && !addr0 && dns->addr) {
       ai_family0 = dns->addr->ai_family;
       addr0 = addr_first_match(dns->addr, ai_family0);
     }
@@ -932,8 +931,8 @@ static CURLcode cf_he_shutdown(struct Curl_cfilter *cf,
 }
 
 static void cf_he_adjust_pollset(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data,
-                                  struct easy_pollset *ps)
+                                 struct Curl_easy *data,
+                                 struct easy_pollset *ps)
 {
   struct cf_he_ctx *ctx = cf->ctx;
   size_t i;
@@ -993,11 +992,11 @@ static CURLcode cf_he_connect(struct Curl_cfilter *cf,
           struct ip_quadruple ipquad;
           int is_ipv6;
           if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) {
-            const char *host, *disphost;
+            const char *host;
             int port;
-            cf->next->cft->get_host(cf->next, data, &host, &disphost, &port);
+            Curl_conn_get_current_host(data, cf->sockindex, &host, &port);
             CURL_TRC_CF(data, cf, "Connected to %s (%s) port %u",
-                        disphost, ipquad.remote_ip, ipquad.remote_port);
+                        host, ipquad.remote_ip, ipquad.remote_port);
           }
         }
         data->info.numconnects++; /* to track the # of connections made */
@@ -1046,8 +1045,8 @@ static bool cf_he_data_pending(struct Curl_cfilter *cf,
 }
 
 static struct curltime get_max_baller_time(struct Curl_cfilter *cf,
-                                          struct Curl_easy *data,
-                                          int query)
+                                           struct Curl_easy *data,
+                                           int query)
 {
   struct cf_he_ctx *ctx = cf->ctx;
   struct curltime t, tmax;
@@ -1134,7 +1133,6 @@ struct Curl_cftype Curl_cft_happy_eyeballs = {
   cf_he_connect,
   cf_he_close,
   cf_he_shutdown,
-  Curl_cf_def_get_host,
   cf_he_adjust_pollset,
   cf_he_data_pending,
   Curl_cf_def_send,
@@ -1398,7 +1396,6 @@ struct Curl_cftype Curl_cft_setup = {
   cf_setup_connect,
   cf_setup_close,
   Curl_cf_def_shutdown,
-  Curl_cf_def_get_host,
   Curl_cf_def_adjust_pollset,
   Curl_cf_def_data_pending,
   Curl_cf_def_send,
@@ -1518,7 +1515,8 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
 
   /* Still no cfilter set, apply default. */
   if(!conn->cfilter[sockindex]) {
-    result = cf_setup_add(data, conn, sockindex, conn->transport, ssl_mode);
+    result = cf_setup_add(data, conn, sockindex,
+                          conn->transport_wanted, ssl_mode);
     if(result)
       goto out;
   }

+ 12 - 13
lib/content_encoding.c

@@ -52,7 +52,6 @@
 #include "http.h"
 #include "content_encoding.h"
 #include "strdup.h"
-#include "strcase.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -343,7 +342,7 @@ static CURLcode gzip_do_write(struct Curl_easy *data,
 }
 
 static void gzip_do_close(struct Curl_easy *data,
-                              struct Curl_cwriter *writer)
+                          struct Curl_cwriter *writer)
 {
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
@@ -464,7 +463,7 @@ static CURLcode brotli_do_write(struct Curl_easy *data,
 }
 
 static void brotli_do_close(struct Curl_easy *data,
-                                struct Curl_cwriter *writer)
+                            struct Curl_cwriter *writer)
 {
   struct brotli_writer *bp = (struct brotli_writer *) writer;
   (void) data;
@@ -567,7 +566,7 @@ static CURLcode zstd_do_write(struct Curl_easy *data,
 }
 
 static void zstd_do_close(struct Curl_easy *data,
-                              struct Curl_cwriter *writer)
+                          struct Curl_cwriter *writer)
 {
   struct zstd_writer *zp = (struct zstd_writer *) writer;
   (void)data;
@@ -636,7 +635,7 @@ void Curl_all_content_encodings(char *buf, size_t blen)
 
   for(cep = general_unencoders; *cep; cep++) {
     ce = *cep;
-    if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT))
+    if(!curl_strequal(ce->name, CONTENT_ENCODING_DEFAULT))
       len += strlen(ce->name) + 2;
   }
 
@@ -648,7 +647,7 @@ void Curl_all_content_encodings(char *buf, size_t blen)
     char *p = buf;
     for(cep = general_unencoders; *cep; cep++) {
       ce = *cep;
-      if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) {
+      if(!curl_strequal(ce->name, CONTENT_ENCODING_DEFAULT)) {
         strcpy(p, ce->name);
         p += strlen(p);
         *p++ = ',';
@@ -688,7 +687,7 @@ static CURLcode error_do_write(struct Curl_easy *data,
 }
 
 static void error_do_close(struct Curl_easy *data,
-                               struct Curl_cwriter *writer)
+                           struct Curl_cwriter *writer)
 {
   (void) data;
   (void) writer;
@@ -713,8 +712,8 @@ static const struct Curl_cwtype *find_unencode_writer(const char *name,
   if(phase == CURL_CW_TRANSFER_DECODE) {
     for(cep = transfer_unencoders; *cep; cep++) {
       const struct Curl_cwtype *ce = *cep;
-      if((strncasecompare(name, ce->name, len) && !ce->name[len]) ||
-         (ce->alias && strncasecompare(name, ce->alias, len)
+      if((curl_strnequal(name, ce->name, len) && !ce->name[len]) ||
+         (ce->alias && curl_strnequal(name, ce->alias, len)
                     && !ce->alias[len]))
         return ce;
     }
@@ -722,8 +721,8 @@ static const struct Curl_cwtype *find_unencode_writer(const char *name,
   /* look among the general decoders */
   for(cep = general_unencoders; *cep; cep++) {
     const struct Curl_cwtype *ce = *cep;
-    if((strncasecompare(name, ce->name, len) && !ce->name[len]) ||
-       (ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len]))
+    if((curl_strnequal(name, ce->name, len) && !ce->name[len]) ||
+       (ce->alias && curl_strnequal(name, ce->alias, len) && !ce->alias[len]))
       return ce;
   }
   return NULL;
@@ -761,12 +760,12 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
       CURL_TRC_WRITE(data, "looking for %s decoder: %.*s",
                      is_transfer ? "transfer" : "content", (int)namelen, name);
       is_chunked = (is_transfer && (namelen == 7) &&
-                    strncasecompare(name, "chunked", 7));
+                    curl_strnequal(name, "chunked", 7));
       /* if we skip the decoding in this phase, do not look further.
        * Exception is "chunked" transfer-encoding which always must happen */
       if((is_transfer && !data->set.http_transfer_encoding && !is_chunked) ||
          (!is_transfer && data->set.http_ce_skip)) {
-        bool is_identity = strncasecompare(name, "identity", 8);
+        bool is_identity = curl_strnequal(name, "identity", 8);
         /* not requested, ignore */
         CURL_TRC_WRITE(data, "decoder not requested, ignored: %.*s",
                        (int)namelen, name);

+ 14 - 14
lib/cookie.c

@@ -135,9 +135,9 @@ static bool cookie_tailmatch(const char *cookie_domain,
   if(hostname_len < cookie_domain_len)
     return FALSE;
 
-  if(!strncasecompare(cookie_domain,
-                      hostname + hostname_len-cookie_domain_len,
-                      cookie_domain_len))
+  if(!curl_strnequal(cookie_domain,
+                     hostname + hostname_len-cookie_domain_len,
+                     cookie_domain_len))
     return FALSE;
 
   /*
@@ -409,7 +409,7 @@ static void remove_expired(struct CookieInfo *ci)
 /* Make sure domain contains a dot or is localhost. */
 static bool bad_domain(const char *domain, size_t len)
 {
-  if((len == 9) && strncasecompare(domain, "localhost", 9))
+  if((len == 9) && curl_strnequal(domain, "localhost", 9))
     return FALSE;
   else {
     /* there must be a dot present, but that dot must not be a trailing dot */
@@ -815,7 +815,7 @@ parse_netscape(struct Cookie *co,
        * domain can access the variable. Set TRUE when the cookie says
        * .example.com and to false when the domain is complete www.example.com
        */
-      co->tailmatch = !!strncasecompare(ptr, "TRUE", len);
+      co->tailmatch = !!curl_strnequal(ptr, "TRUE", len);
       break;
     case 2:
       /* The file format allows the path field to remain not filled in */
@@ -842,7 +842,7 @@ parse_netscape(struct Cookie *co,
       FALLTHROUGH();
     case 3:
       co->secure = FALSE;
-      if(strncasecompare(ptr, "TRUE", len)) {
+      if(curl_strnequal(ptr, "TRUE", len)) {
         if(secure || ci->running)
           co->secure = TRUE;
         else
@@ -859,9 +859,9 @@ parse_netscape(struct Cookie *co,
         return CERR_OUT_OF_MEMORY;
       else {
         /* For Netscape file format cookies we check prefix on the name */
-        if(strncasecompare("__Secure-", co->name, 9))
+        if(curl_strnequal("__Secure-", co->name, 9))
           co->prefix_secure = TRUE;
-        else if(strncasecompare("__Host-", co->name, 7))
+        else if(curl_strnequal("__Host-", co->name, 7))
           co->prefix_host = TRUE;
       }
       break;
@@ -917,7 +917,7 @@ is_public_suffix(struct Curl_easy *data,
         Curl_psl_release(data);
       }
       else
-        infof(data, "libpsl problem, rejecting cookie for satety");
+        infof(data, "libpsl problem, rejecting cookie for safety");
     }
 
     if(!acceptable) {
@@ -954,7 +954,7 @@ replace_existing(struct Curl_easy *data,
       bool matching_domains = FALSE;
 
       if(clist->domain && co->domain) {
-        if(strcasecompare(clist->domain, co->domain))
+        if(curl_strequal(clist->domain, co->domain))
           /* The domains are identical */
           matching_domains = TRUE;
       }
@@ -981,7 +981,7 @@ replace_existing(struct Curl_easy *data,
         else
           cllen = strlen(clist->spath);
 
-        if(strncasecompare(clist->spath, co->spath, cllen)) {
+        if(curl_strnequal(clist->spath, co->spath, cllen)) {
           infof(data, "cookie '%s' for domain '%s' dropped, would "
                 "overlay an existing cookie", co->name, co->domain);
           return CERR_BAD_SECURE;
@@ -993,7 +993,7 @@ replace_existing(struct Curl_easy *data,
       /* the names are identical */
 
       if(clist->domain && co->domain) {
-        if(strcasecompare(clist->domain, co->domain) &&
+        if(curl_strequal(clist->domain, co->domain) &&
           (clist->tailmatch == co->tailmatch))
           /* The domains are identical */
           replace_old = TRUE;
@@ -1005,7 +1005,7 @@ replace_existing(struct Curl_easy *data,
         /* the domains were identical */
 
         if(clist->spath && co->spath &&
-           !strcasecompare(clist->spath, co->spath))
+           !curl_strequal(clist->spath, co->spath))
           replace_old = FALSE;
         else if(!clist->spath != !co->spath)
           replace_old = FALSE;
@@ -1337,7 +1337,7 @@ int Curl_cookie_getlist(struct Curl_easy *data,
       if(!co->domain ||
          (co->tailmatch && !is_ip &&
           cookie_tailmatch(co->domain, strlen(co->domain), host)) ||
-         ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) {
+         ((!co->tailmatch || is_ip) && curl_strequal(host, co->domain)) ) {
         /*
          * the right part of the host matches the domain stuff in the
          * cookie data

+ 11 - 18
lib/cshutdn.c

@@ -40,7 +40,6 @@
 #include "sigpipe.h"
 #include "connect.h"
 #include "select.h"
-#include "strcase.h"
 #include "curlx/strparse.h"
 
 /* The last 3 #include files should be in this order */
@@ -54,12 +53,6 @@ static void cshutdn_run_conn_handler(struct Curl_easy *data,
 {
   if(!conn->bits.shutdown_handler) {
 
-    /* Cleanup NTLM connection-related data */
-    Curl_http_auth_cleanup_ntlm(conn);
-
-    /* Cleanup NEGOTIATE connection-related data */
-    Curl_http_auth_cleanup_negotiate(conn);
-
     if(conn->handler && conn->handler->disconnect) {
       /* Some disconnect handlers do a blocking wait on server responses.
        * FTP/IMAP/SMTP and SFTP are among them. When using the internal
@@ -121,8 +114,8 @@ static void cshutdn_run_once(struct Curl_easy *data,
 }
 
 void Curl_cshutdn_run_once(struct Curl_easy *data,
-                      struct connectdata *conn,
-                      bool *done)
+                           struct connectdata *conn,
+                           bool *done)
 {
   DEBUGASSERT(!data->conn);
   Curl_attach_connection(data, conn);
@@ -219,8 +212,8 @@ bool Curl_cshutdn_close_oldest(struct Curl_easy *data,
 #define NUM_POLLS_ON_STACK 10
 
 static CURLcode cshutdn_wait(struct cshutdn *cshutdn,
-                               struct Curl_easy *data,
-                               int timeout_ms)
+                             struct Curl_easy *data,
+                             int timeout_ms)
 {
   struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
   struct curl_pollfds cpfds;
@@ -241,7 +234,7 @@ out:
 
 
 static void cshutdn_perform(struct cshutdn *cshutdn,
-                              struct Curl_easy *data)
+                            struct Curl_easy *data)
 {
   struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list);
   struct Curl_llist_node *enext;
@@ -402,8 +395,8 @@ size_t Curl_cshutdn_dest_count(struct Curl_easy *data,
 
 
 static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn,
-                                     struct Curl_easy *data,
-                                     struct connectdata *conn)
+                                   struct Curl_easy *data,
+                                   struct connectdata *conn)
 {
   CURLMcode mresult;
 
@@ -418,8 +411,8 @@ static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn,
 
 
 void Curl_cshutdn_add(struct cshutdn *cshutdn,
-                        struct connectdata *conn,
-                        size_t conns_in_pool)
+                      struct connectdata *conn,
+                      size_t conns_in_pool)
 {
   struct Curl_easy *data = cshutdn->multi->admin;
   size_t max_total = (cshutdn->multi->max_total_connections > 0) ?
@@ -451,8 +444,8 @@ void Curl_cshutdn_add(struct cshutdn *cshutdn,
 
 
 static void cshutdn_multi_socket(struct cshutdn *cshutdn,
-                                   struct Curl_easy *data,
-                                   curl_socket_t s)
+                                 struct Curl_easy *data,
+                                 curl_socket_t s)
 {
   struct Curl_llist_node *e;
   struct connectdata *conn;

+ 0 - 27
lib/curl_config.h.cmake

@@ -586,18 +586,9 @@
 /* Define to 1 if you have the <sys/select.h> header file. */
 #cmakedefine HAVE_SYS_SELECT_H 1
 
-/* Define to 1 if you have the <sys/socket.h> header file. */
-#cmakedefine HAVE_SYS_SOCKET_H 1
-
 /* Define to 1 if you have the <sys/sockio.h> header file. */
 #cmakedefine HAVE_SYS_SOCKIO_H 1
 
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#cmakedefine HAVE_SYS_STAT_H 1
-
-/* Define to 1 if you have the <sys/time.h> header file. */
-#cmakedefine HAVE_SYS_TIME_H 1
-
 /* Define to 1 if you have the <sys/types.h> header file. */
 #cmakedefine HAVE_SYS_TYPES_H 1
 
@@ -685,18 +676,12 @@ ${SIZEOF_TIME_T_CODE}
 /* if GnuTLS is enabled */
 #cmakedefine USE_GNUTLS 1
 
-/* if Secure Transport is enabled */
-#cmakedefine USE_SECTRANSP 1
-
 /* if SSL session export support is available */
 #cmakedefine USE_SSLS_EXPORT 1
 
 /* if mbedTLS is enabled */
 #cmakedefine USE_MBEDTLS 1
 
-/* if BearSSL is enabled */
-#cmakedefine USE_BEARSSL 1
-
 /* if Rustls is enabled */
 #cmakedefine USE_RUSTLS 1
 
@@ -800,18 +785,6 @@ ${SIZEOF_TIME_T_CODE}
 /* Number of bits in a file offset, on hosts where this is settable. */
 #cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS}
 
-/* Define for large files, on AIX-style hosts. */
-#cmakedefine _LARGE_FILES ${_LARGE_FILES}
-
-/* define this if you need it to compile thread-safe code */
-#cmakedefine _THREAD_SAFE ${_THREAD_SAFE}
-
-/* Define to empty if `const' does not conform to ANSI C. */
-#cmakedefine const ${const}
-
-/* Define to `unsigned int' if <sys/types.h> does not define. */
-#cmakedefine size_t ${size_t}
-
 /* the signed version of size_t */
 #cmakedefine ssize_t ${ssize_t}
 

+ 0 - 1
lib/curl_des.c

@@ -26,7 +26,6 @@
 
 #if defined(USE_CURL_NTLM_CORE) && \
   (defined(USE_GNUTLS) ||          \
-   defined(USE_SECTRANSP) ||       \
    defined(USE_OS400CRYPTO) ||     \
    defined(USE_WIN32_CRYPTO))
 

+ 0 - 1
lib/curl_des.h

@@ -28,7 +28,6 @@
 
 #if defined(USE_CURL_NTLM_CORE) && \
   (defined(USE_GNUTLS) ||          \
-   defined(USE_SECTRANSP) ||       \
    defined(USE_OS400CRYPTO) ||     \
    defined(USE_WIN32_CRYPTO))
 

+ 25 - 10
lib/curl_get_line.c

@@ -32,6 +32,15 @@
 /* The last #include file should be: */
 #include "memdebug.h"
 
+static int appendnl(struct dynbuf *buf)
+{
+  CURLcode result = curlx_dyn_addn(buf, "\n", 1);
+  if(result)
+    /* too long line or out of memory */
+    return 0; /* error */
+  return 1; /* all good */
+}
+
 /*
  * Curl_get_line() makes sure to only return complete whole lines that end
  * newlines.
@@ -43,9 +52,10 @@ int Curl_get_line(struct dynbuf *buf, FILE *input)
   curlx_dyn_reset(buf);
   while(1) {
     char *b = fgets(buffer, sizeof(buffer), input);
+    size_t rlen;
 
     if(b) {
-      size_t rlen = strlen(b);
+      rlen = strlen(b);
 
       if(!rlen)
         break;
@@ -59,19 +69,24 @@ int Curl_get_line(struct dynbuf *buf, FILE *input)
         /* end of the line */
         return 1; /* all good */
 
-      else if(feof(input)) {
+      else if(feof(input))
         /* append a newline */
-        result = curlx_dyn_addn(buf, "\n", 1);
-        if(result)
-          /* too long line or out of memory */
-          return 0; /* error */
+        return appendnl(buf);
+    }
+    else {
+      rlen = curlx_dyn_len(buf);
+      if(rlen) {
+        b = curlx_dyn_ptr(buf);
+
+        if(b[rlen-1] != '\n')
+          /* append a newline */
+          return appendnl(buf);
+
         return 1; /* all good */
       }
+      else
+        break;
     }
-    else if(curlx_dyn_len(buf))
-      return 1; /* all good */
-    else
-      break;
   }
   return 0;
 }

+ 286 - 12
lib/curl_gssapi.c

@@ -52,17 +52,260 @@ gss_OID_desc Curl_krb5_mech_oid CURL_ALIGN8 = {
   9, CURL_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")
 };
 
-OM_uint32 Curl_gss_init_sec_context(
-    struct Curl_easy *data,
-    OM_uint32 *minor_status,
-    gss_ctx_id_t *context,
-    gss_name_t target_name,
-    gss_OID mech_type,
-    gss_channel_bindings_t input_chan_bindings,
-    gss_buffer_t input_token,
-    gss_buffer_t output_token,
-    const bool mutual_auth,
-    OM_uint32 *ret_flags)
+#ifdef DEBUGBUILD
+enum min_err_code {
+  STUB_GSS_OK = 0,
+  STUB_GSS_NO_MEMORY,
+  STUB_GSS_INVALID_ARGS,
+  STUB_GSS_INVALID_CREDS,
+  STUB_GSS_INVALID_CTX,
+  STUB_GSS_SERVER_ERR,
+  STUB_GSS_NO_MECH,
+  STUB_GSS_LAST
+};
+
+/* libcurl is also passing this struct to these functions, which are not yet
+ * stubbed:
+ *   gss_inquire_context()
+ *   gss_unwrap()
+ *   gss_wrap()
+ */
+struct stub_gss_ctx_id_t_desc {
+  enum { STUB_GSS_NONE, STUB_GSS_KRB5, STUB_GSS_NTLM1, STUB_GSS_NTLM3 } sent;
+  int have_krb5;
+  int have_ntlm;
+  OM_uint32 flags;
+  char creds[250];
+};
+
+static OM_uint32
+stub_gss_init_sec_context(OM_uint32 *min,
+                          gss_cred_id_t initiator_cred_handle,
+                          struct stub_gss_ctx_id_t_desc **context,
+                          gss_name_t target_name,
+                          const gss_OID mech_type,
+                          OM_uint32 req_flags,
+                          OM_uint32 time_req,
+                          const gss_channel_bindings_t input_chan_bindings,
+                          gss_buffer_desc *input_token,
+                          gss_OID *actual_mech_type,
+                          gss_buffer_desc *output_token,
+                          OM_uint32 *ret_flags,
+                          OM_uint32 *time_rec)
+{
+  struct stub_gss_ctx_id_t_desc *ctx = NULL;
+
+  /* The token will be encoded in base64 */
+  size_t length = sizeof(ctx->creds) * 3 / 4;
+  size_t used = 0;
+  char *token = NULL;
+  const char *creds = NULL;
+
+  (void)initiator_cred_handle;
+  (void)mech_type;
+  (void)time_req;
+  (void)input_chan_bindings;
+  (void)actual_mech_type;
+
+  if(!min)
+    return GSS_S_FAILURE;
+
+  *min = 0;
+
+  if(!context || !target_name || !output_token) {
+    *min = STUB_GSS_INVALID_ARGS;
+    return GSS_S_FAILURE;
+  }
+
+  creds = getenv("CURL_STUB_GSS_CREDS");
+  if(!creds || strlen(creds) >= sizeof(ctx->creds)) {
+    *min = STUB_GSS_INVALID_CREDS;
+    return GSS_S_FAILURE;
+  }
+
+  ctx = *context;
+  if(ctx && strcmp(ctx->creds, creds)) {
+    *min = STUB_GSS_INVALID_CREDS;
+    return GSS_S_FAILURE;
+  }
+
+  output_token->length = 0;
+  output_token->value = NULL;
+
+  if(input_token && input_token->length) {
+    if(!ctx) {
+      *min = STUB_GSS_INVALID_CTX;
+      return GSS_S_FAILURE;
+    }
+
+    /* Server response, either D (RA==) or C (Qw==) */
+    if(((char *) input_token->value)[0] == 'D') {
+      /* Done */
+      switch(ctx->sent) {
+      case STUB_GSS_KRB5:
+      case STUB_GSS_NTLM3:
+        if(ret_flags)
+          *ret_flags = ctx->flags;
+        if(time_rec)
+          *time_rec = GSS_C_INDEFINITE;
+        return GSS_S_COMPLETE;
+      default:
+        *min = STUB_GSS_SERVER_ERR;
+        return GSS_S_FAILURE;
+      }
+    }
+
+    if(((char *) input_token->value)[0] != 'C') {
+      /* We only support Done or Continue */
+      *min = STUB_GSS_SERVER_ERR;
+      return GSS_S_FAILURE;
+    }
+
+    /* Continue */
+    switch(ctx->sent) {
+    case STUB_GSS_KRB5:
+      /* We sent KRB5 and it failed, let's try NTLM */
+      if(ctx->have_ntlm) {
+        ctx->sent = STUB_GSS_NTLM1;
+        break;
+      }
+      else {
+        *min = STUB_GSS_SERVER_ERR;
+        return GSS_S_FAILURE;
+      }
+    case STUB_GSS_NTLM1:
+      ctx->sent = STUB_GSS_NTLM3;
+      break;
+    default:
+      *min = STUB_GSS_SERVER_ERR;
+      return GSS_S_FAILURE;
+    }
+  }
+  else {
+    if(ctx) {
+      *min = STUB_GSS_INVALID_CTX;
+      return GSS_S_FAILURE;
+    }
+
+    ctx = calloc(1, sizeof(*ctx));
+    if(!ctx) {
+      *min = STUB_GSS_NO_MEMORY;
+      return GSS_S_FAILURE;
+    }
+
+    if(strstr(creds, "KRB5"))
+      ctx->have_krb5 = 1;
+
+    if(strstr(creds, "NTLM"))
+      ctx->have_ntlm = 1;
+
+    if(ctx->have_krb5)
+      ctx->sent = STUB_GSS_KRB5;
+    else if(ctx->have_ntlm)
+      ctx->sent = STUB_GSS_NTLM1;
+    else {
+      free(ctx);
+      *min = STUB_GSS_NO_MECH;
+      return GSS_S_FAILURE;
+    }
+
+    strcpy(ctx->creds, creds);
+    ctx->flags = req_flags;
+  }
+
+  /* To avoid memdebug macro replacement, wrap the name in parentheses to call
+     the original version. It is freed via the GSS API gss_release_buffer(). */
+  token = (malloc)(length);
+  if(!token) {
+    free(ctx);
+    *min = STUB_GSS_NO_MEMORY;
+    return GSS_S_FAILURE;
+  }
+
+  {
+    gss_buffer_desc target_desc;
+    gss_OID name_type = GSS_C_NO_OID;
+    OM_uint32 minor_status;
+    OM_uint32 major_status;
+    major_status = gss_display_name(&minor_status, target_name,
+                                    &target_desc, &name_type);
+    if(GSS_ERROR(major_status)) {
+      (free)(token);
+      free(ctx);
+      *min = STUB_GSS_NO_MEMORY;
+      return GSS_S_FAILURE;
+    }
+
+    if(strlen(creds) + target_desc.length + 5 >= sizeof(ctx->creds)) {
+      (free)(token);
+      free(ctx);
+      *min = STUB_GSS_NO_MEMORY;
+      return GSS_S_FAILURE;
+    }
+
+    /* Token format: creds:target:type:padding */
+    used = msnprintf(token, length, "%s:%.*s:%d:", creds,
+                     (int)target_desc.length, (const char *)target_desc.value,
+                     ctx->sent);
+
+    gss_release_buffer(&minor_status, &target_desc);
+  }
+
+  if(used >= length) {
+    (free)(token);
+    free(ctx);
+    *min = STUB_GSS_NO_MEMORY;
+    return GSS_S_FAILURE;
+  }
+
+  /* Overwrite null-terminator */
+  memset(token + used, 'A', length - used);
+
+  *context = ctx;
+
+  output_token->value = token;
+  output_token->length = length;
+
+  return GSS_S_CONTINUE_NEEDED;
+}
+
+static OM_uint32
+stub_gss_delete_sec_context(OM_uint32 *min,
+                            struct stub_gss_ctx_id_t_desc **context,
+                            gss_buffer_t output_token)
+{
+  (void)output_token;
+
+  if(!min)
+    return GSS_S_FAILURE;
+
+  if(!context) {
+    *min = STUB_GSS_INVALID_CTX;
+    return GSS_S_FAILURE;
+  }
+  if(!*context) {
+    *min = STUB_GSS_INVALID_CTX;
+    return GSS_S_FAILURE;
+  }
+
+  free(*context);
+  *context = NULL;
+  *min = 0;
+
+  return GSS_S_COMPLETE;
+}
+#endif /* DEBUGBUILD */
+
+OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data,
+                                    OM_uint32 *minor_status,
+                                    gss_ctx_id_t *context,
+                                    gss_name_t target_name,
+                                    gss_OID mech_type,
+                                    gss_channel_bindings_t input_chan_bindings,
+                                    gss_buffer_t input_token,
+                                    gss_buffer_t output_token,
+                                    const bool mutual_auth,
+                                    OM_uint32 *ret_flags)
 {
   OM_uint32 req_flags = GSS_C_REPLAY_FLAG;
 
@@ -74,13 +317,30 @@ OM_uint32 Curl_gss_init_sec_context(
     req_flags |= GSS_C_DELEG_POLICY_FLAG;
 #else
     infof(data, "WARNING: support for CURLGSSAPI_DELEGATION_POLICY_FLAG not "
-        "compiled in");
+          "compiled in");
 #endif
   }
 
   if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_FLAG)
     req_flags |= GSS_C_DELEG_FLAG;
 
+#ifdef DEBUGBUILD
+  if(getenv("CURL_STUB_GSS_CREDS"))
+    return stub_gss_init_sec_context(minor_status,
+                                     GSS_C_NO_CREDENTIAL, /* cred_handle */
+                                     (struct stub_gss_ctx_id_t_desc **)context,
+                                     target_name,
+                                     mech_type,
+                                     req_flags,
+                                     0, /* time_req */
+                                     input_chan_bindings,
+                                     input_token,
+                                     NULL, /* actual_mech_type */
+                                     output_token,
+                                     ret_flags,
+                                     NULL /* time_rec */);
+#endif /* DEBUGBUILD */
+
   return gss_init_sec_context(minor_status,
                               GSS_C_NO_CREDENTIAL, /* cred_handle */
                               context,
@@ -96,6 +356,20 @@ OM_uint32 Curl_gss_init_sec_context(
                               NULL /* time_rec */);
 }
 
+OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min,
+                                      gss_ctx_id_t *context,
+                                      gss_buffer_t output_token)
+{
+#ifdef DEBUGBUILD
+  if(getenv("CURL_STUB_GSS_CREDS"))
+    return stub_gss_delete_sec_context(min,
+                                     (struct stub_gss_ctx_id_t_desc **)context,
+                                     output_token);
+#endif /* DEBUGBUILD */
+
+  return gss_delete_sec_context(min, context, output_token);
+}
+
 #define GSS_LOG_BUFFER_LEN 1024
 static size_t display_gss_error(OM_uint32 status, int type,
                                 char *buf, size_t len) {

+ 14 - 11
lib/curl_gssapi.h

@@ -32,17 +32,20 @@ extern gss_OID_desc Curl_spnego_mech_oid;
 extern gss_OID_desc Curl_krb5_mech_oid;
 
 /* Common method for using GSS-API */
-OM_uint32 Curl_gss_init_sec_context(
-    struct Curl_easy *data,
-    OM_uint32 *minor_status,
-    gss_ctx_id_t *context,
-    gss_name_t target_name,
-    gss_OID mech_type,
-    gss_channel_bindings_t input_chan_bindings,
-    gss_buffer_t input_token,
-    gss_buffer_t output_token,
-    const bool mutual_auth,
-    OM_uint32 *ret_flags);
+OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data,
+                                    OM_uint32 *minor_status,
+                                    gss_ctx_id_t *context,
+                                    gss_name_t target_name,
+                                    gss_OID mech_type,
+                                    gss_channel_bindings_t input_chan_bindings,
+                                    gss_buffer_t input_token,
+                                    gss_buffer_t output_token,
+                                    const bool mutual_auth,
+                                    OM_uint32 *ret_flags);
+
+OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min,
+                                      gss_ctx_id_t *context_handle,
+                                      gss_buffer_t output_token);
 
 /* Helper to log a GSS-API error status */
 void Curl_gss_log_error(struct Curl_easy *data, const char *prefix,

+ 14 - 23
lib/curl_memory.h

@@ -57,7 +57,6 @@
 #ifdef HEADER_CURL_MEMDEBUG_H
 /* cleanup after memdebug.h */
 
-#ifdef MEMDEBUG_NODEFINES
 #ifdef CURLDEBUG
 
 #undef strdup
@@ -69,28 +68,28 @@
 #undef recv
 
 #ifdef _WIN32
-#  ifdef UNICODE
-#    undef wcsdup
-#    undef _wcsdup
-#    undef _tcsdup
-#  else
-#    undef _tcsdup
-#  endif
+#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 /* MEMDEBUG_NODEFINES */
 #endif /* CURLDEBUG */
 
 #undef HEADER_CURL_MEMDEBUG_H
@@ -122,9 +121,6 @@ extern curl_free_callback Curl_cfree;
 extern curl_realloc_callback Curl_crealloc;
 extern curl_strdup_callback Curl_cstrdup;
 extern curl_calloc_callback Curl_ccalloc;
-#if defined(_WIN32) && defined(UNICODE)
-extern curl_wcsdup_callback Curl_cwcsdup;
-#endif
 
 #ifndef CURLDEBUG
 
@@ -149,18 +145,13 @@ extern curl_wcsdup_callback Curl_cwcsdup;
 #define free(ptr) Curl_cfree(ptr)
 
 #ifdef _WIN32
-#  ifdef UNICODE
-#    undef wcsdup
-#    define wcsdup(ptr) Curl_cwcsdup(ptr)
-#    undef _wcsdup
-#    define _wcsdup(ptr) Curl_cwcsdup(ptr)
-#    undef _tcsdup
-#    define _tcsdup(ptr) Curl_cwcsdup(ptr)
-#  else
-#    undef _tcsdup
-#    define _tcsdup(ptr) Curl_cstrdup(ptr)
-#  endif
+#undef _tcsdup
+#ifdef UNICODE
+#define _tcsdup(ptr) Curl_wcsdup(ptr)
+#else
+#define _tcsdup(ptr) Curl_cstrdup(ptr)
 #endif
+#endif /* _WIN32 */
 
 #endif /* CURLDEBUG */
 #endif /* HEADER_CURL_MEMORY_H */

+ 7 - 36
lib/curl_ntlm_core.c

@@ -40,9 +40,8 @@
    3. USE_GNUTLS
    4. -
    5. USE_MBEDTLS
-   6. USE_SECTRANSP
-   7. USE_OS400CRYPTO
-   8. USE_WIN32_CRYPTO
+   6. USE_OS400CRYPTO
+   7. USE_WIN32_CRYPTO
 
    This ensures that:
    - the same SSL branch gets activated throughout this source
@@ -107,11 +106,6 @@
 
 #  include <mbedtls/des.h>
 
-#elif defined(USE_SECTRANSP)
-
-#  include <CommonCrypto/CommonCryptor.h>
-#  include <CommonCrypto/CommonDigest.h>
-
 #elif defined(USE_OS400CRYPTO)
 #  include "cipher.mih"  /* mih/cipher */
 #elif defined(USE_WIN32_CRYPTO)
@@ -209,29 +203,6 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out,
   return mbedtls_des_crypt_ecb(&ctx, in, out) == 0;
 }
 
-#elif defined(USE_SECTRANSP)
-
-static bool encrypt_des(const unsigned char *in, unsigned char *out,
-                        const unsigned char *key_56)
-{
-  char key[8];
-  size_t out_len;
-  CCCryptorStatus err;
-
-  /* Expand the 56-bit key to 64 bits */
-  extend_key_56_to_64(key_56, key);
-
-  /* Set the key parity to odd */
-  Curl_des_set_odd_parity((unsigned char *) key, sizeof(key));
-
-  /* Perform the encryption */
-  err = CCCrypt(kCCEncrypt, kCCAlgorithmDES, kCCOptionECBMode, key,
-                kCCKeySizeDES, NULL, in, 8 /* inbuflen */, out,
-                8 /* outbuflen */, &out_len);
-
-  return err == kCCSuccess;
-}
-
 #elif defined(USE_OS400CRYPTO)
 
 static bool encrypt_des(const unsigned char *in, unsigned char *out,
@@ -339,8 +310,8 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys,
   des_encrypt(&des, 8, results + 8, plaintext);
   setup_des_key(keys + 14, &des);
   des_encrypt(&des, 8, results + 16, plaintext);
-#elif defined(USE_MBEDTLS) || defined(USE_SECTRANSP)            \
-  || defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO)
+#elif defined(USE_MBEDTLS) || defined(USE_OS400CRYPTO) ||       \
+  defined(USE_WIN32_CRYPTO)
   encrypt_des(plaintext, results, keys);
   encrypt_des(plaintext, results + 8, keys + 7);
   encrypt_des(plaintext, results + 16, keys + 14);
@@ -387,8 +358,8 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password,
     des_encrypt(&des, 8, lmbuffer, magic);
     setup_des_key(pw + 7, &des);
     des_encrypt(&des, 8, lmbuffer + 8, magic);
-#elif defined(USE_MBEDTLS) || defined(USE_SECTRANSP)            \
-  || defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO)
+#elif defined(USE_MBEDTLS) || defined(USE_OS400CRYPTO) ||       \
+  defined(USE_WIN32_CRYPTO)
     encrypt_des(magic, lmbuffer, pw);
     encrypt_des(magic, lmbuffer + 8, pw + 7);
 #endif
@@ -466,7 +437,7 @@ struct ms_filetime {
 static void time2filetime(struct ms_filetime *ft, time_t t)
 {
 #if SIZEOF_TIME_T > 4
-  t = (t + CURL_OFF_T_C(11644473600)) * 10000000;
+  t = (t + (curl_off_t)11644473600) * 10000000;
   ft->dwLowDateTime = (unsigned int) (t & 0xFFFFFFFF);
   ft->dwHighDateTime = (unsigned int) (t >> 32);
 #else

+ 4 - 0
lib/curl_ntlm_core.h

@@ -28,6 +28,10 @@
 
 #if defined(USE_CURL_NTLM_CORE)
 
+#include "vauth/vauth.h"
+
+struct ntlmdata;
+
 /* Helpers to generate function byte arguments in little endian order */
 #define SHORTPAIR(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff))
 #define LONGQUARTET(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)), \

+ 23 - 20
lib/curl_rtmp.c

@@ -326,51 +326,54 @@ static CURLcode rtmp_disconnect(struct Curl_easy *data,
   return CURLE_OK;
 }
 
-static ssize_t rtmp_recv(struct Curl_easy *data, int sockindex, char *buf,
-                         size_t len, CURLcode *err)
+static CURLcode rtmp_recv(struct Curl_easy *data, int sockindex, char *buf,
+                          size_t len, size_t *pnread)
 {
   struct connectdata *conn = data->conn;
   RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
+  CURLcode result = CURLE_OK;
   ssize_t nread;
 
   (void)sockindex; /* unused */
-  if(!r) {
-    *err = CURLE_FAILED_INIT;
-    return -1;
-  }
+  *pnread = 0;
+  if(!r)
+    return CURLE_FAILED_INIT;
 
   nread = RTMP_Read(r, buf, curlx_uztosi(len));
   if(nread < 0) {
     if(r->m_read.status == RTMP_READ_COMPLETE ||
        r->m_read.status == RTMP_READ_EOF) {
       data->req.size = data->req.bytecount;
-      nread = 0;
     }
     else
-      *err = CURLE_RECV_ERROR;
+      result = CURLE_RECV_ERROR;
   }
-  return nread;
+  else
+    *pnread = (size_t)nread;
+
+  return result;
 }
 
-static ssize_t rtmp_send(struct Curl_easy *data, int sockindex,
-                         const void *buf, size_t len, bool eos, CURLcode *err)
+static CURLcode rtmp_send(struct Curl_easy *data, int sockindex,
+                          const void *buf, size_t len, bool eos,
+                          size_t *pnwritten)
 {
   struct connectdata *conn = data->conn;
   RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
-  ssize_t num;
+  ssize_t nwritten;
 
   (void)sockindex; /* unused */
   (void)eos; /* unused */
-  if(!r) {
-    *err = CURLE_FAILED_INIT;
-    return -1;
-  }
+  *pnwritten = 0;
+  if(!r)
+    return CURLE_FAILED_INIT;
 
-  num = RTMP_Write(r, (const char *)buf, curlx_uztosi(len));
-  if(num < 0)
-    *err = CURLE_SEND_ERROR;
+  nwritten = RTMP_Write(r, (const char *)buf, curlx_uztosi(len));
+  if(nwritten < 0)
+    return CURLE_SEND_ERROR;
 
-  return num;
+  *pnwritten = (size_t)nwritten;
+  return CURLE_OK;
 }
 
 void Curl_rtmp_version(char *version, size_t len)

+ 315 - 224
lib/curl_sasl.c

@@ -76,44 +76,6 @@ static const struct {
   { ZERO_NULL,      0,  0 }
 };
 
-/*
- * Curl_sasl_cleanup()
- *
- * This is used to cleanup any libraries or curl modules used by the sasl
- * functions.
- *
- * Parameters:
- *
- * conn     [in]     - The connection data.
- * authused [in]     - The authentication mechanism used.
- */
-void Curl_sasl_cleanup(struct connectdata *conn, unsigned short authused)
-{
-  (void)conn;
-  (void)authused;
-
-#if defined(USE_KERBEROS5)
-  /* Cleanup the gssapi structure */
-  if(authused == SASL_MECH_GSSAPI) {
-    Curl_auth_cleanup_gssapi(&conn->krb5);
-  }
-#endif
-
-#if defined(USE_GSASL)
-  /* Cleanup the GSASL structure */
-  if(authused & (SASL_MECH_SCRAM_SHA_1 | SASL_MECH_SCRAM_SHA_256)) {
-    Curl_auth_gsasl_cleanup(&conn->gsasl);
-  }
-#endif
-
-#if defined(USE_NTLM)
-  /* Cleanup the NTLM structure */
-  if(authused == SASL_MECH_NTLM) {
-    Curl_auth_cleanup_ntlm(&conn->ntlm);
-  }
-#endif
-}
-
 /*
  * Curl_sasl_decode_mech()
  *
@@ -334,6 +296,241 @@ bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data)
   return FALSE;
 }
 
+struct sasl_ctx {
+  struct SASL *sasl;
+  struct connectdata *conn;
+  const char *user;
+  unsigned short enabledmechs;
+  const char *mech;
+  saslstate state1;
+  saslstate state2;
+  struct bufref resp;
+  CURLcode result;
+};
+
+static bool sasl_choose_external(struct Curl_easy *data, struct sasl_ctx *sctx)
+{
+  if((sctx->enabledmechs & SASL_MECH_EXTERNAL) && !sctx->conn->passwd[0]) {
+    sctx->mech = SASL_MECH_STRING_EXTERNAL;
+    sctx->state1 = SASL_EXTERNAL;
+    sctx->sasl->authused = SASL_MECH_EXTERNAL;
+
+    if(sctx->sasl->force_ir || data->set.sasl_ir)
+      Curl_auth_create_external_message(sctx->conn->user, &sctx->resp);
+    return TRUE;
+  }
+  return FALSE;
+}
+
+#ifdef USE_KERBEROS5
+static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx)
+{
+  if(sctx->user &&
+     (sctx->enabledmechs & SASL_MECH_GSSAPI) &&
+     Curl_auth_is_gssapi_supported() &&
+     Curl_auth_user_contains_domain(sctx->conn->user)) {
+    const char *service = data->set.str[STRING_SERVICE_NAME] ?
+      data->set.str[STRING_SERVICE_NAME] :
+      sctx->sasl->params->service;
+
+    sctx->sasl->mutual_auth = FALSE;
+    sctx->mech = SASL_MECH_STRING_GSSAPI;
+    sctx->state1 = SASL_GSSAPI;
+    sctx->state2 = SASL_GSSAPI_TOKEN;
+    sctx->sasl->authused = SASL_MECH_GSSAPI;
+
+    if(sctx->sasl->force_ir || data->set.sasl_ir) {
+      struct kerberos5data *krb5 = Curl_auth_krb5_get(sctx->conn);
+      sctx->result = !krb5 ? CURLE_OUT_OF_MEMORY :
+        Curl_auth_create_gssapi_user_message(data, sctx->conn->user,
+                                             sctx->conn->passwd,
+                                             service, sctx->conn->host.name,
+                                             sctx->sasl->mutual_auth, NULL,
+                                             krb5, &sctx->resp);
+    }
+    return TRUE;
+  }
+  return FALSE;
+}
+#endif /* USE_KERBEROS5 */
+
+#ifdef USE_GSASL
+static bool sasl_choose_gsasl(struct Curl_easy *data, struct sasl_ctx *sctx)
+{
+  struct gsasldata *gsasl;
+  struct bufref nullmsg;
+
+  if(sctx->user &&
+     (sctx->enabledmechs & (SASL_MECH_SCRAM_SHA_256|SASL_MECH_SCRAM_SHA_1))) {
+    gsasl = Curl_auth_gsasl_get(sctx->conn);
+    if(!gsasl) {
+      sctx->result = CURLE_OUT_OF_MEMORY;
+      return TRUE; /* attempted, but failed */
+    }
+
+    if((sctx->enabledmechs & SASL_MECH_SCRAM_SHA_256) &&
+      Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_256,
+                                   gsasl)) {
+      sctx->mech = SASL_MECH_STRING_SCRAM_SHA_256;
+      sctx->sasl->authused = SASL_MECH_SCRAM_SHA_256;
+    }
+    else if((sctx->enabledmechs & SASL_MECH_SCRAM_SHA_1) &&
+      Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_1,
+                                   gsasl)) {
+      sctx->mech = SASL_MECH_STRING_SCRAM_SHA_1;
+      sctx->sasl->authused = SASL_MECH_SCRAM_SHA_1;
+    }
+    else
+      return FALSE;
+
+    Curl_bufref_init(&nullmsg);
+    sctx->state1 = SASL_GSASL;
+    sctx->state2 = SASL_GSASL;
+    sctx->result = Curl_auth_gsasl_start(data, sctx->conn->user,
+                                         sctx->conn->passwd, gsasl);
+    if(!sctx->result && (sctx->sasl->force_ir || data->set.sasl_ir))
+      sctx->result = Curl_auth_gsasl_token(data, &nullmsg, gsasl, &sctx->resp);
+    return TRUE;
+  }
+  return FALSE;
+}
+
+#endif /* USE_GSASL */
+
+#ifndef CURL_DISABLE_DIGEST_AUTH
+static bool sasl_choose_digest(struct Curl_easy *data, struct sasl_ctx *sctx)
+{
+  (void)data;
+  if(!sctx->user)
+    return FALSE;
+  else if((sctx->enabledmechs & SASL_MECH_DIGEST_MD5) &&
+     Curl_auth_is_digest_supported()) {
+    sctx->mech = SASL_MECH_STRING_DIGEST_MD5;
+    sctx->state1 = SASL_DIGESTMD5;
+    sctx->sasl->authused = SASL_MECH_DIGEST_MD5;
+    return TRUE;
+  }
+  else if(sctx->enabledmechs & SASL_MECH_CRAM_MD5) {
+    sctx->mech = SASL_MECH_STRING_CRAM_MD5;
+    sctx->state1 = SASL_CRAMMD5;
+    sctx->sasl->authused = SASL_MECH_CRAM_MD5;
+    return TRUE;
+  }
+  return FALSE;
+}
+#endif /* !CURL_DISABLE_DIGEST_AUTH */
+
+#ifdef USE_NTLM
+static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx)
+{
+  if(!sctx->user)
+    return FALSE;
+  else if((sctx->enabledmechs & SASL_MECH_NTLM) &&
+          Curl_auth_is_ntlm_supported()) {
+    const char *service = data->set.str[STRING_SERVICE_NAME] ?
+      data->set.str[STRING_SERVICE_NAME] :
+      sctx->sasl->params->service;
+    const char *hostname;
+    int port;
+
+    Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port);
+
+    sctx->mech = SASL_MECH_STRING_NTLM;
+    sctx->state1 = SASL_NTLM;
+    sctx->state2 = SASL_NTLM_TYPE2MSG;
+    sctx->sasl->authused = SASL_MECH_NTLM;
+
+    if(sctx->sasl->force_ir || data->set.sasl_ir) {
+      struct ntlmdata *ntlm = Curl_auth_ntlm_get(sctx->conn, FALSE);
+      sctx->result = !ntlm ? CURLE_OUT_OF_MEMORY :
+        Curl_auth_create_ntlm_type1_message(data,
+                                            sctx->conn->user,
+                                            sctx->conn->passwd,
+                                            service, hostname,
+                                            ntlm, &sctx->resp);
+    }
+    return TRUE;
+  }
+  return FALSE;
+}
+#endif /* USE_NTLM */
+
+static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx)
+{
+  const char *oauth_bearer = data->set.str[STRING_BEARER];
+
+  if(sctx->user && oauth_bearer &&
+     (sctx->enabledmechs & SASL_MECH_OAUTHBEARER)) {
+    const char *hostname;
+    int port;
+    Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port);
+
+    sctx->mech = SASL_MECH_STRING_OAUTHBEARER;
+    sctx->state1 = SASL_OAUTH2;
+    sctx->state2 = SASL_OAUTH2_RESP;
+    sctx->sasl->authused = SASL_MECH_OAUTHBEARER;
+
+    if(sctx->sasl->force_ir || data->set.sasl_ir)
+      sctx->result =
+        Curl_auth_create_oauth_bearer_message(sctx->conn->user,
+                                              hostname, port,
+                                              oauth_bearer, &sctx->resp);
+    return TRUE;
+  }
+  return FALSE;
+}
+
+static bool sasl_choose_oauth2(struct Curl_easy *data, struct sasl_ctx *sctx)
+{
+  const char *oauth_bearer = data->set.str[STRING_BEARER];
+
+  if(sctx->user && oauth_bearer &&
+     (sctx->enabledmechs & SASL_MECH_XOAUTH2)) {
+    sctx->mech = SASL_MECH_STRING_XOAUTH2;
+    sctx->state1 = SASL_OAUTH2;
+    sctx->sasl->authused = SASL_MECH_XOAUTH2;
+
+    if(sctx->sasl->force_ir || data->set.sasl_ir)
+      sctx->result = Curl_auth_create_xoauth_bearer_message(sctx->conn->user,
+                                                      oauth_bearer,
+                                                      &sctx->resp);
+    return TRUE;
+  }
+  return FALSE;
+}
+
+static bool sasl_choose_plain(struct Curl_easy *data, struct sasl_ctx *sctx)
+{
+  if(sctx->user && (sctx->enabledmechs & SASL_MECH_PLAIN)) {
+    sctx->mech = SASL_MECH_STRING_PLAIN;
+    sctx->state1 = SASL_PLAIN;
+    sctx->sasl->authused = SASL_MECH_PLAIN;
+
+    if(sctx->sasl->force_ir || data->set.sasl_ir)
+      sctx->result =
+        Curl_auth_create_plain_message(sctx->conn->sasl_authzid,
+                                       sctx->conn->user, sctx->conn->passwd,
+                                       &sctx->resp);
+    return TRUE;
+  }
+  return FALSE;
+}
+
+static bool sasl_choose_login(struct Curl_easy *data, struct sasl_ctx *sctx)
+{
+  if(sctx->user && (sctx->enabledmechs & SASL_MECH_LOGIN)) {
+    sctx->mech = SASL_MECH_STRING_LOGIN;
+    sctx->state1 = SASL_LOGIN;
+    sctx->state2 = SASL_LOGIN_PASSWD;
+    sctx->sasl->authused = SASL_MECH_LOGIN;
+
+    if(sctx->sasl->force_ir || data->set.sasl_ir)
+      Curl_auth_create_login_message(sctx->conn->user, &sctx->resp);
+    return TRUE;
+  }
+  return FALSE;
+}
+
 /*
  * Curl_sasl_start()
  *
@@ -342,185 +539,66 @@ bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data)
 CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
                          bool force_ir, saslprogress *progress)
 {
-  CURLcode result = CURLE_OK;
-  struct connectdata *conn = data->conn;
-  unsigned short enabledmechs;
-  const char *mech = NULL;
-  struct bufref resp;
-  saslstate state1 = SASL_STOP;
-  saslstate state2 = SASL_FINAL;
-  const char *hostname, *disp_hostname;
-  int port;
-#if defined(USE_KERBEROS5) || defined(USE_NTLM)
-  const char *service = data->set.str[STRING_SERVICE_NAME] ?
-    data->set.str[STRING_SERVICE_NAME] :
-    sasl->params->service;
-#endif
-  const char *oauth_bearer = data->set.str[STRING_BEARER];
-  struct bufref nullmsg;
+  struct sasl_ctx sctx;
 
-  Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port);
-  Curl_bufref_init(&nullmsg);
-  Curl_bufref_init(&resp);
   sasl->force_ir = force_ir;    /* Latch for future use */
   sasl->authused = 0;           /* No mechanism used yet */
-  enabledmechs = sasl->authmechs & sasl->prefmech;
   *progress = SASL_IDLE;
 
+  memset(&sctx, 0, sizeof(sctx));
+  sctx.sasl = sasl;
+  sctx.conn = data->conn;
+  sctx.user = data->state.aptr.user;
+  Curl_bufref_init(&sctx.resp);
+  sctx.enabledmechs = sasl->authmechs & sasl->prefmech;
+  sctx.state1 = SASL_STOP;
+  sctx.state2 = SASL_FINAL;
+
   /* Calculate the supported authentication mechanism, by decreasing order of
      security, as well as the initial response where appropriate */
-  if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) {
-    mech = SASL_MECH_STRING_EXTERNAL;
-    state1 = SASL_EXTERNAL;
-    sasl->authused = SASL_MECH_EXTERNAL;
-
-    if(force_ir || data->set.sasl_ir)
-      Curl_auth_create_external_message(conn->user, &resp);
-  }
-  else if(data->state.aptr.user) {
+  if(sasl_choose_external(data, &sctx) ||
 #if defined(USE_KERBEROS5)
-    if((enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() &&
-       Curl_auth_user_contains_domain(conn->user)) {
-      sasl->mutual_auth = FALSE;
-      mech = SASL_MECH_STRING_GSSAPI;
-      state1 = SASL_GSSAPI;
-      state2 = SASL_GSSAPI_TOKEN;
-      sasl->authused = SASL_MECH_GSSAPI;
-
-      if(force_ir || data->set.sasl_ir)
-        result = Curl_auth_create_gssapi_user_message(data, conn->user,
-                                                      conn->passwd,
-                                                      service,
-                                                      conn->host.name,
-                                                      sasl->mutual_auth,
-                                                      NULL, &conn->krb5,
-                                                      &resp);
-    }
-    else
+     sasl_choose_krb5(data, &sctx) ||
 #endif
 #ifdef USE_GSASL
-    if((enabledmechs & SASL_MECH_SCRAM_SHA_256) &&
-       Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_256,
-                                    &conn->gsasl)) {
-      mech = SASL_MECH_STRING_SCRAM_SHA_256;
-      sasl->authused = SASL_MECH_SCRAM_SHA_256;
-      state1 = SASL_GSASL;
-      state2 = SASL_GSASL;
-
-      result = Curl_auth_gsasl_start(data, conn->user,
-                                     conn->passwd, &conn->gsasl);
-      if(result == CURLE_OK && (force_ir || data->set.sasl_ir))
-        result = Curl_auth_gsasl_token(data, &nullmsg, &conn->gsasl, &resp);
-    }
-    else if((enabledmechs & SASL_MECH_SCRAM_SHA_1) &&
-            Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_1,
-                                         &conn->gsasl)) {
-      mech = SASL_MECH_STRING_SCRAM_SHA_1;
-      sasl->authused = SASL_MECH_SCRAM_SHA_1;
-      state1 = SASL_GSASL;
-      state2 = SASL_GSASL;
-
-      result = Curl_auth_gsasl_start(data, conn->user,
-                                     conn->passwd, &conn->gsasl);
-      if(result == CURLE_OK && (force_ir || data->set.sasl_ir))
-        result = Curl_auth_gsasl_token(data, &nullmsg, &conn->gsasl, &resp);
-    }
-    else
+     sasl_choose_gsasl(data, &sctx) ||
 #endif
 #ifndef CURL_DISABLE_DIGEST_AUTH
-    if((enabledmechs & SASL_MECH_DIGEST_MD5) &&
-       Curl_auth_is_digest_supported()) {
-      mech = SASL_MECH_STRING_DIGEST_MD5;
-      state1 = SASL_DIGESTMD5;
-      sasl->authused = SASL_MECH_DIGEST_MD5;
-    }
-    else if(enabledmechs & SASL_MECH_CRAM_MD5) {
-      mech = SASL_MECH_STRING_CRAM_MD5;
-      state1 = SASL_CRAMMD5;
-      sasl->authused = SASL_MECH_CRAM_MD5;
-    }
-    else
+     sasl_choose_digest(data, &sctx) ||
 #endif
 #ifdef USE_NTLM
-    if((enabledmechs & SASL_MECH_NTLM) && Curl_auth_is_ntlm_supported()) {
-      mech = SASL_MECH_STRING_NTLM;
-      state1 = SASL_NTLM;
-      state2 = SASL_NTLM_TYPE2MSG;
-      sasl->authused = SASL_MECH_NTLM;
-
-      if(force_ir || data->set.sasl_ir)
-        result = Curl_auth_create_ntlm_type1_message(data,
-                                                     conn->user, conn->passwd,
-                                                     service,
-                                                     hostname,
-                                                     &conn->ntlm, &resp);
-      }
-    else
+     sasl_choose_ntlm(data, &sctx) ||
 #endif
-    if((enabledmechs & SASL_MECH_OAUTHBEARER) && oauth_bearer) {
-      mech = SASL_MECH_STRING_OAUTHBEARER;
-      state1 = SASL_OAUTH2;
-      state2 = SASL_OAUTH2_RESP;
-      sasl->authused = SASL_MECH_OAUTHBEARER;
-
-      if(force_ir || data->set.sasl_ir)
-        result = Curl_auth_create_oauth_bearer_message(conn->user,
-                                                       hostname,
-                                                       port,
-                                                       oauth_bearer,
-                                                       &resp);
-    }
-    else if((enabledmechs & SASL_MECH_XOAUTH2) && oauth_bearer) {
-      mech = SASL_MECH_STRING_XOAUTH2;
-      state1 = SASL_OAUTH2;
-      sasl->authused = SASL_MECH_XOAUTH2;
-
-      if(force_ir || data->set.sasl_ir)
-        result = Curl_auth_create_xoauth_bearer_message(conn->user,
-                                                        oauth_bearer,
-                                                        &resp);
-    }
-    else if(enabledmechs & SASL_MECH_PLAIN) {
-      mech = SASL_MECH_STRING_PLAIN;
-      state1 = SASL_PLAIN;
-      sasl->authused = SASL_MECH_PLAIN;
-
-      if(force_ir || data->set.sasl_ir)
-        result = Curl_auth_create_plain_message(conn->sasl_authzid,
-                                                conn->user, conn->passwd,
-                                                &resp);
-    }
-    else if(enabledmechs & SASL_MECH_LOGIN) {
-      mech = SASL_MECH_STRING_LOGIN;
-      state1 = SASL_LOGIN;
-      state2 = SASL_LOGIN_PASSWD;
-      sasl->authused = SASL_MECH_LOGIN;
-
-      if(force_ir || data->set.sasl_ir)
-        Curl_auth_create_login_message(conn->user, &resp);
-    }
+     sasl_choose_oauth(data, &sctx) ||
+     sasl_choose_oauth2(data, &sctx) ||
+     sasl_choose_plain(data, &sctx) ||
+     sasl_choose_login(data, &sctx)) {
+    /* selected, either we have a mechanism or a failure */
+    DEBUGASSERT(sctx.mech || sctx.result);
   }
 
-  if(!result && mech) {
-    sasl->curmech = mech;
-    if(Curl_bufref_ptr(&resp))
-      result = build_message(sasl, &resp);
+  if(!sctx.result && sctx.mech) {
+    sasl->curmech = sctx.mech;
+    if(Curl_bufref_ptr(&sctx.resp))
+      sctx.result = build_message(sasl, &sctx.resp);
 
     if(sasl->params->maxirlen &&
-       strlen(mech) + Curl_bufref_len(&resp) > sasl->params->maxirlen)
-      Curl_bufref_free(&resp);
+       strlen(sctx.mech) + Curl_bufref_len(&sctx.resp) >
+         sasl->params->maxirlen)
+      Curl_bufref_free(&sctx.resp);
 
-    if(!result)
-      result = sasl->params->sendauth(data, mech, &resp);
+    if(!sctx.result)
+      sctx.result = sasl->params->sendauth(data, sctx.mech, &sctx.resp);
 
-    if(!result) {
+    if(!sctx.result) {
       *progress = SASL_INPROGRESS;
-      sasl_state(sasl, data, Curl_bufref_ptr(&resp) ? state2 : state1);
+      sasl_state(sasl, data, Curl_bufref_ptr(&sctx.resp) ?
+                 sctx.state2 : sctx.state1);
     }
   }
 
-  Curl_bufref_free(&resp);
-  return result;
+  Curl_bufref_free(&sctx.resp);
+  return sctx.result;
 }
 
 /*
@@ -535,7 +613,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
   struct connectdata *conn = data->conn;
   saslstate newstate = SASL_FINAL;
   struct bufref resp;
-  const char *hostname, *disp_hostname;
+  const char *hostname;
   int port;
 #if defined(USE_KERBEROS5) || defined(USE_NTLM) \
     || !defined(CURL_DISABLE_DIGEST_AUTH)
@@ -546,7 +624,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
   const char *oauth_bearer = data->set.str[STRING_BEARER];
   struct bufref serverdata;
 
-  Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port);
+  Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port);
   Curl_bufref_init(&serverdata);
   Curl_bufref_init(&resp);
   *progress = SASL_INPROGRESS;
@@ -587,8 +665,11 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
 #ifdef USE_GSASL
   case SASL_GSASL:
     result = get_server_message(sasl, data, &serverdata);
-    if(!result)
-      result = Curl_auth_gsasl_token(data, &serverdata, &conn->gsasl, &resp);
+    if(!result) {
+      struct gsasldata *gsasl = Curl_auth_gsasl_get(conn);
+      result = !gsasl ? CURLE_OUT_OF_MEMORY :
+        Curl_auth_gsasl_token(data, &serverdata, gsasl, &resp);
+    }
     if(!result && Curl_bufref_len(&resp) > 0)
       newstate = SASL_GSASL;
     break;
@@ -615,50 +696,57 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
 #endif
 
 #ifdef USE_NTLM
-  case SASL_NTLM:
+  case SASL_NTLM: {
     /* Create the type-1 message */
-    result = Curl_auth_create_ntlm_type1_message(data,
-                                                 conn->user, conn->passwd,
-                                                 service, hostname,
-                                                 &conn->ntlm, &resp);
+    struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, FALSE);
+    result = !ntlm ? CURLE_OUT_OF_MEMORY :
+      Curl_auth_create_ntlm_type1_message(data,
+                                          conn->user, conn->passwd,
+                                          service, hostname,
+                                          ntlm, &resp);
     newstate = SASL_NTLM_TYPE2MSG;
     break;
-  case SASL_NTLM_TYPE2MSG:
+  }
+  case SASL_NTLM_TYPE2MSG: {
     /* Decode the type-2 message */
-    result = get_server_message(sasl, data, &serverdata);
+    struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, FALSE);
+    result = !ntlm ? CURLE_FAILED_INIT :
+      get_server_message(sasl, data, &serverdata);
     if(!result)
-      result = Curl_auth_decode_ntlm_type2_message(data, &serverdata,
-                                                   &conn->ntlm);
+      result = Curl_auth_decode_ntlm_type2_message(data, &serverdata, ntlm);
     if(!result)
       result = Curl_auth_create_ntlm_type3_message(data, conn->user,
-                                                   conn->passwd, &conn->ntlm,
+                                                   conn->passwd, ntlm,
                                                    &resp);
     break;
+  }
 #endif
 
 #if defined(USE_KERBEROS5)
-  case SASL_GSSAPI:
-    result = Curl_auth_create_gssapi_user_message(data, conn->user,
-                                                  conn->passwd,
-                                                  service,
-                                                  conn->host.name,
+  case SASL_GSSAPI: {
+    struct kerberos5data *krb5 = Curl_auth_krb5_get(conn);
+    result = !krb5 ? CURLE_OUT_OF_MEMORY :
+      Curl_auth_create_gssapi_user_message(data, conn->user, conn->passwd,
+                                                  service, conn->host.name,
                                                   sasl->mutual_auth, NULL,
-                                                  &conn->krb5,
-                                                  &resp);
+                                                  krb5, &resp);
     newstate = SASL_GSSAPI_TOKEN;
     break;
+  }
   case SASL_GSSAPI_TOKEN:
     result = get_server_message(sasl, data, &serverdata);
     if(!result) {
-      if(sasl->mutual_auth) {
+      struct kerberos5data *krb5 = Curl_auth_krb5_get(conn);
+      if(!krb5)
+        result = CURLE_OUT_OF_MEMORY;
+      else if(sasl->mutual_auth) {
         /* Decode the user token challenge and create the optional response
            message */
         result = Curl_auth_create_gssapi_user_message(data, NULL, NULL,
                                                       NULL, NULL,
                                                       sasl->mutual_auth,
                                                       &serverdata,
-                                                      &conn->krb5,
-                                                      &resp);
+                                                      krb5, &resp);
         newstate = SASL_GSSAPI_NO_DATA;
       }
       else
@@ -666,19 +754,22 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
         result = Curl_auth_create_gssapi_security_message(data,
                                                           conn->sasl_authzid,
                                                           &serverdata,
-                                                          &conn->krb5,
-                                                          &resp);
+                                                          krb5, &resp);
     }
     break;
   case SASL_GSSAPI_NO_DATA:
     /* Decode the security challenge and create the response message */
     result = get_server_message(sasl, data, &serverdata);
-    if(!result)
-      result = Curl_auth_create_gssapi_security_message(data,
-                                                        conn->sasl_authzid,
-                                                        &serverdata,
-                                                        &conn->krb5,
-                                                        &resp);
+    if(!result) {
+      struct kerberos5data *krb5 = Curl_auth_krb5_get(conn);
+      if(!krb5)
+        result = CURLE_OUT_OF_MEMORY;
+      else
+        result = Curl_auth_create_gssapi_security_message(data,
+                                                          conn->sasl_authzid,
+                                                          &serverdata,
+                                                          krb5, &resp);
+    }
     break;
 #endif
 

+ 0 - 4
lib/curl_sasl.h

@@ -135,10 +135,6 @@ struct SASL {
   (wordlen == (sizeof(mech) - 1) / sizeof(char) && \
    !memcmp(line, mech, wordlen))
 
-/* This is used to cleanup any libraries or curl modules used by the sasl
-   functions */
-void Curl_sasl_cleanup(struct connectdata *conn, unsigned short authused);
-
 /* Convert a mechanism name to a token */
 unsigned short Curl_sasl_decode_mech(const char *ptr,
                                      size_t maxlen, size_t *len);

+ 51 - 71
lib/curl_setup.h

@@ -224,46 +224,46 @@
 
 #ifdef HTTP_ONLY
 #  ifndef CURL_DISABLE_DICT
-#    define CURL_DISABLE_DICT
+#  define CURL_DISABLE_DICT
 #  endif
 #  ifndef CURL_DISABLE_FILE
-#    define CURL_DISABLE_FILE
+#  define CURL_DISABLE_FILE
 #  endif
 #  ifndef CURL_DISABLE_FTP
-#    define CURL_DISABLE_FTP
+#  define CURL_DISABLE_FTP
 #  endif
 #  ifndef CURL_DISABLE_GOPHER
-#    define CURL_DISABLE_GOPHER
+#  define CURL_DISABLE_GOPHER
 #  endif
 #  ifndef CURL_DISABLE_IMAP
-#    define CURL_DISABLE_IMAP
+#  define CURL_DISABLE_IMAP
 #  endif
 #  ifndef CURL_DISABLE_LDAP
-#    define CURL_DISABLE_LDAP
+#  define CURL_DISABLE_LDAP
 #  endif
 #  ifndef CURL_DISABLE_LDAPS
-#    define CURL_DISABLE_LDAPS
+#  define CURL_DISABLE_LDAPS
 #  endif
 #  ifndef CURL_DISABLE_MQTT
-#    define CURL_DISABLE_MQTT
+#  define CURL_DISABLE_MQTT
 #  endif
 #  ifndef CURL_DISABLE_POP3
-#    define CURL_DISABLE_POP3
+#  define CURL_DISABLE_POP3
 #  endif
 #  ifndef CURL_DISABLE_RTSP
-#    define CURL_DISABLE_RTSP
+#  define CURL_DISABLE_RTSP
 #  endif
 #  ifndef CURL_DISABLE_SMB
-#    define CURL_DISABLE_SMB
+#  define CURL_DISABLE_SMB
 #  endif
 #  ifndef CURL_DISABLE_SMTP
-#    define CURL_DISABLE_SMTP
+#  define CURL_DISABLE_SMTP
 #  endif
 #  ifndef CURL_DISABLE_TELNET
-#    define CURL_DISABLE_TELNET
+#  define CURL_DISABLE_TELNET
 #  endif
 #  ifndef CURL_DISABLE_TFTP
-#    define CURL_DISABLE_TFTP
+#  define CURL_DISABLE_TFTP
 #  endif
 #endif
 
@@ -470,62 +470,46 @@
 #include <curl/stdcheaders.h>
 #endif
 
-/*
- * Large file (>2Gb) support using Win32 functions.
- */
-
-#ifdef USE_WIN32_LARGE_FILES
+#ifdef _WIN32
 #  ifdef HAVE_IO_H
 #  include <io.h>
 #  endif
 #  include <sys/types.h>
 #  include <sys/stat.h>
-#  undef  lseek
-#  define lseek(fdes,offset,whence)  _lseeki64(fdes, offset, whence)
-#  undef  fstat
-#  define fstat(fdes,stp)            _fstati64(fdes, stp)
-#  undef  stat
-#  define stat(fname,stp)            curlx_win32_stat(fname, stp)
-#  define struct_stat                struct _stati64
-#  define LSEEK_ERROR                (__int64)-1
-#  define open                       curlx_win32_open
-#  define fopen(fname,mode)          curlx_win32_fopen(fname, mode)
-   int curlx_win32_open(const char *filename, int oflag, ...);
-   int curlx_win32_stat(const char *path, struct_stat *buffer);
-   FILE *curlx_win32_fopen(const char *filename, const char *mode);
-#endif
-
-#ifdef __DJGPP__
-/* Requires DJGPP 2.04 */
-#  include <unistd.h>
-#  undef  lseek
-#  define lseek(fdes,offset,whence)  llseek(fdes, offset, whence)
-#  define LSEEK_ERROR                (offset_t)-1
-#endif
-
-/*
- * Small file (<2Gb) support using Win32 functions.
- */
-
-#if defined(_WIN32) && !defined(USE_WIN32_LARGE_FILES)
-#  ifdef HAVE_IO_H
-#  include <io.h>
+#  ifdef USE_WIN32_LARGE_FILES
+     /* Large file (>2Gb) support using Win32 functions. */
+#    undef  lseek
+#    define lseek(fdes, offset, whence)  _lseeki64(fdes, offset, whence)
+#    undef  fstat
+#    define fstat(fdes,stp)              _fstati64(fdes, stp)
+#    undef  stat
+#    define struct_stat                  struct _stati64
+#    define LSEEK_ERROR                  (__int64)-1
+#  else
+     /* Small file (<2Gb) support using Win32 functions. */
+#    ifndef UNDER_CE
+#      undef  lseek
+#      define lseek(fdes, offset, whence)  _lseek(fdes, (long)offset, whence)
+#      define fstat(fdes, stp)             _fstat(fdes, stp)
+#      define struct_stat                  struct _stat
+#    endif
+#    define LSEEK_ERROR                  (long)-1
 #  endif
-#  include <sys/types.h>
-#  include <sys/stat.h>
 #  ifndef UNDER_CE
-#    undef  lseek
-#    define lseek(fdes,offset,whence)  _lseek(fdes, (long)offset, whence)
-#    define fstat(fdes,stp)            _fstat(fdes, stp)
-#    define stat(fname,stp)            curlx_win32_stat(fname, stp)
-#    define struct_stat                struct _stat
-#    define open                       curlx_win32_open
-#    define fopen(fname,mode)          curlx_win32_fopen(fname, mode)
      int curlx_win32_stat(const char *path, struct_stat *buffer);
      int curlx_win32_open(const char *filename, int oflag, ...);
      FILE *curlx_win32_fopen(const char *filename, const char *mode);
+#    define stat(fname, stp)           curlx_win32_stat(fname, stp)
+#    define open                       curlx_win32_open
+#    define CURL_FOPEN(fname, mode)    curlx_win32_fopen(fname, mode)
+#    define fopen(fname, mode)         CURL_FOPEN(fname, mode)
 #  endif
-#  define LSEEK_ERROR                (long)-1
+#elif defined(__DJGPP__)
+   /* Requires DJGPP 2.04 */
+#  include <unistd.h>
+#  undef  lseek
+#  define lseek(fdes,offset,whence)  llseek(fdes, offset, whence)
+#  define LSEEK_ERROR                (offset_t)-1
 #endif
 
 #ifndef struct_stat
@@ -582,7 +566,7 @@
 #    endif
 #  endif
 #  ifndef SIZEOF_OFF_T
-#    define SIZEOF_OFF_T 4
+#  define SIZEOF_OFF_T 4
 #  endif
 #endif
 
@@ -590,9 +574,9 @@
 #error "too small curl_off_t"
 #else
    /* assume SIZEOF_CURL_OFF_T == 8 */
-#  define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF)
+#  define CURL_OFF_T_MAX 0x7FFFFFFFFFFFFFFF
 #endif
-#define CURL_OFF_T_MIN (-CURL_OFF_T_MAX - CURL_OFF_T_C(1))
+#define CURL_OFF_T_MIN (-CURL_OFF_T_MAX - 1)
 
 #if (SIZEOF_CURL_OFF_T != 8)
 #  error "curl_off_t must be exactly 64 bits"
@@ -677,12 +661,8 @@
 #    define select(n,r,w,x,t) select_s(n,r,w,x,t)
 #    define ioctl(x,y,z) ioctlsocket(x,y,(char *)(z))
 #    include <tcp.h>
-#    ifdef word
-#      undef word
-#    endif
-#    ifdef byte
-#      undef byte
-#    endif
+#    undef word
+#    undef byte
 
 #  endif /* MSDOS */
 
@@ -739,8 +719,8 @@
 #endif
 
 #if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \
-  defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
-  defined(USE_BEARSSL) || defined(USE_RUSTLS)
+  defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \
+  defined(USE_RUSTLS)
 #define USE_SSL    /* SSL support has been enabled */
 #endif
 
@@ -775,7 +755,7 @@
 /* Single point where USE_NTLM definition might be defined */
 #ifndef CURL_DISABLE_NTLM
 #  if defined(USE_OPENSSL) || defined(USE_MBEDTLS) ||                   \
-  defined(USE_GNUTLS) || defined(USE_SECTRANSP) ||                      \
+  defined(USE_GNUTLS) ||                                                \
   defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) ||              \
   (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT))
 #    define USE_CURL_NTLM_CORE

+ 9 - 9
lib/curl_setup_once.h

@@ -41,11 +41,9 @@
 #include <sys/types.h>
 #endif
 
-#ifdef HAVE_SYS_STAT_H
 #include <sys/stat.h>
-#endif
 
-#ifdef HAVE_SYS_TIME_H
+#if !defined(_WIN32) || defined(__MINGW32__)
 #include <sys/time.h>
 #endif
 
@@ -96,7 +94,7 @@
 #  endif
 #endif
 
-#ifdef HAVE_SYS_SOCKET_H
+#ifndef _WIN32
 #include <sys/socket.h>
 #endif
 
@@ -197,17 +195,19 @@ struct timeval {
  */
 
 #ifdef HAVE_CLOSESOCKET
-#  define sclose(x)  closesocket((x))
+#  define CURL_SCLOSE(x)  closesocket((x))
 #elif defined(HAVE_CLOSESOCKET_CAMEL)
-#  define sclose(x)  CloseSocket((x))
+#  define CURL_SCLOSE(x)  CloseSocket((x))
 #elif defined(MSDOS)  /* Watt-32 */
-#  define sclose(x)  close_s((x))
+#  define CURL_SCLOSE(x)  close_s((x))
 #elif defined(USE_LWIPSOCK)
-#  define sclose(x)  lwip_close((x))
+#  define CURL_SCLOSE(x)  lwip_close((x))
 #else
-#  define sclose(x)  close((x))
+#  define CURL_SCLOSE(x)  close((x))
 #endif
 
+#define sclose(x)  CURL_SCLOSE(x)
+
 /*
  * Stack-independent version of fcntl() on sockets:
  */

+ 0 - 2
lib/curl_sha512_256.c

@@ -34,9 +34,7 @@
  * * GnuTLS
  * * wolfSSL
  * * Schannel SSPI
- * * Secure Transport (Darwin)
  * * mbedTLS
- * * BearSSL
  * * Rustls
  * Skip the backend if it does not support the required algorithm */
 

+ 8 - 43
lib/curl_sspi.c

@@ -28,6 +28,7 @@
 
 #include <curl/curl.h>
 #include "curl_sspi.h"
+#include "strdup.h"
 #include "curlx/multibyte.h"
 #include "system_win32.h"
 #include "curlx/version_win32.h"
@@ -37,23 +38,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-/* We use our own typedef here since some headers might lack these */
-typedef PSecurityFunctionTable (APIENTRY *INITSECURITYINTERFACE_FN)(VOID);
-
-/* See definition of SECURITY_ENTRYPOINT in sspi.h */
-#ifdef UNICODE
-#  ifdef UNDER_CE
-#    define SECURITYENTRYPOINT L"InitSecurityInterfaceW"
-#  else
-#    define SECURITYENTRYPOINT "InitSecurityInterfaceW"
-#  endif
-#else
-#  define SECURITYENTRYPOINT "InitSecurityInterfaceA"
-#endif
-
-/* Handle of security.dll or secur32.dll, depending on Windows version */
-HMODULE Curl_hSecDll = NULL;
-
 /* Pointer to SSPI dispatch table */
 PSecurityFunctionTable Curl_pSecFn = NULL;
 
@@ -76,31 +60,14 @@ PSecurityFunctionTable Curl_pSecFn = NULL;
  */
 CURLcode Curl_sspi_global_init(void)
 {
-  INITSECURITYINTERFACE_FN pInitSecurityInterface;
-
   /* If security interface is not yet initialized try to do this */
-  if(!Curl_hSecDll) {
-    /* Security Service Provider Interface (SSPI) functions are located in
-     * security.dll on WinNT 4.0 and in secur32.dll on Win9x. Win2K and XP
-     * have both these DLLs (security.dll forwards calls to secur32.dll) */
-
-    /* Load SSPI dll into the address space of the calling process */
-    if(curlx_verify_windows_version(4, 0, 0, PLATFORM_WINNT, VERSION_EQUAL))
-      Curl_hSecDll = Curl_load_library(TEXT("security.dll"));
-    else
-      Curl_hSecDll = Curl_load_library(TEXT("secur32.dll"));
-    if(!Curl_hSecDll)
-      return CURLE_FAILED_INIT;
-
-    /* Get address of the InitSecurityInterfaceA function from the SSPI dll */
-    pInitSecurityInterface =
-      CURLX_FUNCTION_CAST(INITSECURITYINTERFACE_FN,
-                          (GetProcAddress(Curl_hSecDll, SECURITYENTRYPOINT)));
-    if(!pInitSecurityInterface)
-      return CURLE_FAILED_INIT;
-
+  if(!Curl_pSecFn) {
     /* Get pointer to Security Service Provider Interface dispatch table */
-    Curl_pSecFn = pInitSecurityInterface();
+#ifdef __MINGW32CE__
+    Curl_pSecFn = InitSecurityInterfaceW();
+#else
+    Curl_pSecFn = InitSecurityInterface();
+#endif
     if(!Curl_pSecFn)
       return CURLE_FAILED_INIT;
   }
@@ -119,9 +86,7 @@ CURLcode Curl_sspi_global_init(void)
  */
 void Curl_sspi_global_cleanup(void)
 {
-  if(Curl_hSecDll) {
-    FreeLibrary(Curl_hSecDll);
-    Curl_hSecDll = NULL;
+  if(Curl_pSecFn) {
     Curl_pSecFn = NULL;
   }
 }

+ 0 - 1
lib/curl_sspi.h

@@ -57,7 +57,6 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
 void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity);
 
 /* Forward-declaration of global variables defined in curl_sspi.c */
-extern HMODULE Curl_hSecDll;
 extern PSecurityFunctionTable Curl_pSecFn;
 
 /* Provide some definitions missing in old headers */

+ 1 - 2
lib/curl_trc.c

@@ -31,7 +31,6 @@
 #include "easyif.h"
 #include "cfilters.h"
 #include "multiif.h"
-#include "strcase.h"
 
 #include "cf-socket.h"
 #include "connect.h"
@@ -206,7 +205,7 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
 static void trc_infof(struct Curl_easy *data,
                       struct curl_trc_feat *feat,
                       const char *opt_id, int opt_id_idx,
-                      const char * const fmt, va_list ap)  CURL_PRINTF(5, 0);
+                      const char * const fmt, va_list ap) CURL_PRINTF(5, 0);
 
 static void trc_infof(struct Curl_easy *data,
                       struct curl_trc_feat *feat,

+ 14 - 9
lib/vtls/bearssl.h → lib/curlx/binmode.h

@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_BEARSSL_H
-#define HEADER_CURL_BEARSSL_H
+#ifndef HEADER_CURL_TOOL_BINMODE_H
+#define HEADER_CURL_TOOL_BINMODE_H
 /***************************************************************************
  *                                  _   _ ____  _
  *  Project                     ___| | | |  _ \| |
@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) Michael Forney, <[email protected]>
+ * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
  *
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
@@ -23,12 +23,17 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-
 #include "../curl_setup.h"
 
-#ifdef USE_BEARSSL
-
-extern const struct Curl_ssl Curl_ssl_bearssl;
+#if (defined(HAVE_SETMODE) || defined(HAVE__SETMODE)) && defined(O_BINARY)
+/* Requires io.h and/or fcntl.h when available */
+#ifdef HAVE__SETMODE
+#  define CURLX_SET_BINMODE(stream)  (void)_setmode(fileno(stream), O_BINARY)
+#else
+#  define CURLX_SET_BINMODE(stream)  (void)setmode(fileno(stream), O_BINARY)
+#endif
+#else
+#  define CURLX_SET_BINMODE(stream)  (void)stream; Curl_nop_stmt
+#endif
 
-#endif /* USE_BEARSSL */
-#endif /* HEADER_CURL_BEARSSL_H */
+#endif /* HEADER_CURL_TOOL_BINMODE_H */

+ 9 - 0
lib/curlx/curlx.h

@@ -31,6 +31,9 @@
  * be.
  */
 
+#include "binmode.h"
+/* "binmode.h" provides macro CURLX_SET_BINMODE() */
+
 #include "nonblock.h"
 /* "nonblock.h" provides curlx_nonblock() */
 
@@ -65,10 +68,16 @@
 #include "timeval.h"
 #include "timediff.h"
 
+#include "wait.h"
+/* for curlx_wait_ms */
+
 #include "winapi.h"
 /* for curlx_winapi_strerror */
 
 #include "inet_pton.h"
 /* for curlx_inet_pton */
 
+#include "inet_ntop.h"
+/* for curlx_inet_ntop */
+
 #endif /* HEADER_CURL_CURLX_H */

+ 20 - 6
lib/inet_ntop.c → lib/curlx/inet_ntop.c

@@ -20,7 +20,7 @@
  * Original code by Paul Vixie. "curlified" by Gisle Vanem.
  */
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 #ifndef HAVE_INET_NTOP
 
@@ -35,7 +35,6 @@
 #endif
 
 #include "inet_ntop.h"
-#include "curl_printf.h"
 
 #define IN6ADDRSZ       16
 /* #define INADDRSZ         4 */
@@ -65,8 +64,9 @@ static char *inet_ntop4(const unsigned char *src, char *dst, size_t size)
 
   DEBUGASSERT(size >= 16);
 
-  tmp[0] = '\0';
-  (void)msnprintf(tmp, sizeof(tmp), "%d.%d.%d.%d",
+  /* this sprintf() does not overflow the buffer. Avoids snprintf to work more
+     widely. Avoids the msnprintf family to work as a curlx function. */
+  (void)(sprintf)(tmp, "%d.%d.%d.%d",
                   ((int)((unsigned char)src[0])) & 0xff,
                   ((int)((unsigned char)src[1])) & 0xff,
                   ((int)((unsigned char)src[2])) & 0xff,
@@ -162,7 +162,21 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size)
       tp += strlen(tp);
       break;
     }
-    tp += msnprintf(tp, 5, "%x", words[i]);
+    else {
+      /* Lower-case digits. Can't use the set from mprintf.c since this
+         needs to work as a curlx function */
+      static const unsigned char ldigits[] = "0123456789abcdef";
+
+      unsigned int w = words[i];
+      /* output lowercase 16bit hex number but ignore leading zeroes */
+      if(w & 0xf000)
+        *tp++ = ldigits[(w & 0xf000) >> 12];
+      if(w & 0xff00)
+        *tp++ = ldigits[(w & 0x0f00) >> 8];
+      if(w & 0xfff0)
+        *tp++ = ldigits[(w & 0x00f0) >> 4];
+      *tp++ = ldigits[(w & 0x000f)];
+    }
   }
 
   /* Was it a trailing run of 0x00's?
@@ -196,7 +210,7 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size)
  * code. This is to avoid losing the actual last Winsock error. When this
  * function returns NULL, check errno not SOCKERRNO.
  */
-char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size)
+char *curlx_inet_ntop(int af, const void *src, char *buf, size_t size)
 {
   switch(af) {
   case AF_INET:

+ 5 - 5
lib/inet_ntop.h → lib/curlx/inet_ntop.h

@@ -24,26 +24,26 @@
  *
  ***************************************************************************/
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
-char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t size);
+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>
 #endif
-#ifdef HAVE_SYS_SOCKET_H
+#ifndef _WIN32
 #include <sys/socket.h>
 #endif
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
 #ifdef __AMIGA__
-#define Curl_inet_ntop(af,addr,buf,size)                                \
+#define curlx_inet_ntop(af,addr,buf,size)                               \
   (char *)inet_ntop(af, CURL_UNCONST(addr), (unsigned char *)buf,       \
                     (curl_socklen_t)(size))
 #else
-#define Curl_inet_ntop(af,addr,buf,size)                \
+#define curlx_inet_ntop(af,addr,buf,size)                \
   inet_ntop(af, addr, buf, (curl_socklen_t)(size))
 #endif
 #endif

+ 1 - 1
lib/curlx/inet_pton.h

@@ -32,7 +32,7 @@ int curlx_inet_pton(int, const char *, void *);
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #endif
-#ifdef HAVE_SYS_SOCKET_H
+#ifndef _WIN32
 #include <sys/socket.h>
 #endif
 #ifdef HAVE_ARPA_INET_H

+ 9 - 6
lib/curlx/strparse.c

@@ -23,7 +23,10 @@
  ***************************************************************************/
 
 #include "strparse.h"
-#include "../strcase.h"
+
+#ifndef WITHOUT_LIBCURL
+#include <curl/curl.h>  /* for curl_strnequal() */
+#endif
 
 void curlx_str_init(struct Curl_str *out)
 {
@@ -40,7 +43,7 @@ void curlx_str_assign(struct Curl_str *out, const char *str, size_t len)
 /* Get a word until the first DELIM or end of string. At least one byte long.
    return non-zero on error */
 int curlx_str_until(const char **linep, struct Curl_str *out,
-                   const size_t max, char delim)
+                    const size_t max, char delim)
 {
   const char *s = *linep;
   size_t len = 0;
@@ -64,7 +67,7 @@ int curlx_str_until(const char **linep, struct Curl_str *out,
 /* Get a word until the first space or end of string. At least one byte long.
    return non-zero on error */
 int curlx_str_word(const char **linep, struct Curl_str *out,
-                  const size_t max)
+                   const size_t max)
 {
   return curlx_str_until(linep, out, max, ' ');
 }
@@ -72,7 +75,7 @@ int curlx_str_word(const char **linep, struct Curl_str *out,
 /* Get a word until a newline byte or end of string. At least one byte long.
    return non-zero on error */
 int curlx_str_untilnl(const char **linep, struct Curl_str *out,
-                     const size_t max)
+                      const size_t max)
 {
   const char *s = *linep;
   size_t len = 0;
@@ -96,7 +99,7 @@ int curlx_str_untilnl(const char **linep, struct Curl_str *out,
 /* Get a "quoted" word. No escaping possible.
    return non-zero on error */
 int curlx_str_quotedword(const char **linep, struct Curl_str *out,
-                        const size_t max)
+                         const size_t max)
 {
   const char *s = *linep;
   size_t len = 0;
@@ -238,7 +241,7 @@ int curlx_str_newline(const char **linep)
 int curlx_str_casecompare(struct Curl_str *str, const char *check)
 {
   size_t clen = check ? strlen(check) : 0;
-  return ((str->len == clen) && strncasecompare(str->str, check, clen));
+  return ((str->len == clen) && curl_strnequal(str->str, check, clen));
 }
 #endif
 

+ 3 - 3
lib/curlx/strparse.h

@@ -55,17 +55,17 @@ int curlx_str_word(const char **linep, struct Curl_str *out, const size_t max);
 /* Get a word until the first DELIM or end of string
    return non-zero on error */
 int curlx_str_until(const char **linep, struct Curl_str *out, const size_t max,
-                   char delim);
+                    char delim);
 
 /* Get a word until a newline byte or end of string. At least one byte long.
    return non-zero on error */
 int curlx_str_untilnl(const char **linep, struct Curl_str *out,
-                     const size_t max);
+                      const size_t max);
 
 /* Get a "quoted" word. No escaping possible.
    return non-zero on error */
 int curlx_str_quotedword(const char **linep, struct Curl_str *out,
-                        const size_t max);
+                         const size_t max);
 
 /* Advance over a single character.
    return non-zero on error */

+ 97 - 0
lib/curlx/wait.c

@@ -0,0 +1,97 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  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"
+
+#ifndef HAVE_SELECT
+#error "We cannot compile without select() support."
+#endif
+
+#include <limits.h>
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#elif defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
+
+#ifdef MSDOS
+#include <dos.h>  /* delay() */
+#endif
+
+#include "timediff.h"
+#include "wait.h"
+
+/*
+ * Internal function used for waiting a specific amount of ms in
+ * Curl_socket_check() and Curl_poll() when no file descriptor is provided to
+ * wait on, just being used to delay execution. Winsock select() and poll()
+ * timeout mechanisms need a valid socket descriptor in a not null file
+ * descriptor set to work. Waiting indefinitely with this function is not
+ * allowed, a zero or negative timeout value will return immediately. Timeout
+ * resolution, accuracy, as well as maximum supported value is system
+ * dependent, neither factor is a critical issue for the intended use of this
+ * function in the library.
+ *
+ * Return values:
+ *   -1 = system call error, or invalid timeout value
+ *    0 = specified timeout has elapsed, or interrupted
+ */
+int curlx_wait_ms(timediff_t timeout_ms)
+{
+  int r = 0;
+
+  if(!timeout_ms)
+    return 0;
+  if(timeout_ms < 0) {
+    SET_SOCKERRNO(SOCKEINVAL);
+    return -1;
+  }
+#if defined(MSDOS)
+  delay((unsigned int)timeout_ms);
+#elif defined(_WIN32)
+  /* prevent overflow, timeout_ms is typecast to ULONG/DWORD. */
+#if TIMEDIFF_T_MAX >= ULONG_MAX
+  if(timeout_ms >= ULONG_MAX)
+    timeout_ms = ULONG_MAX-1;
+    /* do not use ULONG_MAX, because that is equal to INFINITE */
+#endif
+  Sleep((DWORD)timeout_ms);
+#else
+  /* avoid using poll() for this since it behaves incorrectly with no sockets
+     on Apple operating systems */
+  {
+    struct timeval pending_tv;
+    r = select(0, NULL, NULL, NULL, curlx_mstotv(&pending_tv, timeout_ms));
+  }
+#endif /* _WIN32 */
+  if(r) {
+    if((r == -1) && (SOCKERRNO == SOCKEINTR))
+      /* make EINTR from select or poll not a "lethal" error */
+      r = 0;
+    else
+      r = -1;
+  }
+  return r;
+}

+ 5 - 8
lib/vtls/sectransp.h → lib/curlx/wait.h

@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_SECTRANSP_H
-#define HEADER_CURL_SECTRANSP_H
+#ifndef HEADER_CURL_WAIT_H
+#define HEADER_CURL_WAIT_H
 /***************************************************************************
  *                                  _   _ ____  _
  *  Project                     ___| | | |  _ \| |
@@ -7,7 +7,6 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) Nick Zitzmann, <[email protected]>.
  * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
  *
  * This software is licensed as described in the file COPYING, which
@@ -24,11 +23,9 @@
  * SPDX-License-Identifier: curl
  *
  ***************************************************************************/
-#include "../curl_setup.h"
 
-#ifdef USE_SECTRANSP
+#include "../curl_setup.h"
 
-extern const struct Curl_ssl Curl_ssl_sectransp;
+int curlx_wait_ms(timediff_t timeout_ms);
 
-#endif /* USE_SECTRANSP */
-#endif /* HEADER_CURL_SECTRANSP_H */
+#endif /* HEADER_CURL_WAIT_H */

+ 0 - 23
lib/curlx/warnless.c

@@ -35,11 +35,6 @@
 
 #endif /* __INTEL_COMPILER && __unix__ */
 
-#ifdef _WIN32
-#undef read
-#undef write
-#endif
-
 #include <limits.h>
 
 #define CURL_MASK_UCHAR   ((unsigned char)~0)
@@ -295,21 +290,3 @@ size_t curlx_sitouz(int sinum)
 #  pragma warning(pop)
 #endif
 }
-
-#ifdef _WIN32
-
-ssize_t curlx_read(int fd, void *buf, size_t count)
-{
-  return (ssize_t)read(fd, buf, curlx_uztoui(count));
-}
-
-ssize_t curlx_write(int fd, const void *buf, size_t count)
-{
-  return (ssize_t)write(fd, buf, curlx_uztoui(count));
-}
-
-#endif /* _WIN32 */
-
-/* Ensure that warnless.h redefinitions continue to have an effect
-   in "unity" builds. */
-#undef HEADER_CURL_WARNLESS_H_REDEFS

+ 3 - 16
lib/curlx/warnless.h

@@ -57,24 +57,11 @@ unsigned short curlx_uitous(unsigned int uinum);
 
 size_t curlx_sitouz(int sinum);
 
-#ifdef _WIN32
-
-ssize_t curlx_read(int fd, void *buf, size_t count);
-
-ssize_t curlx_write(int fd, const void *buf, size_t count);
-
-#endif /* _WIN32 */
-
-#endif /* HEADER_CURL_WARNLESS_H */
-
-#ifndef HEADER_CURL_WARNLESS_H_REDEFS
-#define HEADER_CURL_WARNLESS_H_REDEFS
-
 #ifdef _WIN32
 #undef  read
-#define read(fd, buf, count)  curlx_read(fd, buf, count)
+#define read(fd, buf, count)  (ssize_t)_read(fd, buf, curlx_uztoui(count))
 #undef  write
-#define write(fd, buf, count) curlx_write(fd, buf, count)
+#define write(fd, buf, count) (ssize_t)_write(fd, buf, curlx_uztoui(count))
 #endif
 
-#endif /* HEADER_CURL_WARNLESS_H_REDEFS */
+#endif /* HEADER_CURL_WARNLESS_H */

+ 2 - 3
lib/cw-out.c

@@ -31,6 +31,7 @@
 #include "headers.h"
 #include "multiif.h"
 #include "sendf.h"
+#include "transfer.h"
 #include "cw-out.h"
 #include "cw-pause.h"
 
@@ -234,11 +235,9 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
         failf(data, "Write callback asked for PAUSE when not supported");
         return CURLE_WRITE_ERROR;
       }
-      /* mark the connection as RECV paused */
-      data->req.keepon |= KEEP_RECV_PAUSE;
       ctx->paused = TRUE;
       CURL_TRC_WRITE(data, "[OUT] PAUSE requested by client");
-      break;
+      return Curl_xfer_pause_recv(data, TRUE);
     }
     else if(CURL_WRITEFUNC_ERROR == nwritten) {
       failf(data, "client returned ERROR on write of %zu bytes", wlen);

+ 6 - 7
lib/dict.c

@@ -60,7 +60,6 @@
 #include "progress.h"
 #include "dict.h"
 #include "curl_printf.h"
-#include "strcase.h"
 #include "curl_memory.h"
 /* The last #include file should be: */
 #include "memdebug.h"
@@ -198,9 +197,9 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
   if(result)
     return result;
 
-  if(strncasecompare(path, DICT_MATCH, sizeof(DICT_MATCH)-1) ||
-     strncasecompare(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) ||
-     strncasecompare(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) {
+  if(curl_strnequal(path, DICT_MATCH, sizeof(DICT_MATCH)-1) ||
+     curl_strnequal(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) ||
+     curl_strnequal(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) {
 
     word = strchr(path, ':');
     if(word) {
@@ -245,9 +244,9 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
     }
     Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); /* no upload */
   }
-  else if(strncasecompare(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) ||
-          strncasecompare(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) ||
-          strncasecompare(path, DICT_DEFINE3, sizeof(DICT_DEFINE3)-1)) {
+  else if(curl_strnequal(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) ||
+          curl_strnequal(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) ||
+          curl_strnequal(path, DICT_DEFINE3, sizeof(DICT_DEFINE3)-1)) {
 
     word = strchr(path, ':');
     if(word) {

+ 2 - 4
lib/doh.c

@@ -257,7 +257,7 @@ static void doh_probe_done(struct Curl_easy *data,
 
     if(!dohp->pending) {
       /* DoH completed, run the transfer picking up the results */
-      Curl_expire(data, 0, EXPIRE_RUN_NOW);
+      Curl_multi_mark_dirty(data);
     }
   }
 }
@@ -377,8 +377,6 @@ static CURLcode doh_probe_run(struct Curl_easy *data,
      options should be added to check doh proxy insecure separately,
      CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER.
      */
-  if(data->set.ssl.falsestart)
-    ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L);
   if(data->set.str[STRING_SSL_CAFILE]) {
     ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
                        data->set.str[STRING_SSL_CAFILE]);
@@ -590,7 +588,7 @@ static void doh_store_a(const unsigned char *doh, int index,
 }
 
 static void doh_store_aaaa(const unsigned char *doh, int index,
-                              struct dohentry *d)
+                           struct dohentry *d)
 {
   /* silently ignore addresses over the limit */
   if(d->numaddr < DOH_MAX_ADDR) {

+ 3 - 3
lib/dynhds.c

@@ -150,7 +150,7 @@ struct dynhds_entry *Curl_dynhds_get(struct dynhds *dynhds, const char *name,
   size_t i;
   for(i = 0; i < dynhds->hds_len; ++i) {
     if(dynhds->hds[i]->namelen == namelen &&
-       strncasecompare(dynhds->hds[i]->name, name, namelen)) {
+       curl_strnequal(dynhds->hds[i]->name, name, namelen)) {
       return dynhds->hds[i];
     }
   }
@@ -297,7 +297,7 @@ size_t Curl_dynhds_count_name(struct dynhds *dynhds,
     size_t i;
     for(i = 0; i < dynhds->hds_len; ++i) {
       if((namelen == dynhds->hds[i]->namelen) &&
-         strncasecompare(name, dynhds->hds[i]->name, namelen))
+         curl_strnequal(name, dynhds->hds[i]->name, namelen))
         ++n;
     }
   }
@@ -325,7 +325,7 @@ size_t Curl_dynhds_remove(struct dynhds *dynhds,
     size_t i, len;
     for(i = 0; i < dynhds->hds_len; ++i) {
       if((namelen == dynhds->hds[i]->namelen) &&
-         strncasecompare(name, dynhds->hds[i]->name, namelen)) {
+         curl_strnequal(name, dynhds->hds[i]->name, namelen)) {
         ++n;
         --dynhds->hds_len;
         dynhds->strs_len -= (dynhds->hds[i]->namelen +

+ 27 - 65
lib/easy.c

@@ -67,6 +67,7 @@
 #include "amigaos.h"
 #include "macos.h"
 #include "curlx/warnless.h"
+#include "curlx/wait.h"
 #include "sigpipe.h"
 #include "vssh/ssh.h"
 #include "setopt.h"
@@ -128,9 +129,6 @@ curl_free_callback Curl_cfree = (curl_free_callback)free;
 curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc;
 curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup;
 curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
-#if defined(_WIN32) && defined(UNICODE)
-curl_wcsdup_callback Curl_cwcsdup = Curl_wcsdup;
-#endif
 
 #if defined(_MSC_VER) && defined(_DLL)
 #  pragma warning(pop)
@@ -156,9 +154,6 @@ static CURLcode global_init(long flags, bool memoryfuncs)
     Curl_crealloc = (curl_realloc_callback)realloc;
     Curl_cstrdup = (curl_strdup_callback)system_strdup;
     Curl_ccalloc = (curl_calloc_callback)calloc;
-#if defined(_WIN32) && defined(UNICODE)
-    Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup;
-#endif
   }
 
   if(Curl_trc_init()) {
@@ -611,7 +606,7 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
 #endif
       pollrc = 0;
       if(ev->ms > 0)
-        Curl_wait_ms(ev->ms);
+        curlx_wait_ms(ev->ms);
     }
 
     ev->msbump = FALSE; /* reset here */
@@ -733,7 +728,7 @@ static CURLcode easy_transfer(struct Curl_multi *multi)
 
 
 /*
- * easy_perform() is the external interface that performs a blocking
+ * easy_perform() is the internal interface that performs a blocking
  * transfer as previously setup.
  *
  * CONCEPT: This function creates a multi handle, adds the easy handle to it,
@@ -1128,13 +1123,12 @@ void curl_easy_reset(CURL *d)
  */
 CURLcode curl_easy_pause(CURL *d, int action)
 {
-  struct SingleRequest *k;
   CURLcode result = CURLE_OK;
-  int oldstate;
-  int newstate;
   bool recursive = FALSE;
-  bool keep_changed, unpause_read, not_all_paused;
+  bool changed = FALSE;
   struct Curl_easy *data = d;
+  bool recv_paused, recv_paused_new;
+  bool send_paused, send_paused_new;
 
   if(!GOOD_EASY_HANDLE(data) || !data->conn)
     /* crazy input, do not continue */
@@ -1142,62 +1136,37 @@ CURLcode curl_easy_pause(CURL *d, int action)
 
   if(Curl_is_in_callback(data))
     recursive = TRUE;
-  k = &data->req;
-  oldstate = k->keepon & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE);
-
-  /* first switch off both pause bits then set the new pause bits */
-  newstate = (k->keepon &~ (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) |
-    ((action & CURLPAUSE_RECV) ? KEEP_RECV_PAUSE : 0) |
-    ((action & CURLPAUSE_SEND) ? KEEP_SEND_PAUSE : 0);
-
-  keep_changed = ((newstate & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) != oldstate);
-  not_all_paused = (newstate & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) !=
-                   (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE);
-  unpause_read = ((k->keepon & ~newstate & KEEP_SEND_PAUSE) &&
-                  (data->mstate == MSTATE_PERFORMING ||
-                   data->mstate == MSTATE_RATELIMITING));
-  /* Unpausing writes is detected on the next run in
-   * transfer.c:Curl_sendrecv(). This is because this may result
-   * in a transfer error if the application's callbacks fail */
-
-  /* Set the new keepon state, so it takes effect no matter what error
-   * may happen afterwards. */
-  k->keepon = newstate;
+
+  recv_paused = Curl_xfer_recv_is_paused(data);
+  recv_paused_new = (action & CURLPAUSE_RECV);
+  send_paused = Curl_xfer_send_is_paused(data);
+  send_paused_new = (action & CURLPAUSE_SEND);
+
+  if(send_paused != send_paused_new) {
+    changed = TRUE;
+    result = Curl_1st_err(result, Curl_xfer_pause_send(data, send_paused_new));
+  }
+
+  if(recv_paused != recv_paused_new) {
+    changed = TRUE;
+    result = Curl_1st_err(result, Curl_xfer_pause_recv(data, recv_paused_new));
+  }
 
   /* If not completely pausing both directions now, run again in any case. */
-  if(not_all_paused) {
+  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;
-    /* Simulate socket events on next run for unpaused directions */
-    if(!(newstate & KEEP_SEND_PAUSE))
-      data->state.select_bits |= CURL_CSELECT_OUT;
-    if(!(newstate & KEEP_RECV_PAUSE))
-      data->state.select_bits |= CURL_CSELECT_IN;
     /* On changes, tell application to update its timers. */
-    if(keep_changed && data->multi) {
-      if(Curl_update_timer(data->multi)) {
+    if(changed && data->multi) {
+      if(Curl_update_timer(data->multi) && !result)
         result = CURLE_ABORTED_BY_CALLBACK;
-        goto out;
-      }
     }
   }
 
-  if(unpause_read) {
-    result = Curl_creader_unpause(data);
-    if(result)
-      goto out;
-  }
-
-  if(!(k->keepon & KEEP_RECV_PAUSE) && Curl_cwriter_is_paused(data)) {
-    Curl_conn_ev_data_pause(data, FALSE);
-    result = Curl_cwriter_unpause(data);
-  }
-
-out:
-  if(!result && !data->state.done && keep_changed && data->multi)
+  if(!result && changed && !data->state.done && data->multi)
     /* pause/unpausing may result in multi event changes */
-    if(Curl_multi_ev_assess_xfer(data->multi, data))
+    if(Curl_multi_ev_assess_xfer(data->multi, data) && !result)
       result = CURLE_ABORTED_BY_CALLBACK;
 
   if(recursive)
@@ -1241,7 +1210,6 @@ static CURLcode easy_connection(struct Curl_easy *data,
 CURLcode curl_easy_recv(CURL *d, void *buffer, size_t buflen, size_t *n)
 {
   CURLcode result;
-  ssize_t n1;
   struct connectdata *c;
   struct Curl_easy *data = d;
 
@@ -1258,13 +1226,7 @@ CURLcode curl_easy_recv(CURL *d, void *buffer, size_t buflen, size_t *n)
     Curl_attach_connection(data, c);
 
   *n = 0;
-  result = Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, &n1);
-
-  if(result)
-    return result;
-
-  *n = (size_t)n1;
-  return CURLE_OK;
+  return Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, n);
 }
 
 #ifndef CURL_DISABLE_WEBSOCKETS

+ 3 - 4
lib/easygetopt.c

@@ -1,9 +1,9 @@
 /***************************************************************************
  *                                  _   _ ____  _
- *  Project                     ___| | | |  _ | |
+ *  Project                     ___| | | |  _ \| |
  *                             / __| | | | |_) | |
  *                            | (__| |_| |  _ <| |___
- *                             ___|___/|_| ______|
+ *                             \___|\___/|_| \_\_____|
  *
  * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
  *
@@ -23,7 +23,6 @@
  ***************************************************************************/
 
 #include "curl_setup.h"
-#include "strcase.h"
 #include "easyoptions.h"
 
 #ifndef CURL_DISABLE_GETOPTIONS
@@ -37,7 +36,7 @@ static const struct curl_easyoption *lookup(const char *name, CURLoption id)
     const struct curl_easyoption *o = &Curl_easyopts[0];
     do {
       if(name) {
-        if(strcasecompare(o->name, name))
+        if(curl_strequal(o->name, name))
           return o;
       }
       else {

+ 7 - 9
lib/escape.c

@@ -85,7 +85,7 @@ char *curl_easy_escape(CURL *data, const char *string,
     else {
       /* encode it */
       unsigned char out[3]={'%'};
-      Curl_hexbyte(&out[1], in, FALSE);
+      Curl_hexbyte(&out[1], in);
       if(curlx_dyn_addn(&d, out, 3))
         return NULL;
     }
@@ -212,7 +212,8 @@ void Curl_hexencode(const unsigned char *src, size_t len, /* input length */
   DEBUGASSERT(src && len && (olen >= 3));
   if(src && len && (olen >= 3)) {
     while(len-- && (olen >= 3)) {
-      Curl_hexbyte(out, *src, TRUE);
+      out[0] = Curl_ldigits[*src >> 4];
+      out[1] = Curl_ldigits[*src & 0x0F];
       ++src;
       out += 2;
       olen -= 2;
@@ -225,14 +226,11 @@ void Curl_hexencode(const unsigned char *src, size_t len, /* input length */
 
 /* Curl_hexbyte
  *
- * Output a single unsigned char as a two-digit hex number, lowercase or
- * uppercase
+ * Output a single unsigned char as a two-digit UPPERCASE hex number.
  */
 void Curl_hexbyte(unsigned char *dest, /* must fit two bytes */
-                  unsigned char val,
-                  bool lowercase)
+                  unsigned char val)
 {
-  const unsigned char *t = lowercase ? Curl_ldigits : Curl_udigits;
-  dest[0] = t[val >> 4];
-  dest[1] = t[val & 0x0F];
+  dest[0] = Curl_udigits[val >> 4];
+  dest[1] = Curl_udigits[val & 0x0F];
 }

+ 1 - 2
lib/escape.h

@@ -42,7 +42,6 @@ void Curl_hexencode(const unsigned char *src, size_t len, /* input length */
                     unsigned char *out, size_t olen); /* output buffer size */
 
 void Curl_hexbyte(unsigned char *dest, /* must fit two bytes */
-                  unsigned char val,
-                  bool lowercase);
+                  unsigned char val);
 
 #endif /* HEADER_CURL_ESCAPE_H */

+ 0 - 1
lib/formdata.c

@@ -34,7 +34,6 @@ struct Curl_easy;
 #include "urldata.h" /* for struct Curl_easy */
 #include "mime.h"
 #include "vtls/vtls.h"
-#include "strcase.h"
 #include "sendf.h"
 #include "strdup.h"
 #include "rand.h"

+ 53 - 15
lib/ftp.c

@@ -60,7 +60,7 @@
 #include "cf-socket.h"
 #include "connect.h"
 #include "strerror.h"
-#include "inet_ntop.h"
+#include "curlx/inet_ntop.h"
 #include "curlx/inet_pton.h"
 #include "select.h"
 #include "parsedate.h" /* for the week day and month names */
@@ -114,12 +114,14 @@ static const char * const ftp_state_names[]={
   "QUOTE",
   "RETR_PREQUOTE",
   "STOR_PREQUOTE",
+  "LIST_PREQUOTE",
   "POSTQUOTE",
   "CWD",
   "MKD",
   "MDTM",
   "TYPE",
   "LIST_TYPE",
+  "RETR_LIST_TYPE",
   "RETR_TYPE",
   "STOR_TYPE",
   "SIZE",
@@ -765,7 +767,12 @@ static CURLcode ftp_state_user(struct Curl_easy *data,
 static CURLcode ftp_state_pwd(struct Curl_easy *data,
                               struct ftp_conn *ftpc)
 {
-  CURLcode result = Curl_pp_sendf(data, &ftpc->pp, "%s", "PWD");
+  CURLcode result;
+#ifdef DEBUGBUILD
+  if(!data->id && getenv("CURL_FTP_PWD_STOP"))
+    return CURLE_OK;
+#endif
+  result = Curl_pp_sendf(data, &ftpc->pp, "%s", "PWD");
   if(!result)
     ftp_state(data, ftpc, FTP_PWD);
 
@@ -978,6 +985,12 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
       port_min = port_max = 0;
 
     if(addrlen) {
+      const struct Curl_sockaddr_ex *remote_addr =
+       Curl_conn_get_remote_addr(data, FIRSTSOCKET);
+
+      DEBUGASSERT(remote_addr);
+      if(!remote_addr)
+        goto out;
       DEBUGASSERT(addr);
       if(addrlen >= sizeof(ipstr))
         goto out;
@@ -985,9 +998,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
       ipstr[addrlen] = 0;
 
       /* attempt to get the address of the given interface name */
-      switch(Curl_if2ip(conn->remote_addr->family,
+      switch(Curl_if2ip(remote_addr->family,
 #ifdef USE_IPV6
-                        Curl_ipv6_scope(&conn->remote_addr->curl_sa_addr),
+                        Curl_ipv6_scope(&remote_addr->curl_sa_addr),
                         conn->scope_id,
 #endif
                         ipstr, hbuf, sizeof(hbuf))) {
@@ -1020,11 +1033,11 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
     switch(sa->sa_family) {
 #ifdef USE_IPV6
     case AF_INET6:
-      r = Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
+      r = curlx_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
       break;
 #endif
     default:
-      r = Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
+      r = curlx_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
       break;
     }
     if(!r) {
@@ -1052,7 +1065,8 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
   /* step 2, create a socket for the requested address */
   error = 0;
   for(ai = res; ai; ai = ai->ai_next) {
-    if(Curl_socket_open(data, ai, NULL, conn->transport, &portsock)) {
+    if(Curl_socket_open(data, ai, NULL,
+                        Curl_conn_get_transport(data, conn), &portsock)) {
       error = SOCKERRNO;
       continue;
     }
@@ -1245,7 +1259,7 @@ out:
     }
     data->conn->bits.do_more = FALSE;
     Curl_pgrsTime(data, TIMER_STARTACCEPT);
-    Curl_expire(data, data->set.accepttimeout ?
+    Curl_expire(data, (data->set.accepttimeout > 0) ?
                 data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT,
                 EXPIRE_FTP_ACCEPT);
   }
@@ -1448,6 +1462,14 @@ static CURLcode ftp_state_list(struct Curl_easy *data,
   return result;
 }
 
+static CURLcode ftp_state_list_prequote(struct Curl_easy *data,
+                                        struct ftp_conn *ftpc,
+                                        struct FTP *ftp)
+{
+  /* We have sent the TYPE, now we must send the list of prequote strings */
+  return ftp_state_quote(data, ftpc, ftp, TRUE, FTP_LIST_PREQUOTE);
+}
+
 static CURLcode ftp_state_retr_prequote(struct Curl_easy *data,
                                         struct ftp_conn *ftpc,
                                         struct FTP *ftp)
@@ -1636,6 +1658,7 @@ static CURLcode ftp_state_quote(struct Curl_easy *data,
     break;
   case FTP_RETR_PREQUOTE:
   case FTP_STOR_PREQUOTE:
+  case FTP_LIST_PREQUOTE:
     item = data->set.prequote;
     break;
   case FTP_POSTQUOTE:
@@ -1725,6 +1748,10 @@ static CURLcode ftp_state_quote(struct Curl_easy *data,
       break;
     case FTP_POSTQUOTE:
       break;
+    case FTP_LIST_PREQUOTE:
+      ftp_state(data, ftpc, FTP_LIST_TYPE);
+      result = ftp_state_list(data, ftpc, ftp);
+      break;
     }
   }
 
@@ -2198,6 +2225,8 @@ static CURLcode ftp_state_type_resp(struct Curl_easy *data,
     result = ftp_state_retr_prequote(data, ftpc, ftp);
   else if(instate == FTP_STOR_TYPE)
     result = ftp_state_stor_prequote(data, ftpc, ftp);
+  else if(instate == FTP_RETR_LIST_TYPE)
+    result = ftp_state_list_prequote(data, ftpc, ftp);
 
   return result;
 }
@@ -2961,7 +2990,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
           return CURLE_OUT_OF_MEMORY;
 
         /* Check for special servers here. */
-        if(strcasecompare(os, "OS/400")) {
+        if(curl_strequal(os, "OS/400")) {
           /* Force OS400 name format 1. */
           result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1");
           if(result) {
@@ -3002,6 +3031,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
     case FTP_POSTQUOTE:
     case FTP_RETR_PREQUOTE:
     case FTP_STOR_PREQUOTE:
+    case FTP_LIST_PREQUOTE:
       if((ftpcode >= 400) && !ftpc->count2) {
         /* failure response code, and not allowed to fail */
         failf(data, "QUOT command failed with %03d", ftpcode);
@@ -3071,6 +3101,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
     case FTP_LIST_TYPE:
     case FTP_RETR_TYPE:
     case FTP_STOR_TYPE:
+    case FTP_RETR_LIST_TYPE:
       result = ftp_state_type_resp(data, ftpc, ftp, ftpcode, ftpc->state);
       break;
 
@@ -3125,8 +3156,8 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
 
 /* called repeatedly until done from multi.c */
 static CURLcode ftp_statemach(struct Curl_easy *data,
-                               struct ftp_conn *ftpc,
-                               bool *done)
+                              struct ftp_conn *ftpc,
+                              bool *done)
 {
   CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE);
 
@@ -3675,7 +3706,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
 
       if(result)
         ;
-      else if(data->state.list_only || !ftpc->file) {
+      else if((data->state.list_only || !ftpc->file) &&
+              !(data->set.prequote)) {
         /* The specified path ends with a slash, and therefore we think this
            is a directory that is requested, use LIST. But before that we
            need to set ASCII transfer mode. */
@@ -3689,8 +3721,14 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
         /* otherwise just fall through */
       }
       else {
-        result = ftp_nb_type(data, ftpc, ftp, data->state.prefer_ascii,
-                             FTP_RETR_TYPE);
+        if(data->set.prequote && !ftpc->file) {
+          result = ftp_nb_type(data, ftpc, ftp, TRUE,
+                               FTP_RETR_LIST_TYPE);
+        }
+        else {
+          result = ftp_nb_type(data, ftpc, ftp, data->state.prefer_ascii,
+                               FTP_RETR_TYPE);
+        }
         if(result)
           return result;
       }
@@ -4117,7 +4155,7 @@ static CURLcode ftp_disconnect(struct Curl_easy *data,
      will try to send the QUIT command, otherwise it will just return.
   */
   ftpc->shutdown = TRUE;
-  if(dead_connection)
+  if(dead_connection || Curl_pp_needs_flush(data, &ftpc->pp))
     ftpc->ctl_valid = FALSE;
 
   /* The FTP session may or may not have been allocated/setup at this point! */

+ 2 - 0
lib/ftp.h

@@ -62,12 +62,14 @@ enum {
   FTP_QUOTE, /* waiting for a response to a command sent in a quote list */
   FTP_RETR_PREQUOTE,
   FTP_STOR_PREQUOTE,
+  FTP_LIST_PREQUOTE,
   FTP_POSTQUOTE,
   FTP_CWD,  /* change dir */
   FTP_MKD,  /* if the dir did not exist */
   FTP_MDTM, /* to figure out the datestamp */
   FTP_TYPE, /* to set type when doing a head-like request */
   FTP_LIST_TYPE, /* set type when about to do a dir list */
+  FTP_RETR_LIST_TYPE,
   FTP_RETR_TYPE, /* set type when about to RETR a file */
   FTP_STOR_TYPE, /* set type when about to STOR a file */
   FTP_SIZE, /* get the remote file's size for head-like request */

+ 437 - 344
lib/ftplistparser.c

@@ -396,407 +396,500 @@ static CURLcode unix_filetype(const char c, curlfiletype *t)
   return CURLE_OK;
 }
 
-static CURLcode parse_unix(struct Curl_easy *data,
-                           struct ftp_parselist_data *parser,
-                           struct fileinfo *infop,
-                           const char c)
+static CURLcode parse_unix_totalsize(struct ftp_parselist_data *parser,
+                                     struct fileinfo *infop,
+                                     const char c)
 {
-  struct curl_fileinfo *finfo = &infop->info;
   size_t len = curlx_dyn_len(&infop->buf);
   char *mem = curlx_dyn_ptr(&infop->buf);
-  CURLcode result = CURLE_OK;
-
-  switch(parser->state.UNIX.main) {
-  case PL_UNIX_TOTALSIZE:
-    switch(parser->state.UNIX.sub.total_dirsize) {
-    case PL_UNIX_TOTALSIZE_INIT:
-      if(c == 't') {
-        parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
-        parser->item_length++;
-      }
-      else {
-        parser->state.UNIX.main = PL_UNIX_FILETYPE;
-        /* continue to fall through */
-      }
-      break;
-    case PL_UNIX_TOTALSIZE_READING:
+  switch(parser->state.UNIX.sub.total_dirsize) {
+  case PL_UNIX_TOTALSIZE_INIT:
+    if(c == 't') {
+      parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
       parser->item_length++;
-      if(c == '\r') {
-        parser->item_length--;
-        if(len)
-          curlx_dyn_setlen(&infop->buf, --len);
-      }
-      else if(c == '\n') {
-        mem[parser->item_length - 1] = 0;
-        if(!strncmp("total ", mem, 6)) {
-          const char *endptr = mem + 6;
-          /* here we can deal with directory size, pass the leading
-             whitespace and then the digits */
-          curlx_str_passblanks(&endptr);
-          while(ISDIGIT(*endptr))
-            endptr++;
-          if(*endptr) {
-            return CURLE_FTP_BAD_FILE_LIST;
-          }
-          parser->state.UNIX.main = PL_UNIX_FILETYPE;
-          curlx_dyn_reset(&infop->buf);
-        }
-        else
+    }
+    else {
+      parser->state.UNIX.main = PL_UNIX_FILETYPE;
+      /* continue to fall through */
+    }
+    break;
+  case PL_UNIX_TOTALSIZE_READING:
+    parser->item_length++;
+    if(c == '\r') {
+      parser->item_length--;
+      if(len)
+        curlx_dyn_setlen(&infop->buf, --len);
+    }
+    else if(c == '\n') {
+      mem[parser->item_length - 1] = 0;
+      if(!strncmp("total ", mem, 6)) {
+        const char *endptr = mem + 6;
+        /* here we can deal with directory size, pass the leading
+           whitespace and then the digits */
+        curlx_str_passblanks(&endptr);
+        while(ISDIGIT(*endptr))
+          endptr++;
+        if(*endptr) {
           return CURLE_FTP_BAD_FILE_LIST;
-
+        }
+        parser->state.UNIX.main = PL_UNIX_FILETYPE;
+        curlx_dyn_reset(&infop->buf);
       }
-      break;
+      else
+        return CURLE_FTP_BAD_FILE_LIST;
+
     }
-    if(parser->state.UNIX.main != PL_UNIX_FILETYPE)
-      break;
-    FALLTHROUGH();
-  case PL_UNIX_FILETYPE:
-    result = unix_filetype(c, &finfo->filetype);
-    if(result)
-      return result;
-    parser->state.UNIX.main = PL_UNIX_PERMISSION;
-    parser->item_length = 0;
-    parser->item_offset = 1;
     break;
-  case PL_UNIX_PERMISSION:
-    parser->item_length++;
-    if((parser->item_length <= 9) && !strchr("rwx-tTsS", c))
+  }
+  return CURLE_OK;
+}
+
+static CURLcode parse_unix_permission(struct ftp_parselist_data *parser,
+                                      struct fileinfo *infop,
+                                      const char c)
+{
+  char *mem = curlx_dyn_ptr(&infop->buf);
+  parser->item_length++;
+  if((parser->item_length <= 9) && !strchr("rwx-tTsS", c))
+    return CURLE_FTP_BAD_FILE_LIST;
+
+  else if(parser->item_length == 10) {
+    unsigned int perm;
+    if(c != ' ')
       return CURLE_FTP_BAD_FILE_LIST;
 
-    else if(parser->item_length == 10) {
-      unsigned int perm;
-      if(c != ' ')
-        return CURLE_FTP_BAD_FILE_LIST;
+    mem[10] = 0; /* terminate permissions */
+    perm = ftp_pl_get_permission(mem + parser->item_offset);
+    if(perm & FTP_LP_MALFORMATED_PERM)
+      return CURLE_FTP_BAD_FILE_LIST;
 
-      mem[10] = 0; /* terminate permissions */
-      perm = ftp_pl_get_permission(mem + parser->item_offset);
-      if(perm & FTP_LP_MALFORMATED_PERM)
-        return CURLE_FTP_BAD_FILE_LIST;
+    parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM;
+    parser->file_data->info.perm = perm;
+    parser->offsets.perm = parser->item_offset;
 
-      parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM;
-      parser->file_data->info.perm = perm;
-      parser->offsets.perm = parser->item_offset;
+    parser->item_length = 0;
+    parser->state.UNIX.main = PL_UNIX_HLINKS;
+    parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
+  }
+  return CURLE_OK;
+}
 
-      parser->item_length = 0;
-      parser->state.UNIX.main = PL_UNIX_HLINKS;
-      parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
+static CURLcode parse_unix_hlinks(struct ftp_parselist_data *parser,
+                                  struct fileinfo *infop,
+                                  const char c)
+{
+  size_t len = curlx_dyn_len(&infop->buf);
+  char *mem = curlx_dyn_ptr(&infop->buf);
+
+  switch(parser->state.UNIX.sub.hlinks) {
+  case PL_UNIX_HLINKS_PRESPACE:
+    if(c != ' ') {
+      if(ISDIGIT(c) && len) {
+        parser->item_offset = len - 1;
+        parser->item_length = 1;
+        parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
+      }
+      else
+        return CURLE_FTP_BAD_FILE_LIST;
     }
     break;
-  case PL_UNIX_HLINKS:
-    switch(parser->state.UNIX.sub.hlinks) {
-    case PL_UNIX_HLINKS_PRESPACE:
-      if(c != ' ') {
-        if(ISDIGIT(c) && len) {
-          parser->item_offset = len - 1;
-          parser->item_length = 1;
-          parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
-        }
-        else
-          return CURLE_FTP_BAD_FILE_LIST;
+  case PL_UNIX_HLINKS_NUMBER:
+    parser->item_length ++;
+    if(c == ' ') {
+      const char *p = &mem[parser->item_offset];
+      curl_off_t hlinks;
+      mem[parser->item_offset + parser->item_length - 1] = 0;
+
+      if(!curlx_str_number(&p, &hlinks, LONG_MAX)) {
+        parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
+        parser->file_data->info.hardlinks = (long)hlinks;
       }
-      break;
-    case PL_UNIX_HLINKS_NUMBER:
-      parser->item_length ++;
-      if(c == ' ') {
-        const char *p = &mem[parser->item_offset];
-        curl_off_t hlinks;
-        mem[parser->item_offset + parser->item_length - 1] = 0;
+      parser->item_length = 0;
+      parser->item_offset = 0;
+      parser->state.UNIX.main = PL_UNIX_USER;
+      parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
+    }
+    else if(!ISDIGIT(c))
+      return CURLE_FTP_BAD_FILE_LIST;
 
-        if(!curlx_str_number(&p, &hlinks, LONG_MAX)) {
-          parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
-          parser->file_data->info.hardlinks = (long)hlinks;
-        }
-        parser->item_length = 0;
-        parser->item_offset = 0;
-        parser->state.UNIX.main = PL_UNIX_USER;
-        parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
-      }
-      else if(!ISDIGIT(c))
-        return CURLE_FTP_BAD_FILE_LIST;
+    break;
+  }
+  return CURLE_OK;
+}
 
-      break;
+static CURLcode parse_unix_user(struct ftp_parselist_data *parser,
+                                struct fileinfo *infop,
+                                const char c)
+{
+  size_t len = curlx_dyn_len(&infop->buf);
+  char *mem = curlx_dyn_ptr(&infop->buf);
+  switch(parser->state.UNIX.sub.user) {
+  case PL_UNIX_USER_PRESPACE:
+    if(c != ' ' && len) {
+      parser->item_offset = len - 1;
+      parser->item_length = 1;
+      parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
     }
     break;
-  case PL_UNIX_USER:
-    switch(parser->state.UNIX.sub.user) {
-    case PL_UNIX_USER_PRESPACE:
-      if(c != ' ' && len) {
-        parser->item_offset = len - 1;
-        parser->item_length = 1;
-        parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
-      }
-      break;
-    case PL_UNIX_USER_PARSING:
-      parser->item_length++;
-      if(c == ' ') {
-        mem[parser->item_offset + parser->item_length - 1] = 0;
-        parser->offsets.user = parser->item_offset;
-        parser->state.UNIX.main = PL_UNIX_GROUP;
-        parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
-        parser->item_offset = 0;
-        parser->item_length = 0;
-      }
-      break;
+  case PL_UNIX_USER_PARSING:
+    parser->item_length++;
+    if(c == ' ') {
+      mem[parser->item_offset + parser->item_length - 1] = 0;
+      parser->offsets.user = parser->item_offset;
+      parser->state.UNIX.main = PL_UNIX_GROUP;
+      parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
+      parser->item_offset = 0;
+      parser->item_length = 0;
     }
     break;
-  case PL_UNIX_GROUP:
-    switch(parser->state.UNIX.sub.group) {
-    case PL_UNIX_GROUP_PRESPACE:
-      if(c != ' ' && len) {
+  }
+  return CURLE_OK;
+}
+
+static CURLcode parse_unix_group(struct ftp_parselist_data *parser,
+                                 struct fileinfo *infop,
+                                 const char c)
+{
+  size_t len = curlx_dyn_len(&infop->buf);
+  char *mem = curlx_dyn_ptr(&infop->buf);
+  switch(parser->state.UNIX.sub.group) {
+  case PL_UNIX_GROUP_PRESPACE:
+    if(c != ' ' && len) {
+      parser->item_offset = len - 1;
+      parser->item_length = 1;
+      parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
+    }
+    break;
+  case PL_UNIX_GROUP_NAME:
+    parser->item_length++;
+    if(c == ' ') {
+      mem[parser->item_offset + parser->item_length - 1] = 0;
+      parser->offsets.group = parser->item_offset;
+      parser->state.UNIX.main = PL_UNIX_SIZE;
+      parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
+      parser->item_offset = 0;
+      parser->item_length = 0;
+    }
+    break;
+  }
+  return CURLE_OK;
+}
+
+static CURLcode parse_unix_size(struct ftp_parselist_data *parser,
+                                struct fileinfo *infop,
+                                const char c)
+{
+  size_t len = curlx_dyn_len(&infop->buf);
+  char *mem = curlx_dyn_ptr(&infop->buf);
+  switch(parser->state.UNIX.sub.size) {
+  case PL_UNIX_SIZE_PRESPACE:
+    if(c != ' ') {
+      if(ISDIGIT(c) && len) {
         parser->item_offset = len - 1;
         parser->item_length = 1;
-        parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
+        parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
       }
-      break;
-    case PL_UNIX_GROUP_NAME:
-      parser->item_length++;
-      if(c == ' ') {
-        mem[parser->item_offset + parser->item_length - 1] = 0;
-        parser->offsets.group = parser->item_offset;
-        parser->state.UNIX.main = PL_UNIX_SIZE;
-        parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
-        parser->item_offset = 0;
-        parser->item_length = 0;
-      }
-      break;
+      else
+        return CURLE_FTP_BAD_FILE_LIST;
     }
     break;
-  case PL_UNIX_SIZE:
-    switch(parser->state.UNIX.sub.size) {
-    case PL_UNIX_SIZE_PRESPACE:
-      if(c != ' ') {
-        if(ISDIGIT(c) && len) {
-          parser->item_offset = len - 1;
-          parser->item_length = 1;
-          parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
+  case PL_UNIX_SIZE_NUMBER:
+    parser->item_length++;
+    if(c == ' ') {
+      const char *p = mem + parser->item_offset;
+      curl_off_t fsize;
+      mem[parser->item_offset + parser->item_length - 1] = 0;
+      if(!curlx_str_numblanks(&p, &fsize)) {
+        if(p[0] == '\0' && fsize != CURL_OFF_T_MAX) {
+          parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
+          parser->file_data->info.size = fsize;
         }
-        else
-          return CURLE_FTP_BAD_FILE_LIST;
+        parser->item_length = 0;
+        parser->item_offset = 0;
+        parser->state.UNIX.main = PL_UNIX_TIME;
+        parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
       }
-      break;
-    case PL_UNIX_SIZE_NUMBER:
-      parser->item_length++;
-      if(c == ' ') {
-        const char *p = mem + parser->item_offset;
-        curl_off_t fsize;
-        mem[parser->item_offset + parser->item_length - 1] = 0;
-        if(!curlx_str_numblanks(&p, &fsize)) {
-          if(p[0] == '\0' && fsize != CURL_OFF_T_MAX) {
-            parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
-            parser->file_data->info.size = fsize;
-          }
-          parser->item_length = 0;
-          parser->item_offset = 0;
-          parser->state.UNIX.main = PL_UNIX_TIME;
-          parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
-        }
+    }
+    else if(!ISDIGIT(c))
+      return CURLE_FTP_BAD_FILE_LIST;
+
+    break;
+  }
+  return CURLE_OK;
+}
+
+static CURLcode parse_unix_time(struct ftp_parselist_data *parser,
+                                struct fileinfo *infop,
+                                const char c)
+{
+  size_t len = curlx_dyn_len(&infop->buf);
+  char *mem = curlx_dyn_ptr(&infop->buf);
+  struct curl_fileinfo *finfo = &infop->info;
+
+  switch(parser->state.UNIX.sub.time) {
+  case PL_UNIX_TIME_PREPART1:
+    if(c != ' ') {
+      if(ISALNUM(c) && len) {
+        parser->item_offset = len -1;
+        parser->item_length = 1;
+        parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
       }
-      else if(!ISDIGIT(c))
+      else
         return CURLE_FTP_BAD_FILE_LIST;
-
-      break;
     }
     break;
-  case PL_UNIX_TIME:
-    switch(parser->state.UNIX.sub.time) {
-    case PL_UNIX_TIME_PREPART1:
-      if(c != ' ') {
-        if(ISALNUM(c) && len) {
-          parser->item_offset = len -1;
-          parser->item_length = 1;
-          parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
-        }
-        else
-          return CURLE_FTP_BAD_FILE_LIST;
-      }
-      break;
-    case PL_UNIX_TIME_PART1:
-      parser->item_length++;
-      if(c == ' ')
-        parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
+  case PL_UNIX_TIME_PART1:
+    parser->item_length++;
+    if(c == ' ')
+      parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
 
-      else if(!ISALNUM(c) && c != '.')
-        return CURLE_FTP_BAD_FILE_LIST;
+    else if(!ISALNUM(c) && c != '.')
+      return CURLE_FTP_BAD_FILE_LIST;
 
-      break;
-    case PL_UNIX_TIME_PREPART2:
-      parser->item_length++;
-      if(c != ' ') {
-        if(ISALNUM(c))
-          parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
-        else
-          return CURLE_FTP_BAD_FILE_LIST;
-      }
-      break;
-    case PL_UNIX_TIME_PART2:
-      parser->item_length++;
-      if(c == ' ')
-        parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
-      else if(!ISALNUM(c) && c != '.')
+    break;
+  case PL_UNIX_TIME_PREPART2:
+    parser->item_length++;
+    if(c != ' ') {
+      if(ISALNUM(c))
+        parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
+      else
         return CURLE_FTP_BAD_FILE_LIST;
-      break;
-    case PL_UNIX_TIME_PREPART3:
-      parser->item_length++;
-      if(c != ' ') {
-        if(ISALNUM(c))
-          parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
-        else
-          return CURLE_FTP_BAD_FILE_LIST;
-      }
-      break;
-    case PL_UNIX_TIME_PART3:
-      parser->item_length++;
-      if(c == ' ') {
-        mem[parser->item_offset + parser->item_length -1] = 0;
-        parser->offsets.time = parser->item_offset;
-        if(finfo->filetype == CURLFILETYPE_SYMLINK) {
-          parser->state.UNIX.main = PL_UNIX_SYMLINK;
-          parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
-        }
-        else {
-          parser->state.UNIX.main = PL_UNIX_FILENAME;
-          parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
-        }
-      }
-      else if(!ISALNUM(c) && c != '.' && c != ':')
+    }
+    break;
+  case PL_UNIX_TIME_PART2:
+    parser->item_length++;
+    if(c == ' ')
+      parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
+    else if(!ISALNUM(c) && c != '.')
+      return CURLE_FTP_BAD_FILE_LIST;
+    break;
+  case PL_UNIX_TIME_PREPART3:
+    parser->item_length++;
+    if(c != ' ') {
+      if(ISALNUM(c))
+        parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
+      else
         return CURLE_FTP_BAD_FILE_LIST;
-      break;
     }
     break;
-  case PL_UNIX_FILENAME:
-    switch(parser->state.UNIX.sub.filename) {
-    case PL_UNIX_FILENAME_PRESPACE:
-      if(c != ' ' && len) {
-        parser->item_offset = len - 1;
-        parser->item_length = 1;
-        parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
-      }
-      break;
-    case PL_UNIX_FILENAME_NAME:
-      parser->item_length++;
-      if(c == '\r')
-        parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
-
-      else if(c == '\n') {
-        mem[parser->item_offset + parser->item_length - 1] = 0;
-        parser->offsets.filename = parser->item_offset;
-        parser->state.UNIX.main = PL_UNIX_FILETYPE;
-        result = ftp_pl_insert_finfo(data, infop);
-        if(result)
-          return result;
+  case PL_UNIX_TIME_PART3:
+    parser->item_length++;
+    if(c == ' ') {
+      mem[parser->item_offset + parser->item_length -1] = 0;
+      parser->offsets.time = parser->item_offset;
+      if(finfo->filetype == CURLFILETYPE_SYMLINK) {
+        parser->state.UNIX.main = PL_UNIX_SYMLINK;
+        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
       }
-      break;
-    case PL_UNIX_FILENAME_WINDOWSEOL:
-      if(c == '\n') {
-        mem[parser->item_offset + parser->item_length - 1] = 0;
-        parser->offsets.filename = parser->item_offset;
-        parser->state.UNIX.main = PL_UNIX_FILETYPE;
-        result = ftp_pl_insert_finfo(data, infop);
-        if(result)
-          return result;
+      else {
+        parser->state.UNIX.main = PL_UNIX_FILENAME;
+        parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
       }
-      else
-        return CURLE_FTP_BAD_FILE_LIST;
+    }
+    else if(!ISALNUM(c) && c != '.' && c != ':')
+      return CURLE_FTP_BAD_FILE_LIST;
+    break;
+  }
+  return CURLE_OK;
+}
 
-      break;
+static CURLcode parse_unix_filename(struct Curl_easy *data,
+                                    struct ftp_parselist_data *parser,
+                                    struct fileinfo *infop,
+                                    const char c)
+{
+  size_t len = curlx_dyn_len(&infop->buf);
+  char *mem = curlx_dyn_ptr(&infop->buf);
+  CURLcode result = CURLE_OK;
+
+  switch(parser->state.UNIX.sub.filename) {
+  case PL_UNIX_FILENAME_PRESPACE:
+    if(c != ' ' && len) {
+      parser->item_offset = len - 1;
+      parser->item_length = 1;
+      parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
     }
     break;
-  case PL_UNIX_SYMLINK:
-    switch(parser->state.UNIX.sub.symlink) {
-    case PL_UNIX_SYMLINK_PRESPACE:
-      if(c != ' ' && len) {
-        parser->item_offset = len - 1;
-        parser->item_length = 1;
-        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
-      }
-      break;
-    case PL_UNIX_SYMLINK_NAME:
-      parser->item_length++;
-      if(c == ' ')
-        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
+  case PL_UNIX_FILENAME_NAME:
+    parser->item_length++;
+    if(c == '\r')
+      parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
+
+    else if(c == '\n') {
+      mem[parser->item_offset + parser->item_length - 1] = 0;
+      parser->offsets.filename = parser->item_offset;
+      parser->state.UNIX.main = PL_UNIX_FILETYPE;
+      result = ftp_pl_insert_finfo(data, infop);
+    }
+    break;
+  case PL_UNIX_FILENAME_WINDOWSEOL:
+    if(c == '\n') {
+      mem[parser->item_offset + parser->item_length - 1] = 0;
+      parser->offsets.filename = parser->item_offset;
+      parser->state.UNIX.main = PL_UNIX_FILETYPE;
+      result = ftp_pl_insert_finfo(data, infop);
+    }
+    else
+      result = CURLE_FTP_BAD_FILE_LIST;
+    break;
+  }
+  return result;
+}
 
-      else if(c == '\r' || c == '\n')
-        return CURLE_FTP_BAD_FILE_LIST;
+static CURLcode parse_unix_symlink(struct Curl_easy *data,
+                                   struct ftp_parselist_data *parser,
+                                   struct fileinfo *infop,
+                                   const char c)
+{
+  size_t len = curlx_dyn_len(&infop->buf);
+  char *mem = curlx_dyn_ptr(&infop->buf);
+  CURLcode result = CURLE_OK;
 
-      break;
-    case PL_UNIX_SYMLINK_PRETARGET1:
-      parser->item_length++;
-      if(c == '-')
-        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
+  switch(parser->state.UNIX.sub.symlink) {
+  case PL_UNIX_SYMLINK_PRESPACE:
+    if(c != ' ' && len) {
+      parser->item_offset = len - 1;
+      parser->item_length = 1;
+      parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+    }
+    break;
+  case PL_UNIX_SYMLINK_NAME:
+    parser->item_length++;
+    if(c == ' ')
+      parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
 
-      else if(c == '\r' || c == '\n')
-        return CURLE_FTP_BAD_FILE_LIST;
-      else
-        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
-      break;
-    case PL_UNIX_SYMLINK_PRETARGET2:
-      parser->item_length++;
-      if(c == '>')
-        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
-      else if(c == '\r' || c == '\n')
-        return CURLE_FTP_BAD_FILE_LIST;
-      else
-        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+    else if(c == '\r' || c == '\n')
+      return CURLE_FTP_BAD_FILE_LIST;
 
-      break;
-    case PL_UNIX_SYMLINK_PRETARGET3:
-      parser->item_length++;
-      if(c == ' ') {
-        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
-        /* now place where is symlink following */
-        mem[parser->item_offset + parser->item_length - 4] = 0;
-        parser->offsets.filename = parser->item_offset;
-        parser->item_length = 0;
-        parser->item_offset = 0;
-      }
-      else if(c == '\r' || c == '\n')
-        return CURLE_FTP_BAD_FILE_LIST;
-      else
-        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
-      break;
-    case PL_UNIX_SYMLINK_PRETARGET4:
-      if(c != '\r' && c != '\n' && len) {
-        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
-        parser->item_offset = len - 1;
-        parser->item_length = 1;
-      }
-      else
-        return CURLE_FTP_BAD_FILE_LIST;
+    break;
+  case PL_UNIX_SYMLINK_PRETARGET1:
+    parser->item_length++;
+    if(c == '-')
+      parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
 
-      break;
-    case PL_UNIX_SYMLINK_TARGET:
-      parser->item_length++;
-      if(c == '\r')
-        parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
+    else if(c == '\r' || c == '\n')
+      return CURLE_FTP_BAD_FILE_LIST;
+    else
+      parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+    break;
+  case PL_UNIX_SYMLINK_PRETARGET2:
+    parser->item_length++;
+    if(c == '>')
+      parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
+    else if(c == '\r' || c == '\n')
+      return CURLE_FTP_BAD_FILE_LIST;
+    else
+      parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
 
-      else if(c == '\n') {
-        mem[parser->item_offset + parser->item_length - 1] = 0;
-        parser->offsets.symlink_target = parser->item_offset;
-        result = ftp_pl_insert_finfo(data, infop);
-        if(result)
-          return result;
+    break;
+  case PL_UNIX_SYMLINK_PRETARGET3:
+    parser->item_length++;
+    if(c == ' ') {
+      parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
+      /* now place where is symlink following */
+      mem[parser->item_offset + parser->item_length - 4] = 0;
+      parser->offsets.filename = parser->item_offset;
+      parser->item_length = 0;
+      parser->item_offset = 0;
+    }
+    else if(c == '\r' || c == '\n')
+      return CURLE_FTP_BAD_FILE_LIST;
+    else
+      parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+    break;
+  case PL_UNIX_SYMLINK_PRETARGET4:
+    if(c != '\r' && c != '\n' && len) {
+      parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
+      parser->item_offset = len - 1;
+      parser->item_length = 1;
+    }
+    else
+      return CURLE_FTP_BAD_FILE_LIST;
 
-        parser->state.UNIX.main = PL_UNIX_FILETYPE;
-      }
-      break;
-    case PL_UNIX_SYMLINK_WINDOWSEOL:
-      if(c == '\n') {
-        mem[parser->item_offset + parser->item_length - 1] = 0;
-        parser->offsets.symlink_target = parser->item_offset;
-        result = ftp_pl_insert_finfo(data, infop);
-        if(result)
-          return result;
+    break;
+  case PL_UNIX_SYMLINK_TARGET:
+    parser->item_length++;
+    if(c == '\r')
+      parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
 
-        parser->state.UNIX.main = PL_UNIX_FILETYPE;
-      }
-      else
-        return CURLE_FTP_BAD_FILE_LIST;
+    else if(c == '\n') {
+      mem[parser->item_offset + parser->item_length - 1] = 0;
+      parser->offsets.symlink_target = parser->item_offset;
+      result = ftp_pl_insert_finfo(data, infop);
+      if(result)
+        break;
+
+      parser->state.UNIX.main = PL_UNIX_FILETYPE;
+    }
+    break;
+  case PL_UNIX_SYMLINK_WINDOWSEOL:
+    if(c == '\n') {
+      mem[parser->item_offset + parser->item_length - 1] = 0;
+      parser->offsets.symlink_target = parser->item_offset;
+      result = ftp_pl_insert_finfo(data, infop);
+      if(result)
+        break;
+
+      parser->state.UNIX.main = PL_UNIX_FILETYPE;
+    }
+    else
+      result = CURLE_FTP_BAD_FILE_LIST;
+
+    break;
+  }
+  return result;
+}
+
+static CURLcode parse_unix(struct Curl_easy *data,
+                           struct ftp_parselist_data *parser,
+                           struct fileinfo *infop,
+                           const char c)
+{
+  struct curl_fileinfo *finfo = &infop->info;
+  CURLcode result = CURLE_OK;
 
+  switch(parser->state.UNIX.main) {
+  case PL_UNIX_TOTALSIZE:
+    result = parse_unix_totalsize(parser, infop, c);
+    if(result)
       break;
+    if(parser->state.UNIX.main != PL_UNIX_FILETYPE)
+      break;
+    FALLTHROUGH();
+  case PL_UNIX_FILETYPE:
+    result = unix_filetype(c, &finfo->filetype);
+    if(!result) {
+      parser->state.UNIX.main = PL_UNIX_PERMISSION;
+      parser->item_length = 0;
+      parser->item_offset = 1;
     }
     break;
+  case PL_UNIX_PERMISSION:
+    result = parse_unix_permission(parser, infop, c);
+    break;
+  case PL_UNIX_HLINKS:
+    result = parse_unix_hlinks(parser, infop, c);
+    break;
+  case PL_UNIX_USER:
+    result = parse_unix_user(parser, infop, c);
+    break;
+  case PL_UNIX_GROUP:
+    result = parse_unix_group(parser, infop, c);
+    break;
+  case PL_UNIX_SIZE:
+    result = parse_unix_size(parser, infop, c);
+    break;
+  case PL_UNIX_TIME:
+    result = parse_unix_time(parser, infop, c);
+    break;
+  case PL_UNIX_FILENAME:
+    result = parse_unix_filename(data, parser, infop, c);
+    break;
+  case PL_UNIX_SYMLINK:
+    result = parse_unix_symlink(data, parser, infop, c);
+    break;
   }
-  return CURLE_OK;
+  return result;
 }
 
 static CURLcode parse_winnt(struct Curl_easy *data,

+ 6 - 10
lib/getinfo.c

@@ -28,6 +28,7 @@
 
 #include "urldata.h"
 #include "getinfo.h"
+#include "cfilters.h"
 #include "vtls/vtls.h"
 #include "connect.h" /* Curl_getconnectinfo() */
 #include "progress.h"
@@ -579,19 +580,14 @@ static CURLcode getinfo_slist(struct Curl_easy *data, CURLINFO info,
       struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **)
                                           param_slistp;
       struct curl_tlssessioninfo *tsi = &data->tsi;
-#ifdef USE_SSL
-      struct connectdata *conn = data->conn;
-#endif
 
+      /* we are exposing a pointer to internal memory with unknown
+       * lifetime here. */
       *tsip = tsi;
-      tsi->backend = Curl_ssl_backend();
-      tsi->internals = NULL;
-
-#ifdef USE_SSL
-      if(conn && tsi->backend != CURLSSLBACKEND_NONE) {
-        tsi->internals = Curl_ssl_get_internals(data, FIRSTSOCKET, info, 0);
+      if(!Curl_conn_get_ssl_info(data, data->conn, FIRSTSOCKET, tsi)) {
+        tsi->backend = Curl_ssl_backend();
+        tsi->internals = NULL;
       }
-#endif
     }
     break;
   default:

+ 1 - 1
lib/hash.c

@@ -353,7 +353,7 @@ size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num)
 }
 
 size_t curlx_str_key_compare(void *k1, size_t key1_len,
-                            void *k2, size_t key2_len)
+                             void *k2, size_t key2_len)
 {
   if((key1_len == key2_len) && !memcmp(k1, k2, key1_len))
     return 1;

+ 1 - 1
lib/hash.h

@@ -99,7 +99,7 @@ void Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user,
                                     int (*comp)(void *, void *));
 size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num);
 size_t curlx_str_key_compare(void *k1, size_t key1_len, void *k2,
-                            size_t key2_len);
+                             size_t key2_len);
 void Curl_hash_start_iterate(struct Curl_hash *hash,
                              struct Curl_hash_iterator *iter);
 struct Curl_hash_element *

+ 3 - 4
lib/headers.c

@@ -26,7 +26,6 @@
 
 #include "urldata.h"
 #include "strdup.h"
-#include "strcase.h"
 #include "sendf.h"
 #include "headers.h"
 #include "curlx/strparse.h"
@@ -88,7 +87,7 @@ CURLHcode curl_easy_header(CURL *easy,
   /* we need a first round to count amount of this header */
   for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
     hs = Curl_node_elem(e);
-    if(strcasecompare(hs->name, name) &&
+    if(curl_strequal(hs->name, name) &&
        (hs->type & type) &&
        (hs->request == request)) {
       amount++;
@@ -107,7 +106,7 @@ CURLHcode curl_easy_header(CURL *easy,
   else {
     for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
       hs = Curl_node_elem(e);
-      if(strcasecompare(hs->name, name) &&
+      if(curl_strequal(hs->name, name) &&
          (hs->type & type) &&
          (hs->request == request) &&
          (match++ == nameindex)) {
@@ -173,7 +172,7 @@ struct curl_header *curl_easy_nextheader(CURL *easy,
      the index for the currently selected entry */
   for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
     struct Curl_header_store *check = Curl_node_elem(e);
-    if(strcasecompare(hs->name, check->name) &&
+    if(curl_strequal(hs->name, check->name) &&
        (check->request == request) &&
        (check->type & type))
       amount++;

+ 8 - 6
lib/hostip.c

@@ -54,7 +54,7 @@
 #include "rand.h"
 #include "share.h"
 #include "url.h"
-#include "inet_ntop.h"
+#include "curlx/inet_ntop.h"
 #include "curlx/inet_pton.h"
 #include "multiif.h"
 #include "doh.h"
@@ -145,14 +145,14 @@ void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf,
   case AF_INET: {
     const struct sockaddr_in *sa4 = (const void *)ai->ai_addr;
     const struct in_addr *ipaddr4 = &sa4->sin_addr;
-    (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize);
+    (void)curlx_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize);
     break;
   }
 #ifdef USE_IPV6
   case AF_INET6: {
     const struct sockaddr_in6 *sa6 = (const void *)ai->ai_addr;
     const struct in6_addr *ipaddr6 = &sa6->sin6_addr;
-    (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, bufsize);
+    (void)curlx_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, bufsize);
     break;
   }
 #endif
@@ -505,6 +505,8 @@ Curl_dnscache_mk_entry(struct Curl_easy *data,
       return NULL;
     }
   }
+#else
+  (void)data;
 #endif
   if(!hostlen)
     hostlen = strlen(hostname);
@@ -730,7 +732,7 @@ static bool tailmatch(const char *full, size_t flen,
 {
   if(plen > flen)
     return FALSE;
-  return strncasecompare(part, &full[flen - plen], plen);
+  return curl_strnequal(part, &full[flen - plen], plen);
 }
 
 static struct Curl_addrinfo *
@@ -872,8 +874,8 @@ CURLcode Curl_resolv(struct Curl_easy *data,
     goto error;
 
   if(!is_ipaddr &&
-     (strcasecompare(hostname, "localhost") ||
-      strcasecompare(hostname, "localhost.") ||
+     (curl_strequal(hostname, "localhost") ||
+      curl_strequal(hostname, "localhost.") ||
       tailmatch(hostname, hostname_len, STRCONST(".localhost")) ||
       tailmatch(hostname, hostname_len, STRCONST(".localhost.")))) {
     addr = get_localhost(port, hostname);

+ 3 - 1
lib/hostip6.c

@@ -44,6 +44,7 @@
 #endif
 
 #include "urldata.h"
+#include "cfilters.h"
 #include "sendf.h"
 #include "hostip.h"
 #include "hash.h"
@@ -104,7 +105,8 @@ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
 
   memset(&hints, 0, sizeof(hints));
   hints.ai_family = pf;
-  hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ?
+  hints.ai_socktype =
+    (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ?
     SOCK_STREAM : SOCK_DGRAM;
 
 #ifndef USE_RESOLVE_ON_IPS

+ 9 - 6
lib/hsts.c

@@ -33,7 +33,6 @@
 #include "llist.h"
 #include "hsts.h"
 #include "curl_get_line.h"
-#include "strcase.h"
 #include "sendf.h"
 #include "parsedate.h"
 #include "fopen.h"
@@ -155,7 +154,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
 
   do {
     curlx_str_passblanks(&p);
-    if(strncasecompare("max-age", p, 7)) {
+    if(curl_strnequal("max-age", p, 7)) {
       bool quoted = FALSE;
       int rc;
 
@@ -185,7 +184,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
       }
       gotma = TRUE;
     }
-    else if(strncasecompare("includesubdomains", p, 17)) {
+    else if(curl_strnequal("includesubdomains", p, 17)) {
       if(gotinc)
         return CURLE_BAD_FUNCTION_ARGUMENT;
       subdomains = TRUE;
@@ -272,15 +271,15 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
       if((subdomain && sts->includeSubDomains) && (ntail < hlen)) {
         size_t offs = hlen - ntail;
         if((hostname[offs-1] == '.') &&
-           strncasecompare(&hostname[offs], sts->host, ntail) &&
+           curl_strnequal(&hostname[offs], sts->host, ntail) &&
            (ntail > blen)) {
           /* save the tail match with the longest tail */
           bestsub = sts;
           blen = ntail;
         }
       }
-      /* avoid strcasecompare because the host name is not null-terminated */
-      if((hlen == ntail) && strncasecompare(hostname, sts->host, hlen))
+      /* avoid curl_strequal because the host name is not null-terminated */
+      if((hlen == ntail) && curl_strnequal(hostname, sts->host, hlen))
         return sts;
     }
   }
@@ -581,4 +580,8 @@ void Curl_hsts_loadfiles(struct Curl_easy *data)
   }
 }
 
+#if defined(DEBUGBUILD) || defined(UNITTESTS)
+#undef time
+#endif
+
 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */

+ 452 - 341
lib/http.c

@@ -232,7 +232,7 @@ CURLcode Curl_http_setup_conn(struct Curl_easy *data,
   connkeep(conn, "HTTP default");
   if(data->state.http_neg.wanted == CURL_HTTP_V3x) {
     /* only HTTP/3, needs to work */
-    CURLcode result = Curl_conn_may_http3(data, conn);
+    CURLcode result = Curl_conn_may_http3(data, conn, conn->transport_wanted);
     if(result)
       return result;
   }
@@ -259,7 +259,7 @@ char *Curl_checkProxyheaders(struct Curl_easy *data,
   for(head = (conn->bits.proxy && data->set.sep_headers) ?
         data->set.proxyheaders : data->set.headers;
       head; head = head->next) {
-    if(strncasecompare(head->data, thisheader, thislen) &&
+    if(curl_strnequal(head->data, thisheader, thislen) &&
        Curl_headersep(head->data[thislen]))
       return head->data;
   }
@@ -862,7 +862,7 @@ static bool authcmp(const char *auth, const char *line)
 {
   /* the auth string must not have an alnum following */
   size_t n = strlen(auth);
-  return strncasecompare(auth, line, n) && !ISALNUM(line[n]);
+  return curl_strnequal(auth, line, n) && !ISALNUM(line[n]);
 }
 #endif
 
@@ -1482,7 +1482,7 @@ Curl_compareheader(const char *headerline, /* line to check */
   DEBUGASSERT(header);
   DEBUGASSERT(content);
 
-  if(!strncasecompare(headerline, header, hlen))
+  if(!curl_strnequal(headerline, header, hlen))
     return FALSE; /* does not start with header */
 
   /* pass the header */
@@ -1497,7 +1497,7 @@ Curl_compareheader(const char *headerline, /* line to check */
     size_t len;
     p = curlx_str(&val);
     for(len = curlx_strlen(&val); len >= curlx_strlen(&val); len--, p++) {
-      if(strncasecompare(p, content, clen))
+      if(curl_strnequal(p, content, clen))
         return TRUE; /* match! */
     }
   }
@@ -1893,7 +1893,7 @@ static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn)
 
   ptr = Curl_checkheaders(data, STRCONST("Host"));
   if(ptr && (!data->state.this_is_a_follow ||
-             strcasecompare(data->state.first_host, conn->host.name))) {
+             curl_strequal(data->state.first_host, conn->host.name))) {
 #if !defined(CURL_DISABLE_COOKIES)
     /* If we have a given custom Host: header, we extract the hostname in
        order to possibly use it for cookie reasons later on. We only allow the
@@ -1929,7 +1929,7 @@ static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn)
     }
 #endif
 
-    if(!strcasecompare("Host:", ptr)) {
+    if(!curl_strequal("Host:", ptr)) {
       aptr->host = aprintf("Host:%s\r\n", &ptr[5]);
       if(!aptr->host)
         return CURLE_OUT_OF_MEMORY;
@@ -2005,7 +2005,7 @@ static CURLcode http_target(struct Curl_easy *data,
       return CURLE_OUT_OF_MEMORY;
     }
 
-    if(strcasecompare("http", data->state.up.scheme)) {
+    if(curl_strequal("http", data->state.up.scheme)) {
       /* when getting HTTP, we do not want the userinfo the URL */
       uc = curl_url_set(h, CURLUPART_USER, NULL, 0);
       if(uc) {
@@ -2034,7 +2034,7 @@ static CURLcode http_target(struct Curl_easy *data,
     if(result)
       return result;
 
-    if(strcasecompare("ftp", data->state.up.scheme)) {
+    if(curl_strequal("ftp", data->state.up.scheme)) {
       if(data->set.proxy_transfer_mode) {
         /* when doing ftp, append ;type=<a|i> if not present */
         char *type = strstr(path, ";type=");
@@ -2443,7 +2443,7 @@ static CURLcode http_cookies(struct Curl_easy *data,
         data->state.aptr.cookiehost : conn->host.name;
       const bool secure_context =
         conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) ||
-        strcasecompare("localhost", host) ||
+        curl_strequal("localhost", host) ||
         !strcmp(host, "127.0.0.1") ||
         !strcmp(host, "::1");
       Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
@@ -3001,376 +3001,488 @@ checkprotoprefix(struct Curl_easy *data, struct connectdata *conn,
    Curl_compareheader(hd, STRCONST(n), STRCONST(v)))
 
 /*
- * http_header() parses a single response header.
+ * http_header_a() parses a single response header starting with A.
  */
-static CURLcode http_header(struct Curl_easy *data,
-                            const char *hd, size_t hdlen)
+static CURLcode http_header_a(struct Curl_easy *data,
+                              const char *hd, size_t hdlen)
 {
-  struct connectdata *conn = data->conn;
-  CURLcode result;
-  struct SingleRequest *k = &data->req;
-  const char *v;
-
-  switch(hd[0]) {
-  case 'a':
-  case 'A':
 #ifndef CURL_DISABLE_ALTSVC
-    v = (data->asi &&
-         (Curl_conn_is_ssl(data->conn, FIRSTSOCKET) ||
+  const char *v;
+  struct connectdata *conn = data->conn;
+  v = (data->asi &&
+       (Curl_conn_is_ssl(data->conn, FIRSTSOCKET) ||
 #ifdef DEBUGBUILD
-          /* allow debug builds to circumvent the HTTPS restriction */
-          getenv("CURL_ALTSVC_HTTP")
+        /* allow debug builds to circumvent the HTTPS restriction */
+        getenv("CURL_ALTSVC_HTTP")
 #else
-          0
+        0
 #endif
-        )) ? HD_VAL(hd, hdlen, "Alt-Svc:") : NULL;
-    if(v) {
-      /* the ALPN of the current request */
-      enum alpnid id = (k->httpversion == 30) ? ALPN_h3 :
-                         (k->httpversion == 20) ? ALPN_h2 : ALPN_h1;
-      return Curl_altsvc_parse(data, data->asi, v, id, conn->host.name,
-                               curlx_uitous((unsigned int)conn->remote_port));
-    }
+         )) ? HD_VAL(hd, hdlen, "Alt-Svc:") : NULL;
+  if(v) {
+    /* the ALPN of the current request */
+    struct SingleRequest *k = &data->req;
+    enum alpnid id = (k->httpversion == 30) ? ALPN_h3 :
+      (k->httpversion == 20) ? ALPN_h2 : ALPN_h1;
+    return Curl_altsvc_parse(data, data->asi, v, id, conn->host.name,
+                             curlx_uitous((unsigned int)conn->remote_port));
+  }
+#else
+  (void)data;
+  (void)hd;
+  (void)hdlen;
 #endif
-    break;
-  case 'c':
-  case 'C':
-    /* Check for Content-Length: header lines to get size */
-    v = (!k->http_bodyless && !data->set.ignorecl) ?
-      HD_VAL(hd, hdlen, "Content-Length:") : NULL;
-    if(v) {
-      curl_off_t contentlength;
-      int offt = curlx_str_numblanks(&v, &contentlength);
-
-      if(offt == STRE_OK) {
-        k->size = contentlength;
-        k->maxdownload = k->size;
-      }
-      else if(offt == STRE_OVERFLOW) {
-        /* out of range */
-        if(data->set.max_filesize) {
-          failf(data, "Maximum file size exceeded");
-          return CURLE_FILESIZE_EXCEEDED;
-        }
-        streamclose(conn, "overflow content-length");
-        infof(data, "Overflow Content-Length: value");
-      }
-      else {
-        /* negative or just rubbish - bad HTTP */
-        failf(data, "Invalid Content-Length: value");
-        return CURLE_WEIRD_SERVER_REPLY;
-      }
-      return CURLE_OK;
-    }
-    v = (!k->http_bodyless && data->set.str[STRING_ENCODING]) ?
-      HD_VAL(hd, hdlen, "Content-Encoding:") : NULL;
-    if(v) {
-      /*
-       * Process Content-Encoding. Look for the values: identity,
-       * gzip, deflate, compress, x-gzip and x-compress. x-gzip and
-       * x-compress are the same as gzip and compress. (Sec 3.5 RFC
-       * 2616). zlib cannot handle compress. However, errors are
-       * handled further down when the response body is processed
-       */
-      return Curl_build_unencoding_stack(data, v, FALSE);
+  return CURLE_OK;
+}
+
+/*
+ * http_header_c() parses a single response header starting with C.
+ */
+static CURLcode http_header_c(struct Curl_easy *data,
+                              const char *hd, size_t hdlen)
+{
+  struct connectdata *conn = data->conn;
+  struct SingleRequest *k = &data->req;
+  const char *v;
+
+  /* Check for Content-Length: header lines to get size */
+  v = (!k->http_bodyless && !data->set.ignorecl) ?
+    HD_VAL(hd, hdlen, "Content-Length:") : NULL;
+  if(v) {
+    curl_off_t contentlength;
+    int offt = curlx_str_numblanks(&v, &contentlength);
+
+    if(offt == STRE_OK) {
+      k->size = contentlength;
+      k->maxdownload = k->size;
     }
-    /* check for Content-Type: header lines to get the MIME-type */
-    v = HD_VAL(hd, hdlen, "Content-Type:");
-    if(v) {
-      char *contenttype = Curl_copy_header_value(hd);
-      if(!contenttype)
-        return CURLE_OUT_OF_MEMORY;
-      if(!*contenttype)
-        /* ignore empty data */
-        free(contenttype);
-      else {
-        free(data->info.contenttype);
-        data->info.contenttype = contenttype;
+    else if(offt == STRE_OVERFLOW) {
+      /* out of range */
+      if(data->set.max_filesize) {
+        failf(data, "Maximum file size exceeded");
+        return CURLE_FILESIZE_EXCEEDED;
       }
-      return CURLE_OK;
-    }
-    if(HD_IS_AND_SAYS(hd, hdlen, "Connection:", "close")) {
-      /*
-       * [RFC 2616, section 8.1.2.1]
-       * "Connection: close" is HTTP/1.1 language and means that
-       * the connection will close when this request has been
-       * served.
-       */
-      streamclose(conn, "Connection: close used");
-      return CURLE_OK;
+      streamclose(conn, "overflow content-length");
+      infof(data, "Overflow Content-Length: value");
     }
-    if((k->httpversion == 10) &&
-       HD_IS_AND_SAYS(hd, hdlen, "Connection:", "keep-alive")) {
-      /*
-       * An HTTP/1.0 reply with the 'Connection: keep-alive' line
-       * tells us the connection will be kept alive for our
-       * pleasure. Default action for 1.0 is to close.
-       *
-       * [RFC2068, section 19.7.1] */
-      connkeep(conn, "Connection keep-alive");
-      infof(data, "HTTP/1.0 connection set to keep alive");
-      return CURLE_OK;
+    else {
+      /* negative or just rubbish - bad HTTP */
+      failf(data, "Invalid Content-Length: value");
+      return CURLE_WEIRD_SERVER_REPLY;
     }
-    v = !k->http_bodyless ? HD_VAL(hd, hdlen, "Content-Range:") : NULL;
-    if(v) {
-      /* Content-Range: bytes [num]-
-         Content-Range: bytes: [num]-
-         Content-Range: [num]-
-         Content-Range: [asterisk]/[total]
-
-         The second format was added since Sun's webserver
-         JavaWebServer/1.1.1 obviously sends the header this way!
-         The third added since some servers use that!
-         The fourth means the requested range was unsatisfied.
-      */
-
-      const char *ptr = v;
-
-      /* Move forward until first digit or asterisk */
-      while(*ptr && !ISDIGIT(*ptr) && *ptr != '*')
-        ptr++;
-
-      /* if it truly stopped on a digit */
-      if(ISDIGIT(*ptr)) {
-        if(!curlx_str_number(&ptr, &k->offset, CURL_OFF_T_MAX) &&
-           (data->state.resume_from == k->offset))
-          /* we asked for a resume and we got it */
-          k->content_range = TRUE;
-      }
-      else if(k->httpcode < 300)
-        data->state.resume_from = 0; /* get everything */
+    return CURLE_OK;
+  }
+  v = (!k->http_bodyless && data->set.str[STRING_ENCODING]) ?
+    HD_VAL(hd, hdlen, "Content-Encoding:") : NULL;
+  if(v) {
+    /*
+     * Process Content-Encoding. Look for the values: identity,
+     * gzip, deflate, compress, x-gzip and x-compress. x-gzip and
+     * x-compress are the same as gzip and compress. (Sec 3.5 RFC
+     * 2616). zlib cannot handle compress. However, errors are
+     * handled further down when the response body is processed
+     */
+    return Curl_build_unencoding_stack(data, v, FALSE);
+  }
+  /* check for Content-Type: header lines to get the MIME-type */
+  v = HD_VAL(hd, hdlen, "Content-Type:");
+  if(v) {
+    char *contenttype = Curl_copy_header_value(hd);
+    if(!contenttype)
+      return CURLE_OUT_OF_MEMORY;
+    if(!*contenttype)
+      /* ignore empty data */
+      free(contenttype);
+    else {
+      free(data->info.contenttype);
+      data->info.contenttype = contenttype;
     }
-    break;
-  case 'l':
-  case 'L':
-    v = (!k->http_bodyless &&
-         (data->set.timecondition || data->set.get_filetime)) ?
-        HD_VAL(hd, hdlen, "Last-Modified:") : NULL;
-    if(v) {
-      k->timeofdoc = Curl_getdate_capped(v);
-      if(data->set.get_filetime)
-        data->info.filetime = k->timeofdoc;
-      return CURLE_OK;
+    return CURLE_OK;
+  }
+  if(HD_IS_AND_SAYS(hd, hdlen, "Connection:", "close")) {
+    /*
+     * [RFC 2616, section 8.1.2.1]
+     * "Connection: close" is HTTP/1.1 language and means that
+     * the connection will close when this request has been
+     * served.
+     */
+    streamclose(conn, "Connection: close used");
+    return CURLE_OK;
+  }
+  if((k->httpversion == 10) &&
+     HD_IS_AND_SAYS(hd, hdlen, "Connection:", "keep-alive")) {
+    /*
+     * An HTTP/1.0 reply with the 'Connection: keep-alive' line
+     * tells us the connection will be kept alive for our
+     * pleasure. Default action for 1.0 is to close.
+     *
+     * [RFC2068, section 19.7.1] */
+    connkeep(conn, "Connection keep-alive");
+    infof(data, "HTTP/1.0 connection set to keep alive");
+    return CURLE_OK;
+  }
+  v = !k->http_bodyless ? HD_VAL(hd, hdlen, "Content-Range:") : NULL;
+  if(v) {
+    /* Content-Range: bytes [num]-
+       Content-Range: bytes: [num]-
+       Content-Range: [num]-
+       Content-Range: [asterisk]/[total]
+
+       The second format was added since Sun's webserver
+       JavaWebServer/1.1.1 obviously sends the header this way!
+       The third added since some servers use that!
+       The fourth means the requested range was unsatisfied.
+    */
+
+    const char *ptr = v;
+
+    /* Move forward until first digit or asterisk */
+    while(*ptr && !ISDIGIT(*ptr) && *ptr != '*')
+      ptr++;
+
+    /* if it truly stopped on a digit */
+    if(ISDIGIT(*ptr)) {
+      if(!curlx_str_number(&ptr, &k->offset, CURL_OFF_T_MAX) &&
+         (data->state.resume_from == k->offset))
+        /* we asked for a resume and we got it */
+        k->content_range = TRUE;
     }
-    if((k->httpcode >= 300 && k->httpcode < 400) &&
-            HD_IS(hd, hdlen, "Location:") &&
-            !data->req.location) {
-      /* this is the URL that the server advises us to use instead */
-      char *location = Curl_copy_header_value(hd);
-      if(!location)
-        return CURLE_OUT_OF_MEMORY;
-      if(!*location)
-        /* ignore empty data */
-        free(location);
-      else {
-        data->req.location = location;
+    else if(k->httpcode < 300)
+      data->state.resume_from = 0; /* get everything */
+  }
+  return CURLE_OK;
+}
 
-        if(data->set.http_follow_mode) {
-          DEBUGASSERT(!data->req.newurl);
-          data->req.newurl = strdup(data->req.location); /* clone */
-          if(!data->req.newurl)
-            return CURLE_OUT_OF_MEMORY;
+/*
+ * http_header_l() parses a single response header starting with L.
+ */
+static CURLcode http_header_l(struct Curl_easy *data,
+                              const char *hd, size_t hdlen)
+{
+  struct connectdata *conn = data->conn;
+  struct SingleRequest *k = &data->req;
+  const char *v = (!k->http_bodyless &&
+                   (data->set.timecondition || data->set.get_filetime)) ?
+    HD_VAL(hd, hdlen, "Last-Modified:") : NULL;
+  if(v) {
+    k->timeofdoc = Curl_getdate_capped(v);
+    if(data->set.get_filetime)
+      data->info.filetime = k->timeofdoc;
+    return CURLE_OK;
+  }
+  if((k->httpcode >= 300 && k->httpcode < 400) &&
+     HD_IS(hd, hdlen, "Location:") &&
+     !data->req.location) {
+    /* this is the URL that the server advises us to use instead */
+    char *location = Curl_copy_header_value(hd);
+    if(!location)
+      return CURLE_OUT_OF_MEMORY;
+    if(!*location)
+      /* ignore empty data */
+      free(location);
+    else {
+      data->req.location = location;
 
-          /* some cases of POST and PUT etc needs to rewind the data
-             stream at this point */
-          result = http_perhapsrewind(data, conn);
-          if(result)
-            return result;
+      if(data->set.http_follow_mode) {
+        CURLcode result;
+        DEBUGASSERT(!data->req.newurl);
+        data->req.newurl = strdup(data->req.location); /* clone */
+        if(!data->req.newurl)
+          return CURLE_OUT_OF_MEMORY;
 
-          /* mark the next request as a followed location: */
-          data->state.this_is_a_follow = TRUE;
-        }
+        /* some cases of POST and PUT etc needs to rewind the data
+           stream at this point */
+        result = http_perhapsrewind(data, conn);
+        if(result)
+          return result;
+
+        /* mark the next request as a followed location: */
+        data->state.this_is_a_follow = TRUE;
       }
     }
-    break;
-  case 'p':
-  case 'P':
+  }
+  return CURLE_OK;
+}
+
+/*
+ * http_header_p() parses a single response header starting with P.
+ */
+static CURLcode http_header_p(struct Curl_easy *data,
+                              const char *hd, size_t hdlen)
+{
+  struct SingleRequest *k = &data->req;
+
 #ifndef CURL_DISABLE_PROXY
-    v = HD_VAL(hd, hdlen, "Proxy-Connection:");
-    if(v) {
-      if((k->httpversion == 10) && conn->bits.httpproxy &&
-         HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) {
-        /*
-         * When an HTTP/1.0 reply comes when using a proxy, the
-         * 'Proxy-Connection: keep-alive' line tells us the
-         * connection will be kept alive for our pleasure.
-         * Default action for 1.0 is to close.
-         */
-        connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */
-        infof(data, "HTTP/1.0 proxy connection set to keep alive");
-      }
-      else if((k->httpversion == 11) && conn->bits.httpproxy &&
-              HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) {
-        /*
-         * We get an HTTP/1.1 response from a proxy and it says it will
-         * close down after this transfer.
-         */
-        connclose(conn, "Proxy-Connection: asked to close after done");
-        infof(data, "HTTP/1.1 proxy connection set close");
-      }
-      return CURLE_OK;
+  const char *v = HD_VAL(hd, hdlen, "Proxy-Connection:");
+  if(v) {
+    struct connectdata *conn = data->conn;
+    if((k->httpversion == 10) && conn->bits.httpproxy &&
+       HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) {
+      /*
+       * When an HTTP/1.0 reply comes when using a proxy, the
+       * 'Proxy-Connection: keep-alive' line tells us the
+       * connection will be kept alive for our pleasure.
+       * Default action for 1.0 is to close.
+       */
+      connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */
+      infof(data, "HTTP/1.0 proxy connection set to keep alive");
     }
-#endif
-    if((407 == k->httpcode) && HD_IS(hd, hdlen, "Proxy-authenticate:")) {
-      char *auth = Curl_copy_header_value(hd);
-      if(!auth)
-        return CURLE_OUT_OF_MEMORY;
-      result = Curl_http_input_auth(data, TRUE, auth);
-      free(auth);
-      return result;
+    else if((k->httpversion == 11) && conn->bits.httpproxy &&
+            HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) {
+      /*
+       * We get an HTTP/1.1 response from a proxy and it says it will
+       * close down after this transfer.
+       */
+      connclose(conn, "Proxy-Connection: asked to close after done");
+      infof(data, "HTTP/1.1 proxy connection set close");
     }
+    return CURLE_OK;
+  }
+#endif
+  if((407 == k->httpcode) && HD_IS(hd, hdlen, "Proxy-authenticate:")) {
+    char *auth = Curl_copy_header_value(hd);
+    CURLcode result;
+    if(!auth)
+      return CURLE_OUT_OF_MEMORY;
+    result = Curl_http_input_auth(data, TRUE, auth);
+    free(auth);
+    return result;
+  }
 #ifdef USE_SPNEGO
-    if(HD_IS(hd, hdlen, "Persistent-Auth:")) {
-      struct negotiatedata *negdata = &conn->negotiate;
-      struct auth *authp = &data->state.authhost;
-      if(authp->picked == CURLAUTH_NEGOTIATE) {
-        char *persistentauth = Curl_copy_header_value(hd);
-        if(!persistentauth)
-          return CURLE_OUT_OF_MEMORY;
-        negdata->noauthpersist = !!checkprefix("false", persistentauth);
-        negdata->havenoauthpersist = TRUE;
-        infof(data, "Negotiate: noauthpersist -> %d, header part: %s",
-              negdata->noauthpersist, persistentauth);
-        free(persistentauth);
-      }
+  if(HD_IS(hd, hdlen, "Persistent-Auth:")) {
+    struct connectdata *conn = data->conn;
+    struct negotiatedata *negdata = Curl_auth_nego_get(conn, FALSE);
+    struct auth *authp = &data->state.authhost;
+    if(!negdata)
+      return CURLE_OUT_OF_MEMORY;
+    if(authp->picked == CURLAUTH_NEGOTIATE) {
+      char *persistentauth = Curl_copy_header_value(hd);
+      if(!persistentauth)
+        return CURLE_OUT_OF_MEMORY;
+      negdata->noauthpersist = !!checkprefix("false", persistentauth);
+      negdata->havenoauthpersist = TRUE;
+      infof(data, "Negotiate: noauthpersist -> %d, header part: %s",
+            negdata->noauthpersist, persistentauth);
+      free(persistentauth);
     }
+  }
 #endif
-    break;
-  case 'r':
-  case 'R':
-    v = HD_VAL(hd, hdlen, "Retry-After:");
-    if(v) {
-      /* Retry-After = HTTP-date / delay-seconds */
-      curl_off_t retry_after = 0; /* zero for unknown or "now" */
-      time_t date;
-      curlx_str_passblanks(&v);
-
-      /* try it as a date first, because a date can otherwise start with and
-         get treated as a number */
-      date = Curl_getdate_capped(v);
-
-      if((time_t)-1 != date) {
-        time_t current = time(NULL);
-        if(date >= current)
-          /* convert date to number of seconds into the future */
-          retry_after = date - current;
-      }
-      else
-        /* Try it as a decimal number */
-        curlx_str_number(&v, &retry_after, CURL_OFF_T_MAX);
-      /* limit to 6 hours max. this is not documented so that it can be changed
-         in the future if necessary. */
-      if(retry_after > 21600)
-        retry_after = 21600;
-      data->info.retry_after = retry_after;
-      return CURLE_OK;
-    }
-    break;
-  case 's':
-  case 'S':
-#if !defined(CURL_DISABLE_COOKIES)
-    v = (data->cookies && data->state.cookie_engine) ?
-        HD_VAL(hd, hdlen, "Set-Cookie:") : NULL;
-    if(v) {
-      /* If there is a custom-set Host: name, use it here, or else use
-       * real peer hostname. */
-      const char *host = data->state.aptr.cookiehost ?
-        data->state.aptr.cookiehost : conn->host.name;
-      const bool secure_context =
-        conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) ||
-        strcasecompare("localhost", host) ||
-        !strcmp(host, "127.0.0.1") ||
-        !strcmp(host, "::1");
+  return CURLE_OK;
+}
 
-      Curl_share_lock(data, CURL_LOCK_DATA_COOKIE,
-                      CURL_LOCK_ACCESS_SINGLE);
-      Curl_cookie_add(data, data->cookies, TRUE, FALSE, v, host,
-                      data->state.up.path, secure_context);
-      Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
-      return CURLE_OK;
+/*
+ * http_header_r() parses a single response header starting with R.
+ */
+static CURLcode http_header_r(struct Curl_easy *data,
+                              const char *hd, size_t hdlen)
+{
+  const char *v = HD_VAL(hd, hdlen, "Retry-After:");
+  if(v) {
+    /* Retry-After = HTTP-date / delay-seconds */
+    curl_off_t retry_after = 0; /* zero for unknown or "now" */
+    time_t date;
+    curlx_str_passblanks(&v);
+
+    /* try it as a date first, because a date can otherwise start with and
+       get treated as a number */
+    date = Curl_getdate_capped(v);
+
+    if((time_t)-1 != date) {
+      time_t current = time(NULL);
+      if(date >= current)
+        /* convert date to number of seconds into the future */
+        retry_after = date - current;
     }
+    else
+      /* Try it as a decimal number, ignore errors */
+      (void)curlx_str_number(&v, &retry_after, CURL_OFF_T_MAX);
+    /* limit to 6 hours max. this is not documented so that it can be changed
+       in the future if necessary. */
+    if(retry_after > 21600)
+      retry_after = 21600;
+    data->info.retry_after = retry_after;
+  }
+  return CURLE_OK;
+}
+
+/*
+ * http_header_s() parses a single response header starting with S.
+ */
+static CURLcode http_header_s(struct Curl_easy *data,
+                              const char *hd, size_t hdlen)
+{
+#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_HSTS)
+  struct connectdata *conn = data->conn;
+  const char *v;
+#else
+  (void)data;
+  (void)hd;
+  (void)hdlen;
+#endif
+
+#if !defined(CURL_DISABLE_COOKIES)
+  v = (data->cookies && data->state.cookie_engine) ?
+    HD_VAL(hd, hdlen, "Set-Cookie:") : NULL;
+  if(v) {
+    /* If there is a custom-set Host: name, use it here, or else use
+     * real peer hostname. */
+    const char *host = data->state.aptr.cookiehost ?
+      data->state.aptr.cookiehost : conn->host.name;
+    const bool secure_context =
+      conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) ||
+      curl_strequal("localhost", host) ||
+      !strcmp(host, "127.0.0.1") ||
+      !strcmp(host, "::1");
+
+    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE,
+                    CURL_LOCK_ACCESS_SINGLE);
+    Curl_cookie_add(data, data->cookies, TRUE, FALSE, v, host,
+                    data->state.up.path, secure_context);
+    Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
+    return CURLE_OK;
+  }
 #endif
 #ifndef CURL_DISABLE_HSTS
-    /* If enabled, the header is incoming and this is over HTTPS */
-    v = (data->hsts &&
-         (Curl_conn_is_ssl(conn, FIRSTSOCKET) ||
+  /* If enabled, the header is incoming and this is over HTTPS */
+  v = (data->hsts &&
+       (Curl_conn_is_ssl(conn, FIRSTSOCKET) ||
 #ifdef DEBUGBUILD
-           /* allow debug builds to circumvent the HTTPS restriction */
-           getenv("CURL_HSTS_HTTP")
+        /* allow debug builds to circumvent the HTTPS restriction */
+        getenv("CURL_HSTS_HTTP")
 #else
-           0
+        0
 #endif
-            )
-        ) ? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL;
-    if(v) {
-      CURLcode check =
-        Curl_hsts_parse(data->hsts, conn->host.name, v);
-      if(check)
-        infof(data, "Illegal STS header skipped");
+         )
+    ) ? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL;
+  if(v) {
+    CURLcode check =
+      Curl_hsts_parse(data->hsts, conn->host.name, v);
+    if(check)
+      infof(data, "Illegal STS header skipped");
 #ifdef DEBUGBUILD
-      else
-        infof(data, "Parsed STS header fine (%zu entries)",
-              Curl_llist_count(&data->hsts->list));
+    else
+      infof(data, "Parsed STS header fine (%zu entries)",
+            Curl_llist_count(&data->hsts->list));
 #endif
-    }
+  }
 #endif
+
+  return CURLE_OK;
+}
+
+/*
+ * http_header_t() parses a single response header starting with T.
+ */
+static CURLcode http_header_t(struct Curl_easy *data,
+                              const char *hd, size_t hdlen)
+{
+  struct connectdata *conn = data->conn;
+  struct SingleRequest *k = &data->req;
+
+  /* RFC 9112, ch. 6.1
+   * "Transfer-Encoding MAY be sent in a response to a HEAD request or
+   *  in a 304 (Not Modified) response (Section 15.4.5 of [HTTP]) to a
+   *  GET request, neither of which includes a message body, to indicate
+   *  that the origin server would have applied a transfer coding to the
+   *  message body if the request had been an unconditional GET."
+   *
+   * Read: in these cases the 'Transfer-Encoding' does not apply
+   * to any data following the response headers. Do not add any decoders.
+   */
+  const char *v = (!k->http_bodyless &&
+                   (data->state.httpreq != HTTPREQ_HEAD) &&
+                   (k->httpcode != 304)) ?
+    HD_VAL(hd, hdlen, "Transfer-Encoding:") : NULL;
+  if(v) {
+    /* One or more encodings. We check for chunked and/or a compression
+       algorithm. */
+    CURLcode result = Curl_build_unencoding_stack(data, v, TRUE);
+    if(result)
+      return result;
+    if(!k->chunk && data->set.http_transfer_encoding) {
+      /* if this is not chunked, only close can signal the end of this
+       * transfer as Content-Length is said not to be trusted for
+       * transfer-encoding! */
+      connclose(conn, "HTTP/1.1 transfer-encoding without chunks");
+      k->ignore_cl = TRUE;
+    }
+    return CURLE_OK;
+  }
+  v = HD_VAL(hd, hdlen, "Trailer:");
+  if(v) {
+    data->req.resp_trailer = TRUE;
+    return CURLE_OK;
+  }
+  return CURLE_OK;
+}
+
+/*
+ * http_header_w() parses a single response header starting with W.
+ */
+static CURLcode http_header_w(struct Curl_easy *data,
+                              const char *hd, size_t hdlen)
+{
+  struct SingleRequest *k = &data->req;
+  CURLcode result = CURLE_OK;
+
+  if((401 == k->httpcode) && HD_IS(hd, hdlen, "WWW-Authenticate:")) {
+    char *auth = Curl_copy_header_value(hd);
+    if(!auth)
+      return CURLE_OUT_OF_MEMORY;
+    result = Curl_http_input_auth(data, FALSE, auth);
+    free(auth);
+  }
+  return result;
+}
+
+/*
+ * http_header() parses a single response header.
+ */
+static CURLcode http_header(struct Curl_easy *data,
+                            const char *hd, size_t hdlen)
+{
+  CURLcode result = CURLE_OK;
+
+  switch(hd[0]) {
+  case 'a':
+  case 'A':
+    result = http_header_a(data, hd, hdlen);
+    break;
+  case 'c':
+  case 'C':
+    result = http_header_c(data, hd, hdlen);
+    break;
+  case 'l':
+  case 'L':
+    result = http_header_l(data, hd, hdlen);
+    break;
+  case 'p':
+  case 'P':
+    result = http_header_p(data, hd, hdlen);
+    break;
+  case 'r':
+  case 'R':
+    result = http_header_r(data, hd, hdlen);
+    break;
+  case 's':
+  case 'S':
+    result = http_header_s(data, hd, hdlen);
     break;
   case 't':
   case 'T':
-    /* RFC 9112, ch. 6.1
-     * "Transfer-Encoding MAY be sent in a response to a HEAD request or
-     *  in a 304 (Not Modified) response (Section 15.4.5 of [HTTP]) to a
-     *  GET request, neither of which includes a message body, to indicate
-     *  that the origin server would have applied a transfer coding to the
-     *  message body if the request had been an unconditional GET."
-     *
-     * Read: in these cases the 'Transfer-Encoding' does not apply
-     * to any data following the response headers. Do not add any decoders.
-     */
-    v = (!k->http_bodyless &&
-         (data->state.httpreq != HTTPREQ_HEAD) &&
-         (k->httpcode != 304)) ?
-      HD_VAL(hd, hdlen, "Transfer-Encoding:") : NULL;
-    if(v) {
-      /* One or more encodings. We check for chunked and/or a compression
-         algorithm. */
-      result = Curl_build_unencoding_stack(data, v, TRUE);
-      if(result)
-        return result;
-      if(!k->chunk && data->set.http_transfer_encoding) {
-        /* if this is not chunked, only close can signal the end of this
-         * transfer as Content-Length is said not to be trusted for
-         * transfer-encoding! */
-        connclose(conn, "HTTP/1.1 transfer-encoding without chunks");
-        k->ignore_cl = TRUE;
-      }
-      return CURLE_OK;
-    }
-    v = HD_VAL(hd, hdlen, "Trailer:");
-    if(v) {
-      data->req.resp_trailer = TRUE;
-      return CURLE_OK;
-    }
+    result = http_header_t(data, hd, hdlen);
     break;
   case 'w':
   case 'W':
-    if((401 == k->httpcode) && HD_IS(hd, hdlen, "WWW-Authenticate:")) {
-      char *auth = Curl_copy_header_value(hd);
-      if(!auth)
-        return CURLE_OUT_OF_MEMORY;
-      result = Curl_http_input_auth(data, FALSE, auth);
-      free(auth);
-      return result;
-    }
+    result = http_header_w(data, hd, hdlen);
     break;
   }
 
-  if(conn->handler->protocol & CURLPROTO_RTSP) {
-    result = Curl_rtsp_parseheader(data, hd);
-    if(result)
-      return result;
+  if(!result) {
+    struct connectdata *conn = data->conn;
+    if(conn->handler->protocol & CURLPROTO_RTSP)
+      result = Curl_rtsp_parseheader(data, hd);
   }
-  return CURLE_OK;
+  return result;
 }
 
 /*
@@ -3850,9 +3962,8 @@ static CURLcode http_on_response(struct Curl_easy *data,
 out:
   if(last_hd) {
     /* if not written yet, write it now */
-    CURLcode r2 = http_write_header(data, last_hd, last_hd_len);
-    if(!result)
-      result = r2;
+    result = Curl_1st_err(
+      result, http_write_header(data, last_hd, last_hd_len));
   }
   return result;
 }
@@ -4474,7 +4585,7 @@ static bool h2_permissible_field(struct dynhds_entry *e)
     if(e->namelen < H2_NON_FIELD[i].namelen)
       return TRUE;
     if(e->namelen == H2_NON_FIELD[i].namelen &&
-       strcasecompare(H2_NON_FIELD[i].name, e->name))
+       curl_strequal(H2_NON_FIELD[i].name, e->name))
       return FALSE;
   }
   return TRUE;
@@ -4565,7 +4676,7 @@ CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers,
     e = Curl_dynhds_getn(&req->headers, i);
     /* "TE" is special in that it is only permissible when it
      * has only value "trailers". RFC 9113 ch. 8.2.2 */
-    if(e->namelen == 2 && strcasecompare("TE", e->name)) {
+    if(e->namelen == 2 && curl_strequal("TE", e->name)) {
       if(http_TE_has_token(e->value, "trailers"))
         result = Curl_dynhds_add(h2_headers, e->name, e->namelen,
                                  "trailers", sizeof("trailers") - 1);

+ 171 - 227
lib/http2.c

@@ -36,7 +36,6 @@
 #include "sendf.h"
 #include "select.h"
 #include "curlx/base64.h"
-#include "strcase.h"
 #include "multiif.h"
 #include "url.h"
 #include "urlapi-int.h"
@@ -205,6 +204,9 @@ static void cf_h2_ctx_close(struct cf_h2_ctx *ctx)
   }
 }
 
+static CURLcode nw_out_flush(struct Curl_cfilter *cf,
+                             struct Curl_easy *data);
+
 static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
                                    struct Curl_easy *data);
 
@@ -374,27 +376,6 @@ static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf,
 }
 #endif /* !NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE */
 
-/*
- * Mark this transfer to get "drained".
- */
-static void drain_stream(struct Curl_cfilter *cf,
-                         struct Curl_easy *data,
-                         struct h2_stream_ctx *stream)
-{
-  unsigned char bits;
-
-  (void)cf;
-  bits = CURL_CSELECT_IN;
-  if(!stream->closed &&
-     (!stream->body_eos || !Curl_bufq_is_empty(&stream->sendbuf)))
-    bits |= CURL_CSELECT_OUT;
-  if(stream->closed || (data->state.select_bits != bits)) {
-    CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x",
-                stream->id, bits);
-    data->state.select_bits = bits;
-    Curl_expire(data, 0, EXPIRE_RUN_NOW);
-  }
-}
 
 static CURLcode http2_data_setup(struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
@@ -449,8 +430,10 @@ static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
       flush_egress = TRUE;
     }
 
-    if(flush_egress)
-      nghttp2_session_send(ctx->h2);
+    if(flush_egress) {
+      (void)nghttp2_session_send(ctx->h2);
+      (void)nw_out_flush(cf, data);
+    }
   }
 
   Curl_uint_hash_remove(&ctx->streams, data->mid);
@@ -480,33 +463,6 @@ static int h2_client_new(struct Curl_cfilter *cf,
   return rc;
 }
 
-static ssize_t nw_in_reader(void *reader_ctx,
-                              unsigned char *buf, size_t buflen,
-                              CURLcode *err)
-{
-  struct Curl_cfilter *cf = reader_ctx;
-  struct Curl_easy *data = CF_DATA_CURRENT(cf);
-
-  return Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err);
-}
-
-static ssize_t nw_out_writer(void *writer_ctx,
-                             const unsigned char *buf, size_t buflen,
-                             CURLcode *err)
-{
-  struct Curl_cfilter *cf = writer_ctx;
-  struct Curl_easy *data = CF_DATA_CURRENT(cf);
-
-  if(data) {
-    ssize_t nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf,
-                                         buflen, FALSE, err);
-    if(nwritten > 0)
-      CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten);
-    return nwritten;
-  }
-  return 0;
-}
-
 static ssize_t send_callback(nghttp2_session *h2,
                              const uint8_t *mem, size_t length, int flags,
                              void *userp);
@@ -727,12 +683,12 @@ static bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data,
        not in use by any other transfer, there should not be any data here,
        only "protocol frames" */
     CURLcode result;
-    ssize_t nread = -1;
+    size_t nread;
 
     *input_pending = FALSE;
-    nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result);
-    if(nread != -1) {
-      CURL_TRC_CF(data, cf, "%zd bytes stray data read before trying "
+    result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread);
+    if(!result) {
+      CURL_TRC_CF(data, cf, "%zu bytes stray data read before trying "
                   "h2 connection", nread);
       if(h2_process_pending_input(cf, data, &result) < 0)
         /* immediate error, considered dead */
@@ -785,15 +741,16 @@ static CURLcode nw_out_flush(struct Curl_cfilter *cf,
                              struct Curl_easy *data)
 {
   struct cf_h2_ctx *ctx = cf->ctx;
-  ssize_t nwritten;
+  size_t nwritten;
   CURLcode result;
 
   (void)data;
   if(Curl_bufq_is_empty(&ctx->outbufq))
     return CURLE_OK;
 
-  nwritten = Curl_bufq_pass(&ctx->outbufq, nw_out_writer, cf, &result);
-  if(nwritten < 0) {
+  result = Curl_cf_send_bufq(cf->next, data, &ctx->outbufq, NULL, 0,
+                             &nwritten);
+  if(result) {
     if(result == CURLE_AGAIN) {
       CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN",
                   Curl_bufq_len(&ctx->outbufq));
@@ -816,7 +773,7 @@ static ssize_t send_callback(nghttp2_session *h2,
   struct Curl_cfilter *cf = userp;
   struct cf_h2_ctx *ctx = cf->ctx;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
-  ssize_t nwritten;
+  size_t nwritten;
   CURLcode result = CURLE_OK;
 
   (void)h2;
@@ -824,11 +781,12 @@ static ssize_t send_callback(nghttp2_session *h2,
   DEBUGASSERT(data);
 
   if(!cf->connected)
-    nwritten = Curl_bufq_write(&ctx->outbufq, buf, blen, &result);
+    result = Curl_bufq_write(&ctx->outbufq, buf, blen, &nwritten);
   else
-    nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen,
-                                    nw_out_writer, cf, &result);
-  if(nwritten < 0) {
+    result = Curl_cf_send_bufq(cf->next, data, &ctx->outbufq, buf, blen,
+                               &nwritten);
+
+  if(result) {
     if(result == CURLE_AGAIN) {
       ctx->nw_out_blocked = 1;
       return NGHTTP2_ERR_WOULDBLOCK;
@@ -841,7 +799,8 @@ static ssize_t send_callback(nghttp2_session *h2,
     ctx->nw_out_blocked = 1;
     return NGHTTP2_ERR_WOULDBLOCK;
   }
-  return nwritten;
+  return (nwritten  > SSIZE_T_MAX) ?
+    NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nwritten;
 }
 
 
@@ -1191,7 +1150,7 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
     if(stream->status_code / 100 != 1) {
       stream->resp_hds_complete = TRUE;
     }
-    drain_stream(cf, data, stream);
+    Curl_multi_mark_dirty(data);
     break;
   case NGHTTP2_PUSH_PROMISE:
     rv = push_promise(cf, data, &frame->push_promise);
@@ -1214,12 +1173,12 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
     if(frame->rst_stream.error_code) {
       stream->reset = TRUE;
     }
-    drain_stream(cf, data, stream);
+    Curl_multi_mark_dirty(data);
     break;
   case NGHTTP2_WINDOW_UPDATE:
     if(CURL_WANT_SEND(data) && Curl_bufq_is_empty(&stream->sendbuf)) {
       /* need more data, force processing of transfer */
-      drain_stream(cf, data, stream);
+      Curl_multi_mark_dirty(data);
     }
     else if(!Curl_bufq_is_empty(&stream->sendbuf)) {
       /* resume the potentially suspended stream */
@@ -1245,7 +1204,7 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
                                 stream->id, NGHTTP2_STREAM_CLOSED);
       stream->closed = TRUE;
     }
-    drain_stream(cf, data, stream);
+    Curl_multi_mark_dirty(data);
   }
   return CURLE_OK;
 }
@@ -1394,11 +1353,8 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
          * window and *assume* that we treat this like a WINDOW_UPDATE. Some
          * servers send an explicit WINDOW_UPDATE, but not all seem to do that.
          * To be safe, we UNHOLD a stream in order not to stall. */
-        if(CURL_WANT_SEND(data)) {
-          struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
-          if(stream)
-            drain_stream(cf, data, stream);
-        }
+        if(CURL_WANT_SEND(data))
+          Curl_multi_mark_dirty(data);
       }
       break;
     }
@@ -1543,7 +1499,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
               stream_id, nghttp2_http2_strerror(error_code), error_code);
   else
     CURL_TRC_CF(data_s, cf, "[%d] CLOSED", stream_id);
-  drain_stream(cf, data_s, stream);
+  Curl_multi_mark_dirty(data_s);
 
   /* remove `data_s` from the nghttp2 stream */
   rv = nghttp2_session_set_stream_user_data(session, stream_id, 0);
@@ -1641,9 +1597,9 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
       if(!check)
         /* no memory */
         return NGHTTP2_ERR_CALLBACK_FAILURE;
-      if(!strcasecompare(check, (const char *)value) &&
+      if(!curl_strequal(check, (const char *)value) &&
          ((cf->conn->remote_port != cf->conn->given->defport) ||
-          !strcasecompare(cf->conn->host.name, (const char *)value))) {
+          !curl_strequal(cf->conn->host.name, (const char *)value))) {
         /* This is push is not for the same authority that was asked for in
          * the URL. RFC 7540 section 8.2 says: "A client MUST treat a
          * PUSH_PROMISE for which the server is not authoritative as a stream
@@ -1737,7 +1693,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
     }
     /* if we receive data for another handle, wake that up */
     if(CF_DATA_CURRENT(cf) != data_s)
-      Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
+      Curl_multi_mark_dirty(data_s);
 
     CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d",
                 stream->id, stream->status_code);
@@ -1764,7 +1720,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
   }
   /* if we receive data for another handle, wake that up */
   if(CF_DATA_CURRENT(cf) != data_s)
-    Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
+    Curl_multi_mark_dirty(data_s);
 
   CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s",
               stream->id, (int)namelen, name, (int)valuelen, value);
@@ -1785,6 +1741,7 @@ static ssize_t req_body_read_callback(nghttp2_session *session,
   struct h2_stream_ctx *stream = NULL;
   CURLcode result;
   ssize_t nread;
+  size_t n;
   (void)source;
 
   (void)cf;
@@ -1803,12 +1760,14 @@ static ssize_t req_body_read_callback(nghttp2_session *session,
   if(!stream)
     return NGHTTP2_ERR_CALLBACK_FAILURE;
 
-  nread = Curl_bufq_read(&stream->sendbuf, buf, length, &result);
-  if(nread < 0) {
+  result = Curl_bufq_read(&stream->sendbuf, buf, length, &n);
+  if(result) {
     if(result != CURLE_AGAIN)
       return NGHTTP2_ERR_CALLBACK_FAILURE;
     nread = 0;
   }
+  else
+    nread = (ssize_t)n;
 
   CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) eos=%d -> %zd, %d",
               stream_id, length, stream->body_eos, nread, result);
@@ -1874,20 +1833,20 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
   return result;
 }
 
-static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
-                                         struct Curl_easy *data,
-                                         struct h2_stream_ctx *stream,
-                                         CURLcode *err)
+static CURLcode http2_handle_stream_close(struct Curl_cfilter *cf,
+                                          struct Curl_easy *data,
+                                          struct h2_stream_ctx *stream,
+                                          size_t *pnlen)
 {
-  ssize_t rv = 0;
+  CURLcode result;
 
+  *pnlen = 0;
   if(stream->error == NGHTTP2_REFUSED_STREAM) {
     CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new "
                 "connection", stream->id);
     connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */
     data->state.refused_stream = TRUE;
-    *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
-    return -1;
+    return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
   }
   else if(stream->error != NGHTTP2_NO_ERROR) {
     if(stream->resp_hds_complete && data->req.no_body) {
@@ -1896,27 +1855,23 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
                   stream->id, nghttp2_http2_strerror(stream->error),
                   stream->error);
       stream->close_handled = TRUE;
-      *err = CURLE_OK;
-      goto out;
+      return CURLE_OK;
     }
     failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)",
           stream->id, nghttp2_http2_strerror(stream->error),
           stream->error);
-    *err = CURLE_HTTP2_STREAM;
-    return -1;
+    return CURLE_HTTP2_STREAM;
   }
   else if(stream->reset) {
     failf(data, "HTTP/2 stream %u was reset", stream->id);
-    *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2;
-    return -1;
+    return data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2;
   }
 
   if(!stream->bodystarted) {
     failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
           " all response header fields, treated as error",
           stream->id);
-    *err = CURLE_HTTP2_STREAM;
-    return -1;
+    return CURLE_HTTP2_STREAM;
   }
 
   if(Curl_dynhds_count(&stream->resp_trailers)) {
@@ -1924,37 +1879,36 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
     struct dynbuf dbuf;
     size_t i;
 
-    *err = CURLE_OK;
+    result = CURLE_OK;
     curlx_dyn_init(&dbuf, DYN_TRAILERS);
     for(i = 0; i < Curl_dynhds_count(&stream->resp_trailers); ++i) {
       e = Curl_dynhds_getn(&stream->resp_trailers, i);
       if(!e)
         break;
       curlx_dyn_reset(&dbuf);
-      *err = curlx_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a",
+      result = curlx_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a",
                             (int)e->namelen, e->name,
                             (int)e->valuelen, e->value);
-      if(*err)
+      if(result)
         break;
       Curl_debug(data, CURLINFO_HEADER_IN, curlx_dyn_ptr(&dbuf),
                  curlx_dyn_len(&dbuf));
-      *err = Curl_client_write(data, CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER,
-                               curlx_dyn_ptr(&dbuf), curlx_dyn_len(&dbuf));
-      if(*err)
+      result = Curl_client_write(data, CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER,
+                                 curlx_dyn_ptr(&dbuf), curlx_dyn_len(&dbuf));
+      if(result)
         break;
     }
     curlx_dyn_free(&dbuf);
-    if(*err)
+    if(result)
       goto out;
   }
 
   stream->close_handled = TRUE;
-  *err = CURLE_OK;
-  rv = 0;
+  result = CURLE_OK;
 
 out:
-  CURL_TRC_CF(data, cf, "handle_stream_close -> %zd, %d", rv, *err);
-  return rv;
+  CURL_TRC_CF(data, cf, "handle_stream_close -> %d, %zu", result, *pnlen);
+  return result;
 }
 
 static int sweight_wanted(const struct Curl_easy *data)
@@ -1997,7 +1951,7 @@ static void h2_pri_spec(struct cf_h2_ctx *ctx,
  * Flush any out data pending in the network buffer.
  */
 static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data)
+                                   struct Curl_easy *data)
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
@@ -2037,36 +1991,36 @@ out:
   return nw_out_flush(cf, data);
 }
 
-static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
-                           struct h2_stream_ctx *stream,
-                           char *buf, size_t len, CURLcode *err)
+static CURLcode stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+                            struct h2_stream_ctx *stream,
+                            char *buf, size_t len, size_t *pnread)
 {
   struct cf_h2_ctx *ctx = cf->ctx;
-  ssize_t nread = -1;
+  CURLcode result = CURLE_AGAIN;
 
   (void)buf;
-  *err = CURLE_AGAIN;
+  (void)len;
+  *pnread = 0;
+
   if(stream->xfer_result) {
     CURL_TRC_CF(data, cf, "[%d] xfer write failed", stream->id);
-    *err = stream->xfer_result;
-    nread = -1;
+    result = stream->xfer_result;
   }
   else if(stream->closed) {
     CURL_TRC_CF(data, cf, "[%d] returning CLOSE", stream->id);
-    nread = http2_handle_stream_close(cf, data, stream, err);
+    result = http2_handle_stream_close(cf, data, stream, pnread);
   }
   else if(stream->reset ||
           (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) ||
           (ctx->rcvd_goaway && ctx->remote_max_sid < stream->id)) {
     CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id);
-    *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2;
-    nread = -1;
+    result = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2;
   }
 
-  if(nread < 0 && *err != CURLE_AGAIN)
-    CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %zd, %d",
-                stream->id, len, nread, *err);
-  return nread;
+  if(result && (result != CURLE_AGAIN))
+    CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %d, %zu",
+                stream->id, len, result, *pnread);
+  return result;
 }
 
 static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
@@ -2076,7 +2030,7 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
   struct cf_h2_ctx *ctx = cf->ctx;
   struct h2_stream_ctx *stream;
   CURLcode result = CURLE_OK;
-  ssize_t nread;
+  size_t nread;
 
   if(should_close_session(ctx)) {
     CURL_TRC_CF(data, cf, "progress ingress, session is closed");
@@ -2102,12 +2056,12 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
        * this may leave data in underlying buffers that will not
        * be consumed. */
       if(!cf->next || !cf->next->cft->has_data_pending(cf->next, data))
-        drain_stream(cf, data, stream);
+        Curl_multi_mark_dirty(data);
       break;
     }
 
-    nread = Curl_bufq_sipn(&ctx->inbufq, 0, nw_in_reader, cf, &result);
-    if(nread < 0) {
+    result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread);
+    if(result) {
       if(result != CURLE_AGAIN) {
         failf(data, "Failed receiving HTTP2 data: %d(%s)", result,
               curl_easy_strerror(result));
@@ -2121,9 +2075,8 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
       break;
     }
     else {
-      CURL_TRC_CF(data, cf, "[0] ingress: read %zd bytes", nread);
-      data_max_bytes = (data_max_bytes > (size_t)nread) ?
-        (data_max_bytes - (size_t)nread) : 0;
+      CURL_TRC_CF(data, cf, "[0] ingress: read %zu bytes", nread);
+      data_max_bytes = (data_max_bytes > nread) ? (data_max_bytes - nread) : 0;
     }
 
     if(h2_process_pending_input(cf, data, &result))
@@ -2140,15 +2093,15 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
   return CURLE_OK;
 }
 
-static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
-                          char *buf, size_t len, CURLcode *err)
+static CURLcode cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
+                           char *buf, size_t len, size_t *pnread)
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
-  ssize_t nread = -1;
-  CURLcode result;
+  CURLcode result, r2;
   struct cf_call_data save;
 
+  *pnread = 0;
   if(!stream) {
     /* Abnormal call sequence: either this transfer has never opened a stream
      * (unlikely) or the transfer has been done, cleaned up its resources, but
@@ -2156,51 +2109,49 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
      * is for such a case. */
     failf(data, "http/2 recv on a transfer never opened "
           "or already cleared, mid=%u", data->mid);
-    *err = CURLE_HTTP2;
-    return -1;
+    return CURLE_HTTP2;
   }
 
   CF_DATA_SAVE(save, cf, data);
 
-  nread = stream_recv(cf, data, stream, buf, len, err);
-  if(nread < 0 && *err != CURLE_AGAIN)
+  result = stream_recv(cf, data, stream, buf, len, pnread);
+  if(result && (result != CURLE_AGAIN))
     goto out;
 
-  if(nread < 0) {
-    *err = h2_progress_ingress(cf, data, len);
-    if(*err)
+  if(result) {
+    result = h2_progress_ingress(cf, data, len);
+    if(result)
       goto out;
 
-    nread = stream_recv(cf, data, stream, buf, len, err);
+    result = stream_recv(cf, data, stream, buf, len, pnread);
   }
 
-  if(nread > 0) {
+  if(*pnread > 0) {
     /* Now that we transferred this to the upper layer, we report
      * the actual amount of DATA consumed to the H2 session, so
      * that it adjusts stream flow control */
-    nghttp2_session_consume(ctx->h2, stream->id, (size_t)nread);
+    nghttp2_session_consume(ctx->h2, stream->id, *pnread);
     if(stream->closed) {
       CURL_TRC_CF(data, cf, "[%d] DRAIN closed stream", stream->id);
-      drain_stream(cf, data, stream);
+      Curl_multi_mark_dirty(data);
     }
   }
 
 out:
-  result = h2_progress_egress(cf, data);
-  if(result == CURLE_AGAIN) {
+  r2 = h2_progress_egress(cf, data);
+  if(r2 == CURLE_AGAIN) {
     /* pending data to send, need to be called again. Ideally, we
      * monitor the socket for POLLOUT, but when not SENDING
      * any more, we force processing of the transfer. */
     if(!CURL_WANT_SEND(data))
-      drain_stream(cf, data, stream);
+      Curl_multi_mark_dirty(data);
   }
-  else if(result) {
-    *err = result;
-    nread = -1;
+  else if(r2) {
+    result = r2;
   }
-  CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d, "
+  CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %d, %zu, "
               "window=%d/%d, connection %d/%d",
-              stream->id, len, nread, *err,
+              stream->id, len, result, *pnread,
               nghttp2_session_get_stream_effective_recv_data_length(
                 ctx->h2, stream->id),
               nghttp2_session_get_stream_effective_local_window_size(
@@ -2209,7 +2160,7 @@ out:
               HTTP2_HUGE_WINDOW_SIZE);
 
   CF_DATA_RESTORE(cf, save);
-  return nread;
+  return result;
 }
 
 static ssize_t cf_h2_body_send(struct Curl_cfilter *cf,
@@ -2219,7 +2170,7 @@ static ssize_t cf_h2_body_send(struct Curl_cfilter *cf,
                                CURLcode *err)
 {
   struct cf_h2_ctx *ctx = cf->ctx;
-  ssize_t nwritten;
+  size_t nwritten;
 
   if(stream->closed) {
     if(stream->resp_hds_complete) {
@@ -2241,11 +2192,11 @@ static ssize_t cf_h2_body_send(struct Curl_cfilter *cf,
     return -1;
   }
 
-  nwritten = Curl_bufq_write(&stream->sendbuf, buf, blen, err);
-  if(nwritten < 0)
+  *err = Curl_bufq_write(&stream->sendbuf, buf, blen, &nwritten);
+  if(*err)
     return -1;
 
-  if(eos && (blen == (size_t)nwritten))
+  if(eos && (blen == nwritten))
     stream->body_eos = TRUE;
 
   if(eos || !Curl_bufq_is_empty(&stream->sendbuf)) {
@@ -2256,13 +2207,13 @@ static ssize_t cf_h2_body_send(struct Curl_cfilter *cf,
       return -1;
     }
   }
-  return nwritten;
+  return (ssize_t)nwritten;
 }
 
-static ssize_t h2_submit(struct h2_stream_ctx **pstream,
-                         struct Curl_cfilter *cf, struct Curl_easy *data,
-                         const void *buf, size_t len,
-                         bool eos, CURLcode *err)
+static CURLcode h2_submit(struct h2_stream_ctx **pstream,
+                          struct Curl_cfilter *cf, struct Curl_easy *data,
+                          const void *buf, size_t len,
+                          bool eos, size_t *pnwritten)
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   struct h2_stream_ctx *stream = NULL;
@@ -2274,36 +2225,34 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream,
   int32_t stream_id;
   nghttp2_priority_spec pri_spec;
   ssize_t nwritten;
+  CURLcode result = CURLE_OK;
 
+  *pnwritten = 0;
   Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
 
-  *err = http2_data_setup(cf, data, &stream);
-  if(*err) {
-    nwritten = -1;
+  result = http2_data_setup(cf, data, &stream);
+  if(result)
     goto out;
-  }
 
-  nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
+  nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, &result);
   if(nwritten < 0)
     goto out;
+  *pnwritten = (size_t)nwritten;
   if(!stream->h1.done) {
     /* need more data */
     goto out;
   }
   DEBUGASSERT(stream->h1.req);
 
-  *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
-  if(*err) {
-    nwritten = -1;
+  result = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
+  if(result)
     goto out;
-  }
   /* no longer needed */
   Curl_h1_req_parse_free(&stream->h1);
 
   nva = Curl_dynhds_to_nva(&h2_headers, &nheader);
   if(!nva) {
-    *err = CURLE_OUT_OF_MEMORY;
-    nwritten = -1;
+    result = CURLE_OUT_OF_MEMORY;
     goto out;
   }
 
@@ -2329,8 +2278,7 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream,
   if(stream_id < 0) {
     CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%u",
                 nghttp2_strerror(stream_id), stream_id);
-    *err = CURLE_SEND_ERROR;
-    nwritten = -1;
+    result = CURLE_SEND_ERROR;
     goto out;
   }
 
@@ -2357,48 +2305,46 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream,
 
   stream->id = stream_id;
 
-  body = (const char *)buf + nwritten;
-  bodylen = len - nwritten;
+  body = (const char *)buf + *pnwritten;
+  bodylen = len - *pnwritten;
 
   if(bodylen || eos) {
-    ssize_t n = cf_h2_body_send(cf, data, stream, body, bodylen, eos, err);
+    ssize_t n = cf_h2_body_send(cf, data, stream, body, bodylen, eos, &result);
     if(n >= 0)
-      nwritten += n;
-    else if(*err == CURLE_AGAIN)
-      *err = CURLE_OK;
-    else if(*err != CURLE_AGAIN) {
-      *err = CURLE_SEND_ERROR;
-      nwritten = -1;
-      goto out;
+      *pnwritten += n;
+    else if(result == CURLE_AGAIN)
+      result = CURLE_OK;
+    else {
+      result = CURLE_SEND_ERROR;
     }
   }
 
 out:
-  CURL_TRC_CF(data, cf, "[%d] submit -> %zd, %d",
-              stream ? stream->id : -1, nwritten, *err);
+  CURL_TRC_CF(data, cf, "[%d] submit -> %d, %zu",
+              stream ? stream->id : -1, result, *pnwritten);
   Curl_safefree(nva);
   *pstream = stream;
   Curl_dynhds_free(&h2_headers);
-  return nwritten;
+  return result;
 }
 
-static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
-                          const void *buf, size_t len, bool eos,
-                          CURLcode *err)
+static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
+                           const void *buf, size_t len, bool eos,
+                           size_t *pnwritten)
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
   struct cf_call_data save;
   ssize_t nwritten;
-  CURLcode result;
+  CURLcode result = CURLE_OK, r2;
 
   CF_DATA_SAVE(save, cf, data);
+  *pnwritten = 0;
 
   if(!stream || stream->id == -1) {
-    nwritten = h2_submit(&stream, cf, data, buf, len, eos, err);
-    if(nwritten < 0) {
+    result = h2_submit(&stream, cf, data, buf, len, eos, pnwritten);
+    if(result)
       goto out;
-    }
     DEBUGASSERT(stream);
   }
   else if(stream->body_eos) {
@@ -2407,35 +2353,35 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
      * to trigger flushing again.
      * If this works, we report to have written `len` bytes. */
     DEBUGASSERT(eos);
-    nwritten = cf_h2_body_send(cf, data, stream, buf, 0, eos, err);
+    nwritten = cf_h2_body_send(cf, data, stream, buf, 0, eos, &result);
     CURL_TRC_CF(data, cf, "[%d] cf_body_send last CHUNK -> %zd, %d, eos=%d",
-                stream->id, nwritten, *err, eos);
+                stream->id, nwritten, result, eos);
     if(nwritten < 0) {
       goto out;
     }
-    nwritten = len;
+    *pnwritten = len;
   }
   else {
-    nwritten = cf_h2_body_send(cf, data, stream, buf, len, eos, err);
+    nwritten = cf_h2_body_send(cf, data, stream, buf, len, eos, &result);
     CURL_TRC_CF(data, cf, "[%d] cf_body_send(len=%zu) -> %zd, %d, eos=%d",
-                stream->id, len, nwritten, *err, eos);
+                stream->id, len, nwritten, result, eos);
+    if(nwritten >= 0)
+      *pnwritten = (size_t)nwritten;
   }
 
   /* Call the nghttp2 send loop and flush to write ALL buffered data,
    * headers and/or request body completely out to the network */
-  result = h2_progress_egress(cf, data);
+  r2 = h2_progress_egress(cf, data);
 
   /* if the stream has been closed in egress handling (nghttp2 does that
    * when it does not like the headers, for example */
   if(stream && stream->closed) {
     infof(data, "stream %u closed", stream->id);
-    *err = CURLE_SEND_ERROR;
-    nwritten = -1;
+    result = CURLE_SEND_ERROR;
     goto out;
   }
-  else if(result && (result != CURLE_AGAIN)) {
-    *err = result;
-    nwritten = -1;
+  else if(r2 && (r2 != CURLE_AGAIN)) {
+    result = r2;
     goto out;
   }
 
@@ -2443,21 +2389,20 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
     /* nghttp2 thinks this session is done. If the stream has not been
      * closed, this is an error state for out transfer */
     if(stream && stream->closed) {
-      nwritten = http2_handle_stream_close(cf, data, stream, err);
+      result = http2_handle_stream_close(cf, data, stream, pnwritten);
     }
     else {
       CURL_TRC_CF(data, cf, "send: nothing to do in this session");
-      *err = CURLE_HTTP2;
-      nwritten = -1;
+      result = CURLE_HTTP2;
     }
   }
 
 out:
   if(stream) {
-    CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, "
+    CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %d, %zu, "
                 "eos=%d, h2 windows %d-%d (stream-conn), "
                 "buffers %zu-%zu (stream-conn)",
-                stream->id, len, nwritten, *err,
+                stream->id, len, result, *pnwritten,
                 stream->body_eos,
                 nghttp2_session_get_stream_remote_window_size(
                   ctx->h2, stream->id),
@@ -2466,14 +2411,14 @@ out:
                 Curl_bufq_len(&ctx->outbufq));
   }
   else {
-    CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %zd, %d, "
+    CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %d, %zu, "
                 "connection-window=%d, nw_send_buffer(%zu)",
-                len, nwritten, *err,
+                len, result, *pnwritten,
                 nghttp2_session_get_remote_window_size(ctx->h2),
                 Curl_bufq_len(&ctx->outbufq));
   }
   CF_DATA_RESTORE(cf, save);
-  return nwritten;
+  return result;
 }
 
 static CURLcode cf_h2_flush(struct Curl_cfilter *cf,
@@ -2716,8 +2661,7 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf,
        * not. We may have already buffered and exhausted the new window
        * by operating on things in flight during the handling of other
        * transfers. */
-      drain_stream(cf, data, stream);
-      Curl_expire(data, 0, EXPIRE_RUN_NOW);
+      Curl_multi_mark_dirty(data);
     }
     CURL_TRC_CF(data, cf, "[%d] stream now %spaused", stream->id,
                 pause ? "" : "un");
@@ -2769,15 +2713,16 @@ static bool cf_h2_is_alive(struct Curl_cfilter *cf,
                            bool *input_pending)
 {
   struct cf_h2_ctx *ctx = cf->ctx;
-  CURLcode result;
+  bool alive;
   struct cf_call_data save;
 
+  *input_pending = FALSE;
   CF_DATA_SAVE(save, cf, data);
-  result = (ctx && ctx->h2 && http2_connisalive(cf, data, input_pending));
+  alive = (ctx && ctx->h2 && http2_connisalive(cf, data, input_pending));
   CURL_TRC_CF(data, cf, "conn alive -> %d, input_pending=%d",
-              result, *input_pending);
+              alive, *input_pending);
   CF_DATA_RESTORE(cf, save);
-  return result;
+  return alive;
 }
 
 static CURLcode cf_h2_keep_alive(struct Curl_cfilter *cf,
@@ -2805,7 +2750,7 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf,
     DEBUGASSERT(pres1);
 
     CF_DATA_SAVE(save, cf, data);
-    if(nghttp2_session_check_request_allowed(ctx->h2) == 0) {
+    if(!ctx->h2 || !nghttp2_session_check_request_allowed(ctx->h2)) {
       /* the limit is what we have in use right now */
       effective_max = CONN_ATTACHED(cf->conn);
     }
@@ -2848,7 +2793,6 @@ struct Curl_cftype Curl_cft_nghttp2 = {
   cf_h2_connect,
   cf_h2_close,
   cf_h2_shutdown,
-  Curl_cf_def_get_host,
   cf_h2_adjust_pollset,
   cf_h2_data_pending,
   cf_h2_send,
@@ -3001,17 +2945,17 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
     /* Remaining data from the protocol switch reply is already using
      * the switched protocol, ie. HTTP/2. We add that to the network
      * inbufq. */
-    ssize_t copied;
+    size_t copied;
 
-    copied = Curl_bufq_write(&ctx->inbufq,
-                             (const unsigned char *)mem, nread, &result);
-    if(copied < 0) {
+    result = Curl_bufq_write(&ctx->inbufq,
+                             (const unsigned char *)mem, nread, &copied);
+    if(result) {
       failf(data, "error on copying HTTP Upgrade response: %d", result);
       return CURLE_RECV_ERROR;
     }
-    if((size_t)copied < nread) {
+    if(copied < nread) {
       failf(data, "connection buffer size could not take all data "
-            "from HTTP Upgrade response header: copied=%zd, datalen=%zu",
+            "from HTTP Upgrade response header: copied=%zu, datalen=%zu",
             copied, nread);
       return CURLE_HTTP2;
     }

+ 2 - 2
lib/http_aws_sigv4.c

@@ -537,8 +537,8 @@ static int compare_func(const void *a, const void *b)
 }
 
 UNITTEST CURLcode canon_path(const char *q, size_t len,
-                              struct dynbuf *new_path,
-                              bool do_uri_encode)
+                             struct dynbuf *new_path,
+                             bool do_uri_encode)
 {
   CURLcode result = CURLE_OK;
 

+ 26 - 18
lib/http_negotiate.c

@@ -39,6 +39,20 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
+
+static void http_auth_nego_reset(struct connectdata *conn,
+                                 struct negotiatedata *neg_ctx,
+                                 bool proxy)
+{
+  if(proxy)
+    conn->proxy_negotiate_state = GSS_AUTHNONE;
+  else
+    conn->http_negotiate_state = GSS_AUTHNONE;
+  if(neg_ctx)
+    Curl_auth_cleanup_spnego(neg_ctx);
+}
+
+
 CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
                               bool proxy, const char *header)
 {
@@ -62,7 +76,6 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
     service = data->set.str[STRING_PROXY_SERVICE_NAME] ?
               data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP";
     host = conn->http_proxy.host.name;
-    neg_ctx = &conn->proxyneg;
     state = conn->proxy_negotiate_state;
 #else
     return CURLE_NOT_BUILT_IN;
@@ -74,10 +87,13 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
     service = data->set.str[STRING_SERVICE_NAME] ?
               data->set.str[STRING_SERVICE_NAME] : "HTTP";
     host = conn->host.name;
-    neg_ctx = &conn->negotiate;
     state = conn->http_negotiate_state;
   }
 
+  neg_ctx = Curl_auth_nego_get(conn, proxy);
+  if(!neg_ctx)
+    return CURLE_OUT_OF_MEMORY;
+
   /* Not set means empty */
   if(!userp)
     userp = "";
@@ -94,12 +110,12 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
   if(!len) {
     if(state == GSS_AUTHSUCC) {
       infof(data, "Negotiate auth restarted");
-      Curl_http_auth_cleanup_negotiate(conn);
+      http_auth_nego_reset(conn, neg_ctx, proxy);
     }
     else if(state != GSS_AUTHNONE) {
       /* The server rejected our authentication and has not supplied any more
       negotiation mechanisms */
-      Curl_http_auth_cleanup_negotiate(conn);
+      http_auth_nego_reset(conn, neg_ctx, proxy);
       return CURLE_LOGIN_DENIED;
     }
   }
@@ -116,7 +132,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
     result = Curl_ssl_get_channel_binding(
       data, FIRSTSOCKET, &neg_ctx->channel_binding_data);
     if(result) {
-      Curl_http_auth_cleanup_negotiate(conn);
+      http_auth_nego_reset(conn, neg_ctx, proxy);
       return result;
     }
   }
@@ -134,7 +150,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
 #endif
 
   if(result)
-    Curl_http_auth_cleanup_negotiate(conn);
+    http_auth_nego_reset(conn, neg_ctx, proxy);
 
   return result;
 }
@@ -152,7 +168,6 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data,
 
   if(proxy) {
 #ifndef CURL_DISABLE_PROXY
-    neg_ctx = &conn->proxyneg;
     authp = &data->state.authproxy;
     state = &conn->proxy_negotiate_state;
 #else
@@ -160,10 +175,12 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data,
 #endif
   }
   else {
-    neg_ctx = &conn->negotiate;
     authp = &data->state.authhost;
     state = &conn->http_negotiate_state;
   }
+  neg_ctx = Curl_auth_nego_get(conn, proxy);
+  if(!neg_ctx)
+    return CURLE_OUT_OF_MEMORY;
 
   authp->done = FALSE;
 
@@ -184,7 +201,7 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data,
     if(neg_ctx->noauthpersist && *state == GSS_AUTHSUCC) {
       infof(data, "Curl_output_negotiate, "
             "no persistent authentication: cleanup existing context");
-      Curl_http_auth_cleanup_negotiate(conn);
+      http_auth_nego_reset(conn, neg_ctx, proxy);
     }
     if(!neg_ctx->context) {
       result = Curl_input_negotiate(data, conn, proxy, "Negotiate");
@@ -249,13 +266,4 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data,
   return CURLE_OK;
 }
 
-void Curl_http_auth_cleanup_negotiate(struct connectdata *conn)
-{
-  conn->http_negotiate_state = GSS_AUTHNONE;
-  conn->proxy_negotiate_state = GSS_AUTHNONE;
-
-  Curl_auth_cleanup_spnego(&conn->negotiate);
-  Curl_auth_cleanup_spnego(&conn->proxyneg);
-}
-
 #endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */

+ 0 - 4
lib/http_negotiate.h

@@ -34,10 +34,6 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
 CURLcode Curl_output_negotiate(struct Curl_easy *data,
                                struct connectdata *conn, bool proxy);
 
-void Curl_http_auth_cleanup_negotiate(struct connectdata *conn);
-
-#else /* !CURL_DISABLE_HTTP && USE_SPNEGO */
-#define Curl_http_auth_cleanup_negotiate(x)
 #endif
 
 #endif /* HEADER_CURL_HTTP_NEGOTIATE_H */

+ 13 - 18
lib/http_ntlm.c

@@ -60,17 +60,18 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data,
                                                 header */
 {
   /* point to the correct struct with this */
-  struct ntlmdata *ntlm;
   curlntlm *state;
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
 
-  ntlm = proxy ? &conn->proxyntlm : &conn->ntlm;
   state = proxy ? &conn->proxy_ntlm_state : &conn->http_ntlm_state;
 
   if(checkprefix("NTLM", header)) {
-    header += strlen("NTLM");
+    struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, proxy);
+    if(!ntlm)
+      return CURLE_FAILED_INIT;
 
+    header += strlen("NTLM");
     curlx_str_passblanks(&header);
     if(*header) {
       unsigned char *hdr;
@@ -93,11 +94,11 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data,
     else {
       if(*state == NTLMSTATE_LAST) {
         infof(data, "NTLM auth restarted");
-        Curl_http_auth_cleanup_ntlm(conn);
+        Curl_auth_ntlm_remove(conn, proxy);
       }
       else if(*state == NTLMSTATE_TYPE3) {
         infof(data, "NTLM handshake rejected");
-        Curl_http_auth_cleanup_ntlm(conn);
+        Curl_auth_ntlm_remove(conn, proxy);
         *state = NTLMSTATE_NONE;
         return CURLE_REMOTE_ACCESS_DENIED;
       }
@@ -150,7 +151,6 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
     service = data->set.str[STRING_PROXY_SERVICE_NAME] ?
       data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP";
     hostname = conn->http_proxy.host.name;
-    ntlm = &conn->proxyntlm;
     state = &conn->proxy_ntlm_state;
     authp = &data->state.authproxy;
 #else
@@ -164,10 +164,12 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
     service = data->set.str[STRING_SERVICE_NAME] ?
       data->set.str[STRING_SERVICE_NAME] : "HTTP";
     hostname = conn->host.name;
-    ntlm = &conn->ntlm;
     state = &conn->http_ntlm_state;
     authp = &data->state.authhost;
   }
+  ntlm = Curl_auth_ntlm_get(conn, proxy);
+  if(!ntlm)
+    return CURLE_OUT_OF_MEMORY;
   authp->done = FALSE;
 
   /* not set means empty */
@@ -178,10 +180,10 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
     passwdp = "";
 
 #ifdef USE_WINDOWS_SSPI
-  if(!Curl_hSecDll) {
+  if(!Curl_pSecFn) {
     /* not thread safe and leaks - use curl_global_init() to avoid */
     CURLcode err = Curl_sspi_global_init();
-    if(!Curl_hSecDll)
+    if(!Curl_pSecFn)
       return err;
   }
 #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
@@ -200,9 +202,8 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
   case NTLMSTATE_TYPE1:
   default: /* for the weird cases we (re)start here */
     /* Create a type-1 message */
-    result = Curl_auth_create_ntlm_type1_message(data, userp, passwdp,
-                                                 service, hostname,
-                                                 ntlm, &ntlmmsg);
+    result = Curl_auth_create_ntlm_type1_message(data, userp, passwdp, service,
+                                                 hostname, ntlm, &ntlmmsg);
     if(!result) {
       DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0);
       result = curlx_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg),
@@ -258,10 +259,4 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
   return result;
 }
 
-void Curl_http_auth_cleanup_ntlm(struct connectdata *conn)
-{
-  Curl_auth_cleanup_ntlm(&conn->ntlm);
-  Curl_auth_cleanup_ntlm(&conn->proxyntlm);
-}
-
 #endif /* !CURL_DISABLE_HTTP && USE_NTLM */

+ 0 - 4
lib/http_ntlm.h

@@ -35,10 +35,6 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data, bool proxy,
 /* this is for creating NTLM header output */
 CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy);
 
-void Curl_http_auth_cleanup_ntlm(struct connectdata *conn);
-
-#else /* !CURL_DISABLE_HTTP && USE_NTLM */
-#define Curl_http_auth_cleanup_ntlm(x)
 #endif
 
 #endif /* HEADER_CURL_HTTP_NTLM_H */

+ 15 - 17
lib/http_proxy.c

@@ -38,7 +38,6 @@
 #include "cf-h1-proxy.h"
 #include "cf-h2-proxy.h"
 #include "connect.h"
-#include "strcase.h"
 #include "vtls/vtls.h"
 #include "transfer.h"
 #include "multiif.h"
@@ -53,7 +52,7 @@
 static bool hd_name_eq(const char *n1, size_t n1len,
                        const char *n2, size_t n2len)
 {
-  return (n1len == n2len) ? strncasecompare(n1, n2, n1len) : FALSE;
+  return (n1len == n2len) ? curl_strnequal(n1, n2, n1len) : FALSE;
 }
 
 static CURLcode dynhds_add_custom(struct Curl_easy *data,
@@ -383,21 +382,21 @@ out:
   return result;
 }
 
-void Curl_cf_http_proxy_get_host(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data,
-                                 const char **phost,
-                                 const char **pdisplay_host,
-                                 int *pport)
+CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf,
+                                  struct Curl_easy *data,
+                                  int query, int *pres1, void *pres2)
 {
-  (void)data;
-  if(!cf->connected) {
-    *phost = cf->conn->http_proxy.host.name;
-    *pdisplay_host = cf->conn->http_proxy.host.dispname;
-    *pport = (int)cf->conn->http_proxy.port;
-  }
-  else {
-    cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport);
+  switch(query) {
+  case CF_QUERY_HOST_PORT:
+    *pres1 = (int)cf->conn->http_proxy.port;
+    *((const char **)pres2) = cf->conn->http_proxy.host.name;
+    return CURLE_OK;
+  default:
+    break;
   }
+  return cf->next ?
+    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+    CURLE_UNKNOWN_OPTION;
 }
 
 static void http_proxy_cf_destroy(struct Curl_cfilter *cf,
@@ -443,7 +442,6 @@ struct Curl_cftype Curl_cft_http_proxy = {
   http_proxy_cf_connect,
   http_proxy_cf_close,
   Curl_cf_def_shutdown,
-  Curl_cf_http_proxy_get_host,
   Curl_cf_def_adjust_pollset,
   Curl_cf_def_data_pending,
   Curl_cf_def_send,
@@ -451,7 +449,7 @@ struct Curl_cftype Curl_cft_http_proxy = {
   Curl_cf_def_cntrl,
   Curl_cf_def_conn_is_alive,
   Curl_cf_def_conn_keep_alive,
-  Curl_cf_def_query,
+  Curl_cf_http_proxy_query,
 };
 
 CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,

+ 4 - 6
lib/http_proxy.h

@@ -48,18 +48,16 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq,
 /* Default proxy timeout in milliseconds */
 #define PROXY_TIMEOUT (3600*1000)
 
-void Curl_cf_http_proxy_get_host(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data,
-                                 const char **phost,
-                                 const char **pdisplay_host,
-                                 int *pport);
+CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf,
+                                  struct Curl_easy *data,
+                                  int query, int *pres1, void *pres2);
 
 CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
                                          struct Curl_easy *data);
 
 extern struct Curl_cftype Curl_cft_http_proxy;
 
-#endif /* !CURL_DISABLE_PROXY  && !CURL_DISABLE_HTTP */
+#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
 
 #define IS_HTTPS_PROXY(t) (((t) == CURLPROXY_HTTPS) ||  \
                            ((t) == CURLPROXY_HTTPS2))

+ 5 - 6
lib/if2ip.c

@@ -52,8 +52,7 @@
 #  include <inet.h>
 #endif
 
-#include "inet_ntop.h"
-#include "strcase.h"
+#include "curlx/inet_ntop.h"
 #include "if2ip.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -117,7 +116,7 @@ if2ip_result_t Curl_if2ip(int af,
     for(iface = head; iface != NULL; iface = iface->ifa_next) {
       if(iface->ifa_addr) {
         if(iface->ifa_addr->sa_family == af) {
-          if(strcasecompare(iface->ifa_name, interf)) {
+          if(curl_strequal(iface->ifa_name, interf)) {
             void *addr;
             const char *ip;
             char scope[12] = "";
@@ -162,13 +161,13 @@ if2ip_result_t Curl_if2ip(int af,
               addr =
                 &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr;
             res = IF2IP_FOUND;
-            ip = Curl_inet_ntop(af, addr, ipstr, sizeof(ipstr));
+            ip = curlx_inet_ntop(af, addr, ipstr, sizeof(ipstr));
             msnprintf(buf, buf_size, "%s%s", ip, scope);
             break;
           }
         }
         else if((res == IF2IP_NOT_FOUND) &&
-                strcasecompare(iface->ifa_name, interf)) {
+                curl_strequal(iface->ifa_name, interf)) {
           res = IF2IP_AF_NOT_SUPPORTED;
         }
       }
@@ -235,7 +234,7 @@ if2ip_result_t Curl_if2ip(int af,
 
   s = (struct sockaddr_in *)(void *)&req.ifr_addr;
   memcpy(&in, &s->sin_addr, sizeof(in));
-  r = Curl_inet_ntop(s->sin_family, &in, buf, buf_size);
+  r = curlx_inet_ntop(s->sin_family, &in, buf, buf_size);
 
   sclose(dummy);
   if(!r)

+ 24 - 31
lib/imap.c

@@ -302,7 +302,7 @@ static bool imap_matchresp(const char *line, size_t len, const char *cmd)
 
   /* Does the command name match and is it followed by a space character or at
      the end of line? */
-  if(line + cmd_len <= end && strncasecompare(line, cmd, cmd_len) &&
+  if(line + cmd_len <= end && curl_strnequal(line, cmd, cmd_len) &&
      (line[cmd_len] == ' ' || line + cmd_len + 2 == end))
     return TRUE;
 
@@ -358,16 +358,16 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn,
       case IMAP_LIST:
         if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
            (imap->custom && !imap_matchresp(line, len, imap->custom) &&
-            (!strcasecompare(imap->custom, "STORE") ||
+            (!curl_strequal(imap->custom, "STORE") ||
              !imap_matchresp(line, len, "FETCH")) &&
-            !strcasecompare(imap->custom, "SELECT") &&
-            !strcasecompare(imap->custom, "EXAMINE") &&
-            !strcasecompare(imap->custom, "SEARCH") &&
-            !strcasecompare(imap->custom, "EXPUNGE") &&
-            !strcasecompare(imap->custom, "LSUB") &&
-            !strcasecompare(imap->custom, "UID") &&
-            !strcasecompare(imap->custom, "GETQUOTAROOT") &&
-            !strcasecompare(imap->custom, "NOOP")))
+            !curl_strequal(imap->custom, "SELECT") &&
+            !curl_strequal(imap->custom, "EXAMINE") &&
+            !curl_strequal(imap->custom, "SEARCH") &&
+            !curl_strequal(imap->custom, "EXPUNGE") &&
+            !curl_strequal(imap->custom, "LSUB") &&
+            !curl_strequal(imap->custom, "UID") &&
+            !curl_strequal(imap->custom, "GETQUOTAROOT") &&
+            !curl_strequal(imap->custom, "NOOP")))
           return FALSE;
         break;
 
@@ -1239,7 +1239,7 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data,
   else if(imapcode == IMAP_RESP_OK) {
     /* Check if the UIDVALIDITY has been specified and matches */
     if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
-       !strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
+       !curl_strequal(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
       failf(data, "Mailbox UIDVALIDITY has changed");
       result = CURLE_REMOTE_FILE_NOT_FOUND;
     }
@@ -1347,9 +1347,6 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
     else {
       /* IMAP download */
       data->req.maxdownload = size;
-      /* force a recv/send check of this connection, as the data might've been
-       read off the socket already */
-      data->state.select_bits = CURL_CSELECT_IN;
       Curl_xfer_setup1(data, CURL_XFER_RECV, size, FALSE);
     }
   }
@@ -1693,9 +1690,9 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected,
   /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
      has already been selected on this connection */
   if(imap->mailbox && imapc->mailbox &&
-     strcasecompare(imap->mailbox, imapc->mailbox) &&
+     curl_strequal(imap->mailbox, imapc->mailbox) &&
      (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
-      strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)))
+      curl_strequal(imap->uidvalidity, imapc->mailbox_uidvalidity)))
     selected = TRUE;
 
   /* Start the first command in the DO phase */
@@ -1780,18 +1777,14 @@ static CURLcode imap_disconnect(struct Curl_easy *data,
   (void)data;
   if(imapc) {
     /* We cannot send quit unconditionally. If this connection is stale or
-       bad in any way, sending quit and waiting around here will make the
+       bad in any way (pingpong has pending data to send),
+       sending quit and waiting around here will make the
        disconnect wait in vain and cause more problems than we need to. */
-
-    /* The IMAP session may or may not have been allocated/setup at this
-       point! */
-    if(!dead_connection && conn->bits.protoconnstart) {
+    if(!dead_connection && conn->bits.protoconnstart &&
+       !Curl_pp_needs_flush(data, &imapc->pp)) {
       if(!imap_perform_logout(data, imapc))
         (void)imap_block_statemach(data, imapc, TRUE); /* ignore errors */
     }
-
-    /* Cleanup the SASL module */
-    Curl_sasl_cleanup(conn, imapc->sasl.authused);
   }
   return CURLE_OK;
 }
@@ -2079,12 +2072,12 @@ static CURLcode imap_parse_url_options(struct connectdata *conn,
     while(*ptr && *ptr != ';')
       ptr++;
 
-    if(strncasecompare(key, "AUTH=+LOGIN", 11)) {
+    if(curl_strnequal(key, "AUTH=+LOGIN", 11)) {
       /* User prefers plaintext LOGIN over any SASL, including SASL LOGIN */
       prefer_login = TRUE;
       imapc->sasl.prefmech = SASL_AUTH_NONE;
     }
-    else if(strncasecompare(key, "AUTH=", 5)) {
+    else if(curl_strnequal(key, "AUTH=", 5)) {
       prefer_login = FALSE;
       result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
                                                value, ptr - value);
@@ -2189,35 +2182,35 @@ static CURLcode imap_parse_url_path(struct Curl_easy *data,
        PARTIAL) stripping of the trailing slash character if it is present.
 
        Note: Unknown parameters trigger a URL_MALFORMAT error. */
-    if(strcasecompare(name, "UIDVALIDITY") && !imap->uidvalidity) {
+    if(curl_strequal(name, "UIDVALIDITY") && !imap->uidvalidity) {
       if(valuelen > 0 && value[valuelen - 1] == '/')
         value[valuelen - 1] = '\0';
 
       imap->uidvalidity = value;
       value = NULL;
     }
-    else if(strcasecompare(name, "UID") && !imap->uid) {
+    else if(curl_strequal(name, "UID") && !imap->uid) {
       if(valuelen > 0 && value[valuelen - 1] == '/')
         value[valuelen - 1] = '\0';
 
       imap->uid = value;
       value = NULL;
     }
-    else if(strcasecompare(name, "MAILINDEX") && !imap->mindex) {
+    else if(curl_strequal(name, "MAILINDEX") && !imap->mindex) {
       if(valuelen > 0 && value[valuelen - 1] == '/')
         value[valuelen - 1] = '\0';
 
       imap->mindex = value;
       value = NULL;
     }
-    else if(strcasecompare(name, "SECTION") && !imap->section) {
+    else if(curl_strequal(name, "SECTION") && !imap->section) {
       if(valuelen > 0 && value[valuelen - 1] == '/')
         value[valuelen - 1] = '\0';
 
       imap->section = value;
       value = NULL;
     }
-    else if(strcasecompare(name, "PARTIAL") && !imap->partial) {
+    else if(curl_strequal(name, "PARTIAL") && !imap->partial) {
       if(valuelen > 0 && value[valuelen - 1] == '/')
         value[valuelen - 1] = '\0';
 

+ 54 - 54
lib/krb5.c

@@ -56,7 +56,6 @@
 #include "transfer.h"
 #include "curl_krb5.h"
 #include "curlx/warnless.h"
-#include "strcase.h"
 #include "strdup.h"
 
 /* The last 3 #include files should be in this order */
@@ -215,12 +214,14 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
   gss_ctx_id_t *context = app_data;
   struct gss_channel_bindings_struct chan;
   size_t base64_sz = 0;
-  struct sockaddr_in *remote_addr =
-    (struct sockaddr_in *)CURL_UNCONST(&conn->remote_addr->curl_sa_addr);
+  const struct Curl_sockaddr_ex *remote_addr =
+    Curl_conn_get_remote_addr(data, FIRSTSOCKET);
+  struct sockaddr_in *remote_in_addr = remote_addr ?
+    (struct sockaddr_in *)CURL_UNCONST(&remote_addr->curl_sa_addr) : NULL;
   char *stringp;
   struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
 
-  if(!ftpc)
+  if(!ftpc || !remote_in_addr)
     return -2;
 
   if(getsockname(conn->sock[FIRSTSOCKET],
@@ -232,7 +233,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
   chan.initiator_address.value = &conn->local_addr.sin_addr.s_addr;
   chan.acceptor_addrtype = GSS_C_AF_INET;
   chan.acceptor_address.length = l - 4;
-  chan.acceptor_address.value = &remote_addr->sin_addr.s_addr;
+  chan.acceptor_address.value = &remote_in_addr->sin_addr.s_addr;
   chan.application_data.length = 0;
   chan.application_data.value = NULL;
 
@@ -384,7 +385,8 @@ static void krb5_end(void *app_data)
   OM_uint32 min;
   gss_ctx_id_t *context = app_data;
   if(*context != GSS_C_NO_CONTEXT) {
-    OM_uint32 maj = gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER);
+    OM_uint32 maj = Curl_gss_delete_sec_context(&min, context,
+                                                GSS_C_NO_BUFFER);
     (void)maj;
     DEBUGASSERT(maj == GSS_S_COMPLETE);
   }
@@ -479,19 +481,18 @@ socket_read(struct Curl_easy *data, int sockindex, void *to, size_t len)
 {
   char *to_p = to;
   CURLcode result;
-  ssize_t nread = 0;
+  size_t nread = 0;
 
   while(len > 0) {
     result = Curl_conn_recv(data, sockindex, to_p, len, &nread);
-    if(nread > 0) {
-      len -= nread;
-      to_p += nread;
-    }
-    else {
-      if(result == CURLE_AGAIN)
-        continue;
+    if(result == CURLE_AGAIN)
+      continue;
+    if(result)
       return result;
-    }
+    if(nread > len)
+      return CURLE_RECV_ERROR;
+    len -= nread;
+    to_p += nread;
   }
   return CURLE_OK;
 }
@@ -523,8 +524,8 @@ socket_write(struct Curl_easy *data, int sockindex, const void *to,
   return CURLE_OK;
 }
 
-static CURLcode read_data(struct Curl_easy *data, int sockindex,
-                          struct krb5buffer *buf)
+static CURLcode krb5_read_data(struct Curl_easy *data, int sockindex,
+                               struct krb5buffer *buf)
 {
   struct connectdata *conn = data->conn;
   int len;
@@ -579,52 +580,49 @@ buffer_read(struct krb5buffer *buf, void *data, size_t len)
 }
 
 /* Matches Curl_recv signature */
-static ssize_t sec_recv(struct Curl_easy *data, int sockindex,
-                        char *buffer, size_t len, CURLcode *err)
+static CURLcode sec_recv(struct Curl_easy *data, int sockindex,
+                         char *buffer, size_t len, size_t *pnread)
 {
-  size_t bytes_read;
-  size_t total_read = 0;
   struct connectdata *conn = data->conn;
-
-  *err = CURLE_OK;
+  CURLcode result = CURLE_OK;
+  size_t bytes_read;
 
   /* Handle clear text response. */
-  if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR) {
-    ssize_t nread;
-    *err = Curl_conn_recv(data, sockindex, buffer, len, &nread);
-    return nread;
-  }
+  if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR)
+    return Curl_conn_recv(data, sockindex, buffer, len, pnread);
 
   if(conn->in_buffer.eof_flag) {
     conn->in_buffer.eof_flag = 0;
-    return 0;
+    *pnread = 0;
+    return CURLE_OK;
   }
 
   bytes_read = buffer_read(&conn->in_buffer, buffer, len);
-  len -= bytes_read;
-  total_read += bytes_read;
   buffer += bytes_read;
+  len -= bytes_read;
+  *pnread += bytes_read;
 
   while(len > 0) {
-    if(read_data(data, sockindex, &conn->in_buffer))
-      return -1;
+    result = krb5_read_data(data, sockindex, &conn->in_buffer);
+    if(result)
+      return result;
     if(curlx_dyn_len(&conn->in_buffer.buf) == 0) {
-      if(bytes_read > 0)
+      if(*pnread > 0)
         conn->in_buffer.eof_flag = 1;
-      return bytes_read;
+      return result;
     }
     bytes_read = buffer_read(&conn->in_buffer, buffer, len);
-    len -= bytes_read;
-    total_read += bytes_read;
     buffer += bytes_read;
+    len -= bytes_read;
+    *pnread += bytes_read;
   }
-  return total_read;
+  return result;
 }
 
 /* Send |length| bytes from |from| to the |sockindex| socket taking care of
    encoding and negotiating with the server. |from| can be NULL. */
 static void do_sec_send(struct Curl_easy *data, struct connectdata *conn,
-                        int sockindex, const char *from, int length)
+                        int sockindex, const char *from, size_t length)
 {
   int bytes, htonl_bytes; /* 32-bit integers for htonl */
   char *buffer = NULL;
@@ -642,8 +640,8 @@ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn,
     else
       prot_level = conn->command_prot;
   }
-  bytes = conn->mech->encode(conn->app_data, from, length, (int)prot_level,
-                             (void **)&buffer);
+  bytes = conn->mech->encode(conn->app_data, from, (int)length,
+                             (int)prot_level, (void **)&buffer);
   if(!buffer || bytes <= 0)
     return; /* error */
 
@@ -677,34 +675,36 @@ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn,
   free(buffer);
 }
 
-static ssize_t sec_write(struct Curl_easy *data, struct connectdata *conn,
-                         int sockindex, const char *buffer, size_t length)
+static CURLcode sec_write(struct Curl_easy *data, int sockindex,
+                          const char *buffer, size_t length,
+                          size_t *pnwritten)
 {
-  ssize_t tx = 0, len = conn->buffer_size;
+  struct connectdata *conn = data->conn;
+  size_t len = conn->buffer_size;
 
+  *pnwritten = 0;
   if(len <= 0)
     len = length;
   while(length) {
-    if(length < (size_t)len)
+    if(length < len)
       len = length;
 
-    do_sec_send(data, conn, sockindex, buffer, curlx_sztosi(len));
+    /* WTF: this ignores all errors writing to the socket */
+    do_sec_send(data, conn, sockindex, buffer, len);
     length -= len;
     buffer += len;
-    tx += len;
+    *pnwritten += len;
   }
-  return tx;
+  return CURLE_OK;
 }
 
 /* Matches Curl_send signature */
-static ssize_t sec_send(struct Curl_easy *data, int sockindex,
-                        const void *buffer, size_t len, bool eos,
-                        CURLcode *err)
+static CURLcode sec_send(struct Curl_easy *data, int sockindex,
+                         const void *buffer, size_t len, bool eos,
+                         size_t *pnwritten)
 {
-  struct connectdata *conn = data->conn;
   (void)eos; /* unused */
-  *err = CURLE_OK;
-  return sec_write(data, conn, sockindex, buffer, len);
+  return sec_write(data, sockindex, buffer, len, pnwritten);
 }
 
 int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn,

+ 7 - 8
lib/ldap.c

@@ -88,7 +88,6 @@
 #include "escape.h"
 #include "progress.h"
 #include "transfer.h"
-#include "strcase.h"
 #include "curlx/strparse.h"
 #include "curl_ldap.h"
 #include "curlx/multibyte.h"
@@ -393,7 +392,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
     if(conn->ssl_config.verifypeer) {
       /* OpenLDAP SDK supports BASE64 files. */
       if((data->set.ssl.cert_type) &&
-         (!strcasecompare(data->set.ssl.cert_type, "PEM"))) {
+         (!curl_strequal(data->set.ssl.cert_type, "PEM"))) {
         failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type");
         result = CURLE_SSL_CERTPROBLEM;
         goto quit;
@@ -748,15 +747,15 @@ static void _ldap_trace(const char *fmt, ...)
  */
 static int str2scope(const char *p)
 {
-  if(strcasecompare(p, "one"))
+  if(curl_strequal(p, "one"))
     return LDAP_SCOPE_ONELEVEL;
-  if(strcasecompare(p, "onetree"))
+  if(curl_strequal(p, "onetree"))
     return LDAP_SCOPE_ONELEVEL;
-  if(strcasecompare(p, "base"))
+  if(curl_strequal(p, "base"))
     return LDAP_SCOPE_BASE;
-  if(strcasecompare(p, "sub"))
+  if(curl_strequal(p, "sub"))
     return LDAP_SCOPE_SUBTREE;
-  if(strcasecompare(p, "subtree"))
+  if(curl_strequal(p, "subtree"))
     return LDAP_SCOPE_SUBTREE;
   return -1;
 }
@@ -801,7 +800,7 @@ static int _ldap_url_parse2(struct Curl_easy *data,
   if(!data ||
      !data->state.up.path ||
      data->state.up.path[0] != '/' ||
-     !strncasecompare("LDAP", data->state.up.scheme, 4))
+     !curl_strnequal("LDAP", data->state.up.scheme, 4))
     return LDAP_INVALID_SYNTAX;
 
   ludp->lud_scope = LDAP_SCOPE_BASE;

+ 2 - 1
lib/llist.c

@@ -181,7 +181,8 @@ void *Curl_node_take_elem(struct Curl_llist_node *e)
 /*
  * @unittest: 1300
  */
-void
+UNITTEST void Curl_node_uremove(struct Curl_llist_node *, void *);
+UNITTEST void
 Curl_node_uremove(struct Curl_llist_node *e, void *user)
 {
   struct Curl_llist *list;

+ 1 - 2
lib/llist.h

@@ -57,7 +57,6 @@ void Curl_llist_insert_next(struct Curl_llist *, struct Curl_llist_node *,
                             const void *, struct Curl_llist_node *node);
 void Curl_llist_append(struct Curl_llist *,
                        const void *, struct Curl_llist_node *node);
-void Curl_node_uremove(struct Curl_llist_node *, void *);
 void Curl_node_remove(struct Curl_llist_node *);
 void Curl_llist_destroy(struct Curl_llist *, void *);
 
@@ -76,7 +75,7 @@ size_t Curl_llist_count(struct Curl_llist *list);
 void *Curl_node_elem(struct Curl_llist_node *n);
 
 /* Remove the node from the list and return the custom data
- * from a Curl_llist_node. Will NOT incoke a registered `dtor`. */
+ * from a Curl_llist_node. Will NOT invoke a registered `dtor`. */
 void *Curl_node_take_elem(struct Curl_llist_node *);
 
 /* Curl_node_next() returns the next element in a list from a given

+ 26 - 19
lib/memdebug.c

@@ -30,8 +30,6 @@
 
 #include "urldata.h"
 
-#define MEMDEBUG_NODEFINES /* do not redefine the standard functions */
-
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -70,7 +68,7 @@ static void curl_dbg_cleanup(void)
   if(curl_dbg_logfile &&
      curl_dbg_logfile != stderr &&
      curl_dbg_logfile != stdout) {
-    fclose(curl_dbg_logfile);
+    (fclose)(curl_dbg_logfile);
   }
   curl_dbg_logfile = NULL;
 }
@@ -80,7 +78,11 @@ void curl_dbg_memdebug(const char *logname)
 {
   if(!curl_dbg_logfile) {
     if(logname && *logname)
-      curl_dbg_logfile = fopen(logname, FOPEN_WRITETEXT);
+#ifdef CURL_FOPEN
+      curl_dbg_logfile = CURL_FOPEN(logname, FOPEN_WRITETEXT);
+#else
+      curl_dbg_logfile = (fopen)(logname, FOPEN_WRITETEXT);
+#endif
     else
       curl_dbg_logfile = stderr;
 #ifdef MEMDEBUG_LOG_SYNC
@@ -302,14 +304,14 @@ void curl_dbg_free(void *ptr, int line, const char *source)
 }
 
 curl_socket_t curl_dbg_socket(int domain, int type, int protocol,
-                             int line, const char *source)
+                              int line, const char *source)
 {
   curl_socket_t sockfd;
 
   if(countcheck("socket", line, source))
     return CURL_SOCKET_BAD;
 
-  sockfd = socket(domain, type, protocol);
+  sockfd = (socket)(domain, type, protocol);
 
   if(source && (sockfd != CURL_SOCKET_BAD))
     curl_dbg_log("FD %s:%d socket() = %" FMT_SOCKET_T "\n",
@@ -326,7 +328,7 @@ SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd,
   SEND_TYPE_RETV rc;
   if(countcheck("send", line, source))
     return -1;
-  rc = send(sockfd, buf, len, flags);
+  rc = (send)(sockfd, buf, len, flags);
   if(source)
     curl_dbg_log("SEND %s:%d send(%lu) = %ld\n",
                 source, line, (unsigned long)len, (long)rc);
@@ -340,7 +342,7 @@ RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd, RECV_TYPE_ARG2 buf,
   RECV_TYPE_RETV rc;
   if(countcheck("recv", line, source))
     return -1;
-  rc = recv(sockfd, buf, len, flags);
+  rc = (recv)(sockfd, buf, len, flags);
   if(source)
     curl_dbg_log("RECV %s:%d recv(%lu) = %ld\n",
                 source, line, (unsigned long)len, (long)rc);
@@ -349,10 +351,10 @@ RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd, RECV_TYPE_ARG2 buf,
 
 #ifdef HAVE_SOCKETPAIR
 int curl_dbg_socketpair(int domain, int type, int protocol,
-                       curl_socket_t socket_vector[2],
-                       int line, const char *source)
+                        curl_socket_t socket_vector[2],
+                        int line, const char *source)
 {
-  int res = socketpair(domain, type, protocol, socket_vector);
+  int res = (socketpair)(domain, type, protocol, socket_vector);
 
   if(source && (0 == res))
     curl_dbg_log("FD %s:%d socketpair() = "
@@ -364,12 +366,12 @@ int curl_dbg_socketpair(int domain, int type, int protocol,
 #endif
 
 curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen,
-                             int line, const char *source)
+                              int line, const char *source)
 {
   struct sockaddr *addr = (struct sockaddr *)saddr;
   curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen;
 
-  curl_socket_t sockfd = accept(s, addr, addrlen);
+  curl_socket_t sockfd = (accept)(s, addr, addrlen);
 
   if(source && (sockfd != CURL_SOCKET_BAD))
     curl_dbg_log("FD %s:%d accept() = %" FMT_SOCKET_T "\n",
@@ -386,7 +388,7 @@ curl_socket_t curl_dbg_accept4(curl_socket_t s, void *saddr, void *saddrlen,
   struct sockaddr *addr = (struct sockaddr *)saddr;
   curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen;
 
-  curl_socket_t sockfd = accept4(s, addr, addrlen, flags);
+  curl_socket_t sockfd = (accept4)(s, addr, addrlen, flags);
 
   if(source && (sockfd != CURL_SOCKET_BAD))
     curl_dbg_log("FD %s:%d accept() = %" FMT_SOCKET_T "\n",
@@ -407,7 +409,7 @@ void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source)
 /* this is our own defined way to close sockets on *ALL* platforms */
 int curl_dbg_sclose(curl_socket_t sockfd, int line, const char *source)
 {
-  int res = sclose(sockfd);
+  int res = CURL_SCLOSE(sockfd);
   curl_dbg_mark_sclose(sockfd, line, source);
   return res;
 }
@@ -416,7 +418,12 @@ ALLOC_FUNC
 FILE *curl_dbg_fopen(const char *file, const char *mode,
                      int line, const char *source)
 {
-  FILE *res = fopen(file, mode);
+  FILE *res;
+#ifdef CURL_FOPEN
+  res = CURL_FOPEN(file, mode);
+#else
+  res = (fopen)(file, mode);
+#endif
 
   if(source)
     curl_dbg_log("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n",
@@ -429,7 +436,7 @@ ALLOC_FUNC
 FILE *curl_dbg_fdopen(int filedes, const char *mode,
                       int line, const char *source)
 {
-  FILE *res = fdopen(filedes, mode);
+  FILE *res = (fdopen)(filedes, mode);
   if(source)
     curl_dbg_log("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n",
                  source, line, filedes, mode, (void *)res);
@@ -446,7 +453,7 @@ int curl_dbg_fclose(FILE *file, int line, const char *source)
     curl_dbg_log("FILE %s:%d fclose(%p)\n",
                  source, line, (void *)file);
 
-  res = fclose(file);
+  res = (fclose)(file);
 
   return res;
 }
@@ -469,7 +476,7 @@ void curl_dbg_log(const char *format, ...)
     nchars = (int)sizeof(buf) - 1;
 
   if(nchars > 0)
-    fwrite(buf, 1, (size_t)nchars, curl_dbg_logfile);
+    (fwrite)(buf, 1, (size_t)nchars, curl_dbg_logfile);
 }
 
 #endif /* CURLDEBUG */

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff