瀏覽代碼

curl 2025-07-16 (cfbfb650)

Code extracted from:

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

at commit cfbfb65047e85e6b08af65fe9cdbcf68e9ad496a (curl-8_15_0).
Curl Upstream 3 月之前
父節點
當前提交
71e5adbcc9
共有 100 個文件被更改,包括 3313 次插入2920 次删除
  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
 #ifdef HAVE_UNISTD_H
 #  include <unistd.h>
 #  include <unistd.h>
 #endif
 #endif
-#ifdef HAVE_SYS_SOCKET_H
+#ifndef _WIN32
 #  include <sys/socket.h>
 #  include <sys/socket.h>
 #endif
 #endif
 #ifdef HAVE_SYS_IOCTL_H
 #ifdef HAVE_SYS_IOCTL_H
@@ -230,7 +230,7 @@ int main(void)
 #ifdef HAVE_UNISTD_H
 #ifdef HAVE_UNISTD_H
 #  include <unistd.h>
 #  include <unistd.h>
 #endif
 #endif
-#ifdef HAVE_SYS_SOCKET_H
+#ifndef _WIN32
 #  include <sys/socket.h>
 #  include <sys/socket.h>
 #endif
 #endif
 #ifdef HAVE_SYS_IOCTL_H
 #ifdef HAVE_SYS_IOCTL_H
@@ -257,7 +257,7 @@ int main(void)
 #ifdef HAVE_SYS_TYPES_H
 #ifdef HAVE_SYS_TYPES_H
 #  include <sys/types.h>
 #  include <sys/types.h>
 #endif
 #endif
-#ifdef HAVE_SYS_SOCKET_H
+#ifndef _WIN32
 #  include <sys/socket.h>
 #  include <sys/socket.h>
 #endif
 #endif
 int main(void)
 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}"
   "$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
 # 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(NOT GSS_ROOT_DIR AND NOT "$ENV{GSS_ROOT_DIR}")
   if(CURL_USE_PKGCONFIG)
   if(CURL_USE_PKGCONFIG)
     find_package(PkgConfig QUIET)
     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()
   endif()
   if(WIN32)
   if(WIN32)
     list(APPEND _gss_root_hints "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos;InstallDir]")
     list(APPEND _gss_root_hints "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos;InstallDir]")
   endif()
   endif()
 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
   find_file(_gss_configure_script
     NAMES
     NAMES
       "krb5-config"
       "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)
   if(_gss_configure_script)
+
+    set(_gss_INCLUDE_DIRS "")
+    set(_gss_LIBRARIES "")
+
     execute_process(
     execute_process(
       COMMAND ${_gss_configure_script} "--cflags" "gssapi"
       COMMAND ${_gss_configure_script} "--cflags" "gssapi"
-      OUTPUT_VARIABLE _GSS_CFLAGS
+      OUTPUT_VARIABLE _gss_cflags_raw
       RESULT_VARIABLE _gss_configure_failed
       RESULT_VARIABLE _gss_configure_failed
       OUTPUT_STRIP_TRAILING_WHITESPACE
       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
     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")
         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()
         else()
-          list(APPEND _GSS_CFLAGS "${_flag}")
+          list(APPEND _gss_CFLAGS "${_flag}")
         endif()
         endif()
       endforeach()
       endforeach()
     endif()
     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}")
     message(STATUS "FindGSS krb5-config --libs: ${_gss_lib_flags}")
 
 
     if(NOT _gss_configure_failed)  # 0 means success
     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(STRIP "${_gss_lib_flags}" _gss_lib_flags)
       string(REGEX REPLACE " +-(L|l)" ";-\\1" _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}")
       string(REGEX REPLACE " +-([^Ll][^ \\t;]*)" ";-\\1" _gss_lib_flags "${_gss_lib_flags}")
 
 
       foreach(_flag IN LISTS _gss_lib_flags)
       foreach(_flag IN LISTS _gss_lib_flags)
         if(_flag MATCHES "^-l")
         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")
         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()
         endif()
       endforeach()
       endforeach()
     endif()
     endif()
 
 
     execute_process(
     execute_process(
       COMMAND ${_gss_configure_script} "--version"
       COMMAND ${_gss_configure_script} "--version"
-      OUTPUT_VARIABLE _GSS_VERSION
+      OUTPUT_VARIABLE _gss_version
       RESULT_VARIABLE _gss_configure_failed
       RESULT_VARIABLE _gss_configure_failed
       OUTPUT_STRIP_TRAILING_WHITESPACE
       OUTPUT_STRIP_TRAILING_WHITESPACE
     )
     )
 
 
     # Older versions may not have the "--version" parameter. In this case we just do not care.
     # Older versions may not have the "--version" parameter. In this case we just do not care.
     if(_gss_configure_failed)
     if(_gss_configure_failed)
-      set(_GSS_VERSION 0)
+      set(_gss_version 0)
     endif()
     endif()
 
 
     execute_process(
     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?)
   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
       HINTS
         ${_gss_root_hints}
         ${_gss_root_hints}
       PATH_SUFFIXES
       PATH_SUFFIXES
@@ -173,9 +181,9 @@ if(NOT _GSS_FOUND)  # Not found by pkg-config. Let us take more traditional appr
         "inc"
         "inc"
     )
     )
 
 
-    if(_GSS_INCLUDE_DIRS)  # jay, we have found something
+    if(_gss_INCLUDE_DIRS)  # jay, we have found something
       cmake_push_check_state()
       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)
       check_include_files("gssapi/gssapi_generic.h;gssapi/gssapi_krb5.h" _gss_have_mit_headers)
 
 
       if(_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()
       cmake_pop_check_state()
     else()
     else()
       # I am not convinced if this is the right way but this is what autotools do at the moment
       # 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
         HINTS
           ${_gss_root_hints}
           ${_gss_root_hints}
         PATH_SUFFIXES
         PATH_SUFFIXES
@@ -201,17 +209,17 @@ if(NOT _GSS_FOUND)  # Not found by pkg-config. Let us take more traditional appr
           "inc"
           "inc"
       )
       )
 
 
-      if(_GSS_INCLUDE_DIRS)
+      if(_gss_INCLUDE_DIRS)
         set(GSS_FLAVOUR "Heimdal")
         set(GSS_FLAVOUR "Heimdal")
       else()
       else()
-        find_path(_GSS_INCLUDE_DIRS NAMES "gss.h"
+        find_path(_gss_INCLUDE_DIRS NAMES "gss.h"
           HINTS
           HINTS
             ${_gss_root_hints}
             ${_gss_root_hints}
           PATH_SUFFIXES
           PATH_SUFFIXES
             "include"
             "include"
         )
         )
 
 
-        if(_GSS_INCLUDE_DIRS)
+        if(_gss_INCLUDE_DIRS)
           set(GSS_FLAVOUR "GNU")
           set(GSS_FLAVOUR "GNU")
           set(GSS_PC_REQUIRES "gss")
           set(GSS_PC_REQUIRES "gss")
         endif()
         endif()
@@ -222,7 +230,7 @@ if(NOT _GSS_FOUND)  # Not found by pkg-config. Let us take more traditional appr
     if(GSS_FLAVOUR)
     if(GSS_FLAVOUR)
       set(_gss_libdir_suffixes "")
       set(_gss_libdir_suffixes "")
       set(_gss_libdir_hints ${_gss_root_hints})
       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})
       list(APPEND _gss_libdir_hints ${_gss_calculated_potential_root})
 
 
       if(WIN32)
       if(WIN32)
@@ -256,7 +264,7 @@ if(NOT _GSS_FOUND)  # Not found by pkg-config. Let us take more traditional appr
         endif()
         endif()
       endif()
       endif()
 
 
-      find_library(_GSS_LIBRARIES NAMES ${_gss_libname}
+      find_library(_gss_LIBRARIES NAMES ${_gss_libname}
         HINTS
         HINTS
           ${_gss_libdir_hints}
           ${_gss_libdir_hints}
         PATH_SUFFIXES
         PATH_SUFFIXES
@@ -265,36 +273,36 @@ if(NOT _GSS_FOUND)  # Not found by pkg-config. Let us take more traditional appr
     endif()
     endif()
   endif()
   endif()
 else()
 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_FLAVOUR "GNU")
     set(GSS_PC_REQUIRES "gss")
     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()
     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_FLAVOUR "MIT")
     set(GSS_PC_REQUIRES "mit-krb5-gssapi")
     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()
     endif()
   else()
   else()
     set(GSS_FLAVOUR "Heimdal")
     set(GSS_FLAVOUR "Heimdal")
     set(GSS_PC_REQUIRES "heimdal-gssapi")
     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()
   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()
 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(GSS_FLAVOUR)
   if(NOT GSS_VERSION AND GSS_FLAVOUR STREQUAL "Heimdal")
   if(NOT GSS_VERSION AND GSS_FLAVOUR STREQUAL "Heimdal")
@@ -346,12 +354,12 @@ find_package_handle_standard_args(GSS
 )
 )
 
 
 mark_as_advanced(
 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()
   endif()
 
 
   include(FindPackageHandleStandardArgs)
   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)
   if(LIBGSASL_FOUND)

+ 15 - 10
CMake/FindNGTCP2.cmake

@@ -34,23 +34,28 @@
 #
 #
 # Input variables:
 # 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:
 # 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)
 if(NGTCP2_FIND_COMPONENTS)
   set(_ngtcp2_crypto_backend "")
   set(_ngtcp2_crypto_backend "")
   foreach(_component IN LISTS NGTCP2_FIND_COMPONENTS)
   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)
       if(_ngtcp2_crypto_backend)
         message(FATAL_ERROR "NGTCP2: Only one crypto library can be selected")
         message(FATAL_ERROR "NGTCP2: Only one crypto library can be selected")
       endif()
       endif()

+ 78 - 0
CMake/Macros.cmake

@@ -65,6 +65,7 @@ macro(curl_internal_test _curl_test)
   endif()
   endif()
 endmacro()
 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)
 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(${_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")
   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} ${_size})
   set(SIZEOF_${_type}_CODE "#define SIZEOF_${_type} ${_size}")
   set(SIZEOF_${_type}_CODE "#define SIZEOF_${_type} ${_size}")
 endmacro()
 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(CheckCSourceRuns)
 include(CheckTypeSize)
 include(CheckTypeSize)
 
 
+# #include header if condition is true
 macro(curl_add_header_include _check _header)
 macro(curl_add_header_include _check _header)
   if(${_check})
   if(${_check})
     set(_source_epilogue "${_source_epilogue}
     set(_source_epilogue "${_source_epilogue}
@@ -41,7 +42,7 @@ if(NOT DEFINED HAVE_STRUCT_SOCKADDR_STORAGE)
   if(WIN32)
   if(WIN32)
     set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h")
     set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h")
     list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32")
     list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32")
-  elseif(HAVE_SYS_SOCKET_H)
+  else()
     set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h")
     set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h")
   endif()
   endif()
   check_type_size("struct sockaddr_storage" SIZEOF_STRUCT_SOCKADDR_STORAGE)
   check_type_size("struct sockaddr_storage" SIZEOF_STRUCT_SOCKADDR_STORAGE)
@@ -52,8 +53,8 @@ endif()
 if(NOT WIN32)
 if(NOT WIN32)
   set(_source_epilogue "#undef inline")
   set(_source_epilogue "#undef inline")
   curl_add_header_include(HAVE_SYS_TYPES_H "sys/types.h")
   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}
   check_c_source_compiles("${_source_epilogue}
+    #include <sys/socket.h>
     int main(void)
     int main(void)
     {
     {
       int flag = MSG_NOSIGNAL;
       int flag = MSG_NOSIGNAL;
@@ -63,11 +64,13 @@ if(NOT WIN32)
 endif()
 endif()
 
 
 set(_source_epilogue "#undef inline")
 set(_source_epilogue "#undef inline")
-curl_add_header_include(HAVE_SYS_TIME_H "sys/time.h")
 check_c_source_compiles("${_source_epilogue}
 check_c_source_compiles("${_source_epilogue}
   #ifdef _MSC_VER
   #ifdef _MSC_VER
   #include <winsock2.h>
   #include <winsock2.h>
   #endif
   #endif
+  #ifndef _WIN32
+  #include <sys/time.h>
+  #endif
   #include <time.h>
   #include <time.h>
   int main(void)
   int main(void)
   {
   {
@@ -100,9 +103,11 @@ elseif(BSD OR CMAKE_SYSTEM_NAME MATCHES "BSD")
 endif()
 endif()
 
 
 if(NOT DEFINED HAVE_GETADDRINFO_THREADSAFE)
 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")
   curl_add_header_include(HAVE_NETDB_H "netdb.h")
   check_c_source_compiles("${_source_epilogue}
   check_c_source_compiles("${_source_epilogue}
     int main(void)
     int main(void)
@@ -143,8 +148,8 @@ endif()
 if(NOT WIN32 AND NOT DEFINED HAVE_CLOCK_GETTIME_MONOTONIC_RAW)
 if(NOT WIN32 AND NOT DEFINED HAVE_CLOCK_GETTIME_MONOTONIC_RAW)
   set(_source_epilogue "#undef inline")
   set(_source_epilogue "#undef inline")
   curl_add_header_include(HAVE_SYS_TYPES_H "sys/types.h")
   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}
   check_c_source_compiles("${_source_epilogue}
+    #include <sys/time.h>
     #include <time.h>
     #include <time.h>
     int main(void)
     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 "-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 "-wd4710")  # 'snprintf': function not inlined
     list(APPEND _picky "-wd4711")  # function 'A' selected for automatic inline expansion
     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 "-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'
     list(APPEND _picky "-wd4820")  # 'A': 'N' bytes padding added after data member 'B'
     if(MSVC_VERSION GREATER_EQUAL 1900)
     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}")
         list(APPEND _picky_tmp "-clang:${_ccopt}")
       endif()
       endif()
     endforeach()
     endforeach()
-    set("${_wlist}" ${_picky_tmp})
+    set("${_wlist}" ${_picky_tmp})  # cmake-lint: disable=C0103
   endforeach()
   endforeach()
 endif()
 endif()
 
 

+ 28 - 0
CMake/Utilities.cmake

@@ -51,3 +51,31 @@ function(curl_dumpvars)
   endforeach()
   endforeach()
   message("::endgroup::")
   message("::endgroup::")
 endfunction()
 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_POLL_H 1)
 set(HAVE_SYS_RESOURCE_H 1)
 set(HAVE_SYS_RESOURCE_H 1)
 set(HAVE_SYS_SELECT_H 1)
 set(HAVE_SYS_SELECT_H 1)
-set(HAVE_SYS_SOCKET_H 1)
 if(CYGWIN OR
 if(CYGWIN OR
    CMAKE_SYSTEM_NAME STREQUAL "Linux")
    CMAKE_SYSTEM_NAME STREQUAL "Linux")
   set(HAVE_SYS_SOCKIO_H 0)
   set(HAVE_SYS_SOCKIO_H 0)
 else()
 else()
   set(HAVE_SYS_SOCKIO_H 1)
   set(HAVE_SYS_SOCKIO_H 1)
 endif()
 endif()
-set(HAVE_SYS_STAT_H 1)
-set(HAVE_SYS_TIME_H 1)
 set(HAVE_SYS_TYPES_H 1)
 set(HAVE_SYS_TYPES_H 1)
 set(HAVE_SYS_UN_H 1)
 set(HAVE_SYS_UN_H 1)
 if(CYGWIN)
 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_STDINT_H 1)  # detected by CMake internally in check_type_size()
   set(HAVE_STRINGS_H 1)  # wrapper to string.h
   set(HAVE_STRINGS_H 1)  # wrapper to string.h
   set(HAVE_SYS_PARAM_H 1)
   set(HAVE_SYS_PARAM_H 1)
-  set(HAVE_SYS_TIME_H 1)
   set(HAVE_UNISTD_H 1)
   set(HAVE_UNISTD_H 1)
   set(HAVE_UTIME_H 1)  # wrapper to sys/utime.h
   set(HAVE_UTIME_H 1)  # wrapper to sys/utime.h
 else()
 else()
@@ -50,7 +49,6 @@ else()
   set(HAVE_OPENDIR 0)
   set(HAVE_OPENDIR 0)
   set(HAVE_STRINGS_H 0)
   set(HAVE_STRINGS_H 0)
   set(HAVE_SYS_PARAM_H 0)
   set(HAVE_SYS_PARAM_H 0)
-  set(HAVE_SYS_TIME_H 0)
   set(HAVE_UTIME_H 0)
   set(HAVE_UTIME_H 0)
   if(MSVC)
   if(MSVC)
     set(HAVE_UNISTD_H 0)
     set(HAVE_UNISTD_H 0)
@@ -118,7 +116,6 @@ set(HAVE_GETSOCKNAME 1)
 set(HAVE_GLIBC_STRERROR_R 0)
 set(HAVE_GLIBC_STRERROR_R 0)
 set(HAVE_GMTIME_R 0)
 set(HAVE_GMTIME_R 0)
 set(HAVE_IFADDRS_H 0)
 set(HAVE_IFADDRS_H 0)
-set(HAVE_IF_NAMETOINDEX 0)
 set(HAVE_INET_NTOP 0)
 set(HAVE_INET_NTOP 0)
 set(HAVE_INET_PTON 0)
 set(HAVE_INET_PTON 0)
 set(HAVE_IOCTLSOCKET 1)
 set(HAVE_IOCTLSOCKET 1)
@@ -171,9 +168,7 @@ set(HAVE_SYS_IOCTL_H 0)
 set(HAVE_SYS_POLL_H 0)
 set(HAVE_SYS_POLL_H 0)
 set(HAVE_SYS_RESOURCE_H 0)
 set(HAVE_SYS_RESOURCE_H 0)
 set(HAVE_SYS_SELECT_H 0)
 set(HAVE_SYS_SELECT_H 0)
-set(HAVE_SYS_SOCKET_H 0)
 set(HAVE_SYS_SOCKIO_H 0)
 set(HAVE_SYS_SOCKIO_H 0)
-set(HAVE_SYS_STAT_H 1)
 set(HAVE_SYS_TYPES_H 1)
 set(HAVE_SYS_TYPES_H 1)
 set(HAVE_SYS_UN_H 0)
 set(HAVE_SYS_UN_H 0)
 set(HAVE_SYS_UTIME_H 1)
 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.")
   message(WARNING "This curl build is Debug-enabled and insecure, do not use in production.")
 endif()
 endif()
 option(ENABLE_CURLDEBUG "Enable TrackMemory debug feature" ${ENABLE_DEBUG})
 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 "")
 set(CURL_DEBUG_MACROS "")
 if(ENABLE_DEBUG)
 if(ENABLE_DEBUG)
@@ -287,15 +286,11 @@ if(ENABLE_CURLDEBUG)
   list(APPEND CURL_DEBUG_MACROS "CURLDEBUG")
   list(APPEND CURL_DEBUG_MACROS "CURLDEBUG")
 endif()
 endif()
 
 
-option(CURL_TEST_BUNDLES "Build tests into single-binary bundles" OFF)
-
 option(CURL_CLANG_TIDY "Run the build through clang-tidy" OFF)
 option(CURL_CLANG_TIDY "Run the build through clang-tidy" OFF)
 if(CURL_CLANG_TIDY)
 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 "")
   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-security.insecureAPI.strcpy")
   list(APPEND _tidy_checks "-clang-analyzer-optin.performance.Padding")
   list(APPEND _tidy_checks "-clang-analyzer-optin.performance.Padding")
   list(APPEND _tidy_checks "-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling")
   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_LIBS ${CARES_LIBRARIES})
   list(APPEND CURL_LIBDIRS ${CARES_LIBRARY_DIRS})
   list(APPEND CURL_LIBDIRS ${CARES_LIBRARY_DIRS})
   list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${CARES_PC_REQUIRES})
   list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${CARES_PC_REQUIRES})
+  include_directories(SYSTEM ${CARES_INCLUDE_DIRS})
   link_directories(${CARES_LIBRARY_DIRS})
   link_directories(${CARES_LIBRARY_DIRS})
   if(CARES_CFLAGS)
   if(CARES_CFLAGS)
     string(APPEND CMAKE_C_FLAGS " ${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"
     DEPENDS "${PROJECT_SOURCE_DIR}/scripts/mk-ca-bundle.pl"
   )
   )
   add_custom_target(curl-ca-firefox
   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"
     COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/firefox-db2pem.sh" "lib/ca-bundle.crt"
     DEPENDS "${PROJECT_SOURCE_DIR}/scripts/firefox-db2pem.sh"
     DEPENDS "${PROJECT_SOURCE_DIR}/scripts/firefox-db2pem.sh"
   )
   )
@@ -605,10 +601,12 @@ if(WIN32)
     set(_win32_winsock "ws2_32")
     set(_win32_winsock "ws2_32")
   endif()
   endif()
   set(_win32_crypt32 "crypt32")
   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"
   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_winsock "${MINGW32CE_LIBRARY_DIR}/lib${_win32_winsock}.a")
     set(_win32_crypt32 "${MINGW32CE_LIBRARY_DIR}/lib${_win32_crypt32}.a")
     set(_win32_crypt32 "${MINGW32CE_LIBRARY_DIR}/lib${_win32_crypt32}.a")
+    set(_win32_secur32 "${MINGW32CE_LIBRARY_DIR}/lib${_win32_secur32}.a")
   endif()
   endif()
 elseif(DOS)
 elseif(DOS)
   if(WATT_ROOT)
   if(WATT_ROOT)
@@ -677,24 +675,18 @@ if(CURL_DEFAULT_SSL_BACKEND)
   set(_valid_default_ssl_backend FALSE)
   set(_valid_default_ssl_backend FALSE)
 endif()
 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)
 if(WIN32)
   cmake_dependent_option(CURL_USE_SCHANNEL "Enable Windows native SSL/TLS (Schannel)" OFF CURL_ENABLE_SSL OFF)
   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})
   option(CURL_WINDOWS_SSPI "Enable SSPI on Windows" ${CURL_USE_SCHANNEL})
 endif()
 endif()
 cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
 cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
-cmake_dependent_option(CURL_USE_BEARSSL "Enable BearSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
 cmake_dependent_option(CURL_USE_WOLFSSL "Enable wolfSSL 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_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)
 cmake_dependent_option(CURL_USE_RUSTLS "Enable Rustls for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
 
 
 if(WIN32 OR
 if(WIN32 OR
-   CURL_USE_SECTRANSP OR
    CURL_USE_SCHANNEL OR
    CURL_USE_SCHANNEL OR
    CURL_USE_MBEDTLS OR
    CURL_USE_MBEDTLS OR
-   CURL_USE_BEARSSL OR
    CURL_USE_WOLFSSL OR
    CURL_USE_WOLFSSL OR
    CURL_USE_GNUTLS OR
    CURL_USE_GNUTLS OR
    CURL_USE_RUSTLS)
    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_count_true(_enabled_ssl_options_count
   CURL_USE_SCHANNEL
   CURL_USE_SCHANNEL
-  CURL_USE_SECTRANSP
   CURL_USE_OPENSSL
   CURL_USE_OPENSSL
   CURL_USE_MBEDTLS
   CURL_USE_MBEDTLS
-  CURL_USE_BEARSSL
   CURL_USE_WOLFSSL
   CURL_USE_WOLFSSL
   CURL_USE_GNUTLS
   CURL_USE_GNUTLS
   CURL_USE_RUSTLS
   CURL_USE_RUSTLS
@@ -739,26 +729,6 @@ if(CURL_WINDOWS_SSPI)
   set(USE_WINDOWS_SSPI ON)
   set(USE_WINDOWS_SSPI ON)
 endif()
 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)
 if(_use_core_foundation_and_core_services)
   find_library(COREFOUNDATION_FRAMEWORK NAMES "CoreFoundation")
   find_library(COREFOUNDATION_FRAMEWORK NAMES "CoreFoundation")
   mark_as_advanced(COREFOUNDATION_FRAMEWORK)
   mark_as_advanced(COREFOUNDATION_FRAMEWORK)
@@ -842,21 +812,6 @@ if(CURL_USE_MBEDTLS)
   set(_curl_ca_bundle_supported TRUE)
   set(_curl_ca_bundle_supported TRUE)
 endif()
 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)
 if(CURL_USE_WOLFSSL)
   find_package(WolfSSL REQUIRED)
   find_package(WolfSSL REQUIRED)
   set(_ssl_enabled ON)
   set(_ssl_enabled ON)
@@ -1651,6 +1606,23 @@ if(WIN32)
     # Windows XP is required for freeaddrinfo, getaddrinfo
     # Windows XP is required for freeaddrinfo, getaddrinfo
     message(FATAL_ERROR "Building for Windows XP or newer is required.")
     message(FATAL_ERROR "Building for Windows XP or newer is required.")
   endif()
   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()
 endif()
 
 
 # Detect headers
 # 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/poll.h"       HAVE_SYS_POLL_H)
 check_include_file("sys/resource.h"   HAVE_SYS_RESOURCE_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/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/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_concat_curl("sys/types.h"      HAVE_SYS_TYPES_H)
 check_include_file("sys/un.h"         HAVE_SYS_UN_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)
 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_STDBOOL_H
     HAVE_STROPTS_H
     HAVE_STROPTS_H
     HAVE_SYS_IOCTL_H
     HAVE_SYS_IOCTL_H
-    HAVE_SYS_SOCKET_H
     HAVE_SYS_TYPES_H
     HAVE_SYS_TYPES_H
     HAVE_UNISTD_H
     HAVE_UNISTD_H
-    )
+)
   if(${_variable})
   if(${_variable})
     string(APPEND CURL_TEST_DEFINES " -D${_variable}")
     string(APPEND CURL_TEST_DEFINES " -D${_variable}")
   endif()
   endif()
@@ -1749,6 +1717,9 @@ endif()
 # Apply to all feature checks
 # Apply to all feature checks
 if(WIN32)
 if(WIN32)
   list(APPEND CMAKE_REQUIRED_LIBRARIES "${_win32_winsock}")
   list(APPEND CMAKE_REQUIRED_LIBRARIES "${_win32_winsock}")
+  if(NOT WINCE AND NOT WINDOWS_STORE)
+    list(APPEND CMAKE_REQUIRED_LIBRARIES "iphlpapi")
+  endif()
 elseif(HAVE_LIBSOCKET)
 elseif(HAVE_LIBSOCKET)
   list(APPEND CMAKE_REQUIRED_LIBRARIES "socket")
   list(APPEND CMAKE_REQUIRED_LIBRARIES "socket")
 elseif(DOS)
 elseif(DOS)
@@ -1800,12 +1771,12 @@ check_function_exists("eventfd"       HAVE_EVENTFD)
 check_symbol_exists("ftruncate"       "unistd.h" HAVE_FTRUNCATE)
 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("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_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("getrlimit"       HAVE_GETRLIMIT)
 check_function_exists("setlocale"       HAVE_SETLOCALE)
 check_function_exists("setlocale"       HAVE_SETLOCALE)
 check_function_exists("setrlimit"       HAVE_SETRLIMIT)
 check_function_exists("setrlimit"       HAVE_SETRLIMIT)
 
 
 if(NOT WIN32)
 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("realpath"        HAVE_REALPATH)
   check_function_exists("sched_yield"     HAVE_SCHED_YIELD)
   check_function_exists("sched_yield"     HAVE_SCHED_YIELD)
   check_symbol_exists("strcasecmp"      "string.h" HAVE_STRCASECMP)
   check_symbol_exists("strcasecmp"      "string.h" HAVE_STRCASECMP)
@@ -1852,7 +1823,7 @@ if(WIN32)
   list(APPEND CMAKE_EXTRA_INCLUDE_FILES "winsock2.h")
   list(APPEND CMAKE_EXTRA_INCLUDE_FILES "winsock2.h")
   check_type_size("ADDRESS_FAMILY" SIZEOF_ADDRESS_FAMILY)
   check_type_size("ADDRESS_FAMILY" SIZEOF_ADDRESS_FAMILY)
   set(HAVE_ADDRESS_FAMILY ${HAVE_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")
   list(APPEND CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h")
   check_type_size("sa_family_t" SIZEOF_SA_FAMILY_T)
   check_type_size("sa_family_t" SIZEOF_SA_FAMILY_T)
   set(HAVE_SA_FAMILY_T ${HAVE_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
     HAVE_BOOL_T
     STDC_HEADERS
     STDC_HEADERS
     HAVE_ATOMIC
     HAVE_ATOMIC
-    )
+)
   curl_internal_test(${_curl_test})
   curl_internal_test(${_curl_test})
 endforeach()
 endforeach()
 
 
@@ -1984,6 +1955,9 @@ set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "HAVE_CONFIG_H")
 
 
 if(WIN32)
 if(WIN32)
   list(APPEND CURL_LIBS "${_win32_winsock}")
   list(APPEND CURL_LIBS "${_win32_winsock}")
+  if(NOT WINCE AND NOT WINDOWS_STORE)
+    list(APPEND CURL_LIBS "iphlpapi")
+  endif()
   if(NOT WINCE)
   if(NOT WINCE)
     list(APPEND CURL_LIBS "bcrypt")
     list(APPEND CURL_LIBS "bcrypt")
   endif()
   endif()
@@ -2004,6 +1978,9 @@ if(WIN32)
     endif()
     endif()
     list(APPEND CURL_LIBS "${_win32_crypt32}")
     list(APPEND CURL_LIBS "${_win32_crypt32}")
   endif()
   endif()
+  if(USE_WINDOWS_SSPI)
+    list(APPEND CURL_LIBS "${_win32_secur32}")
+  endif()
 endif()
 endif()
 
 
 if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")  # MSVC but exclude clang-cl
 if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")  # MSVC but exclude clang-cl
@@ -2031,6 +2008,7 @@ endif()
 # (= regenerate it).
 # (= regenerate it).
 function(curl_transform_makefile_inc _input_file _output_file)
 function(curl_transform_makefile_inc _input_file _output_file)
   file(READ ${_input_file} _makefile_inc_text)
   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_srcdir)"   "\${PROJECT_SOURCE_DIR}" _makefile_inc_text ${_makefile_inc_text})
   string(REPLACE "$(top_builddir)" "\${PROJECT_BINARY_DIR}" _makefile_inc_text ${_makefile_inc_text})
   string(REPLACE "$(top_builddir)" "\${PROJECT_BINARY_DIR}" _makefile_inc_text ${_makefile_inc_text})
 
 
@@ -2102,7 +2080,6 @@ if(NOT CURL_DISABLE_NTLM AND
    (USE_OPENSSL OR
    (USE_OPENSSL OR
     USE_MBEDTLS OR
     USE_MBEDTLS OR
     USE_GNUTLS OR
     USE_GNUTLS OR
-    USE_SECTRANSP OR
     USE_WIN32_CRYPTO OR
     USE_WIN32_CRYPTO OR
     (USE_WOLFSSL AND HAVE_WOLFSSL_DES_ECB_ENCRYPT)))
     (USE_WOLFSSL AND HAVE_WOLFSSL_DES_ECB_ENCRYPT)))
   set(_use_curl_ntlm_core ON)
   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("HTTP3"         USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC)
 curl_add_if("MultiSSL"      CURL_WITH_MULTI_SSL)
 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
 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)))
                             (USE_WOLFSSL AND HAVE_WOLFSSL_BIO_NEW)))
 curl_add_if("Unicode"       ENABLE_UNICODE)
 curl_add_if("Unicode"       ENABLE_UNICODE)
 curl_add_if("threadsafe"    HAVE_ATOMIC OR
 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("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}"      _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("${_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("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("wolfSSL"          _ssl_enabled AND USE_WOLFSSL)
 curl_add_if("GnuTLS"           _ssl_enabled AND USE_GNUTLS)
 curl_add_if("GnuTLS"           _ssl_enabled AND USE_GNUTLS)
 curl_add_if("rustls"           _ssl_enabled AND USE_RUSTLS)
 curl_add_if("rustls"           _ssl_enabled AND USE_RUSTLS)

+ 15 - 27
include/curl/curl.h

@@ -158,11 +158,11 @@ typedef enum {
   CURLSSLBACKEND_POLARSSL               CURL_DEPRECATED(7.69.0, "") = 6,
   CURLSSLBACKEND_POLARSSL               CURL_DEPRECATED(7.69.0, "") = 6,
   CURLSSLBACKEND_WOLFSSL = 7,
   CURLSSLBACKEND_WOLFSSL = 7,
   CURLSSLBACKEND_SCHANNEL = 8,
   CURLSSLBACKEND_SCHANNEL = 8,
-  CURLSSLBACKEND_SECURETRANSPORT = 9,
+  CURLSSLBACKEND_SECURETRANSPORT        CURL_DEPRECATED(8.15.0, "") = 9,
   CURLSSLBACKEND_AXTLS                  CURL_DEPRECATED(7.61.0, "") = 10,
   CURLSSLBACKEND_AXTLS                  CURL_DEPRECATED(7.61.0, "") = 10,
   CURLSSLBACKEND_MBEDTLS = 11,
   CURLSSLBACKEND_MBEDTLS = 11,
   CURLSSLBACKEND_MESALINK               CURL_DEPRECATED(7.82.0, "") = 12,
   CURLSSLBACKEND_MESALINK               CURL_DEPRECATED(7.82.0, "") = 12,
-  CURLSSLBACKEND_BEARSSL = 13,
+  CURLSSLBACKEND_BEARSSL                CURL_DEPRECATED(8.15.0, "") = 13,
   CURLSSLBACKEND_RUSTLS = 14
   CURLSSLBACKEND_RUSTLS = 14
 } curl_sslbackend;
 } curl_sslbackend;
 
 
@@ -645,20 +645,7 @@ typedef enum {
   CURLE_UNRECOVERABLE_POLL,      /* 99 - poll/select returned fatal error */
   CURLE_UNRECOVERABLE_POLL,      /* 99 - poll/select returned fatal error */
   CURLE_TOO_LARGE,               /* 100 - a value/data met its maximum */
   CURLE_TOO_LARGE,               /* 100 - a value/data met its maximum */
   CURLE_ECH_REQUIRED,            /* 101 - ECH tried but failed */
   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;
 } CURLcode;
 
 
 #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
 #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
    have introduced work-arounds for this flaw but those work-arounds sometimes
    make the SSL communication fail. To regain functionality with those broken
    make the SSL communication fail. To regain functionality with those broken
    servers, a user can this way allow the vulnerability back. */
    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
 /* - NO_REVOKE tells libcurl to disable certificate revocation checks for those
    SSL backends where such behavior is present. */
    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
 /* - NO_PARTIALCHAIN tells libcurl to *NOT* accept a partial certificate chain
    if possible. The OpenSSL backend has this ability. */
    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
 /* - REVOKE_BEST_EFFORT tells libcurl to ignore certificate revocation offline
    checks and ignore missing revocation list for those SSL backends where such
    checks and ignore missing revocation list for those SSL backends where such
    behavior is present. */
    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
 /* - CURLSSLOPT_NATIVE_CA tells libcurl to use standard certificate store of
    operating system. Currently implemented under MS-Windows. */
    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
 /* - CURLSSLOPT_AUTO_CLIENT_CERT tells libcurl to automatically locate and use
    a client certificate for authentication. (Schannel) */
    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 */
 /* 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.
 /* The default connection attempt delay in milliseconds for happy eyeballs.
    CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.3 and happy-eyeballs-timeout-ms.d document
    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),
   CURLOPT(CURLOPT_SSL_VERIFYSTATUS, CURLOPTTYPE_LONG, 232),
 
 
   /* Set if we should enable TLS false start. */
   /* 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 */
   /* Do not squash dot-dot sequences */
   CURLOPT(CURLOPT_PATH_AS_IS, CURLOPTTYPE_LONG, 234),
   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
   /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host
      name resolves addresses using more than one IP protocol version, this
      name resolves addresses using more than one IP protocol version, this
      option might be handy to force libcurl to use a specific IP version. */
      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 */
                                      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" */
   /* Convenient "aliases" */
 #define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER
 #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
 /* This is the version number of the libcurl package from which this header
    file origins: */
    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
 /* The numeric version number is also available "in parts" by using these
    defines: */
    defines: */
 #define LIBCURL_VERSION_MAJOR 8
 #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
 /* This is the numeric version of the libcurl version number, meant for easier
    parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will
    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
    CURL_VERSION_BITS() macro since curl's own configure script greps for it
    and needs it to contain the full number.
    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
  * 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(__ppc__) || defined(__powerpc__) || defined(__arm__) ||      \
    defined(__sparc__) || defined(__mips__) || defined(__sh__) ||        \
    defined(__sparc__) || defined(__mips__) || defined(__sh__) ||        \
    defined(__XTENSA__) ||                                               \
    defined(__XTENSA__) ||                                               \
-   (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4)  ||               \
+   (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4) ||                \
    (defined(__LONG_MAX__) && __LONG_MAX__ == 2147483647L))
    (defined(__LONG_MAX__) && __LONG_MAX__ == 2147483647L))
 #    define CURL_TYPEOF_CURL_OFF_T     long long
 #    define CURL_TYPEOF_CURL_OFF_T     long long
 #    define CURL_FORMAT_CURL_OFF_T     "lld"
 #    define CURL_FORMAT_CURL_OFF_T     "lld"
@@ -357,11 +357,11 @@
 
 
 #else
 #else
 /* generic "safe guess" on old 32-bit style */
 /* 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
 #  define CURL_TYPEOF_CURL_SOCKLEN_T int
 #endif
 #endif
 
 
@@ -399,52 +399,4 @@
   typedef CURL_TYPEOF_CURL_OFF_T curl_off_t;
   typedef CURL_TYPEOF_CURL_OFF_T curl_off_t;
 #endif
 #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 */
 #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")
 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")
 curl_transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
 include("${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"
   "${PROJECT_BINARY_DIR}/lib"        # for "curl_config.h"
 )
 )
 
 
-if(USE_ARES)
-  include_directories(SYSTEM ${CARES_INCLUDE_DIRS})
-endif()
-
 if(CURL_BUILD_TESTING)
 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_compile_definitions(curlu PUBLIC "CURL_STATICLIB" "UNITTESTS")
   target_link_libraries(curlu PRIVATE ${CURL_LIBS})
   target_link_libraries(curlu PRIVATE ${CURL_LIBS})
   # There is plenty of parallelism when building the testdeps target.
   # There is plenty of parallelism when building the testdeps target.
   # Override the curlu batch size with the maximum to optimize performance.
   # 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()
 endif()
 
 
 ## Library definition
 ## Library definition
@@ -181,7 +175,7 @@ if(BUILD_SHARED_LIBS)
   add_library(${PROJECT_NAME}::${LIB_SHARED} ALIAS ${LIB_SHARED})
   add_library(${PROJECT_NAME}::${LIB_SHARED} ALIAS ${LIB_SHARED})
   if(WIN32)
   if(WIN32)
     set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "dllmain.c")
     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)
     if(CURL_HIDES_PRIVATE_SYMBOLS)
       set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "${PROJECT_SOURCE_DIR}/lib/libcurl.def")
       set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "${PROJECT_SOURCE_DIR}/lib/libcurl.def")
     endif()
     endif()
@@ -217,6 +211,7 @@ if(BUILD_SHARED_LIBS)
     CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
     CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
     CMAKE_SYSTEM_NAME STREQUAL "SunOS" OR
     CMAKE_SYSTEM_NAME STREQUAL "SunOS" OR
     CMAKE_SYSTEM_NAME STREQUAL "Haiku" OR
     CMAKE_SYSTEM_NAME STREQUAL "Haiku" OR
+    CMAKE_SYSTEM_NAME STREQUAL "OHOS" OR  # OpenHarmony
     CMAKE_SYSTEM_NAME STREQUAL "GNU/kFreeBSD" OR
     CMAKE_SYSTEM_NAME STREQUAL "GNU/kFreeBSD" OR
     # FreeBSD comes with the a.out and ELF flavours but a.out was supported
     # 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
     # 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)
   option(CURL_LIBCURL_VERSIONED_SYMBOLS "Enable libcurl versioned symbols" OFF)
 
 
   if(CURL_LIBCURL_SOVERSION OR CURL_LIBCURL_VERSIONED_SYMBOLS)
   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")
     curl_transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")
     include("${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_")
         set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "OPENSSL_")
       elseif(CURL_USE_MBEDTLS)
       elseif(CURL_USE_MBEDTLS)
         set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "MBEDTLS_")
         set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "MBEDTLS_")
-      elseif(CURL_USE_BEARSSL)
-        set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "BEARSSL_")
       elseif(CURL_USE_WOLFSSL)
       elseif(CURL_USE_WOLFSSL)
         set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "WOLFSSL_")
         set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "WOLFSSL_")
       elseif(CURL_USE_GNUTLS)
       elseif(CURL_USE_GNUTLS)

+ 6 - 6
lib/Makefile.inc

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

+ 6 - 4
lib/altsvc.c

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

+ 4 - 3
lib/asyn-ares.c

@@ -48,6 +48,7 @@
 #endif
 #endif
 
 
 #include "urldata.h"
 #include "urldata.h"
+#include "cfilters.h"
 #include "sendf.h"
 #include "sendf.h"
 #include "hostip.h"
 #include "hostip.h"
 #include "hash.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
   /* Operation complete, if the lookup was successful we now have the entry
      in the cache. */
      in the cache. */
   data->state.async.done = TRUE;
   data->state.async.done = TRUE;
-  if(entry)
-    *entry = data->state.async.dns;
+  *entry = data->state.async.dns;
 
 
   if(result)
   if(result)
     ares_cancel(ares->channel);
     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_UNSPEC) ? "A+AAAA" :
                  ((pf == PF_INET) ? "A" : "AAAA"));
                  ((pf == PF_INET) ? "A" : "AAAA"));
     hints.ai_family = pf;
     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;
       SOCK_STREAM : SOCK_DGRAM;
     /* Since the service is a numerical one, set the hint flags
     /* Since the service is a numerical one, set the hint flags
      * accordingly to save a call to getservbyname in inside C-Ares
      * accordingly to save a call to getservbyname in inside C-Ares

+ 4 - 2
lib/asyn-thrdd.c

@@ -55,13 +55,13 @@
 #endif
 #endif
 
 
 #include "urldata.h"
 #include "urldata.h"
+#include "cfilters.h"
 #include "sendf.h"
 #include "sendf.h"
 #include "hostip.h"
 #include "hostip.h"
 #include "hash.h"
 #include "hash.h"
 #include "share.h"
 #include "share.h"
 #include "url.h"
 #include "url.h"
 #include "multiif.h"
 #include "multiif.h"
-#include "inet_ntop.h"
 #include "curl_threads.h"
 #include "curl_threads.h"
 #include "strdup.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.done = FALSE;
   data->state.async.port = port;
   data->state.async.port = port;
   data->state.async.ip_version = ip_version;
   data->state.async.ip_version = ip_version;
+  free(data->state.async.hostname);
   data->state.async.hostname = strdup(hostname);
   data->state.async.hostname = strdup(hostname);
   if(!data->state.async.hostname)
   if(!data->state.async.hostname)
     goto err_exit;
     goto err_exit;
@@ -741,7 +742,8 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
 
 
   memset(&hints, 0, sizeof(hints));
   memset(&hints, 0, sizeof(hints));
   hints.ai_family = pf;
   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;
     SOCK_STREAM : SOCK_DGRAM;
 
 
   /* fire up a new resolver thread! */
   /* 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];
   unsigned char *p = &chunk->x.data[chunk->w_offset];
   size_t n = chunk->dlen - chunk->w_offset; /* free amount */
   size_t n = chunk->dlen - chunk->w_offset; /* free amount */
-  ssize_t nread;
+  CURLcode result;
 
 
+  *pnread = 0;
   DEBUGASSERT(chunk->dlen >= chunk->w_offset);
   DEBUGASSERT(chunk->dlen >= chunk->w_offset);
-  if(!n) {
-    *err = CURLE_AGAIN;
-    return -1;
-  }
+  if(!n)
+    return CURLE_AGAIN;
   if(max_len && n > max_len)
   if(max_len && n > 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,
 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)
 static struct buf_chunk *get_non_full_tail(struct bufq *q)
 {
 {
   struct buf_chunk *chunk;
   struct buf_chunk *chunk;
@@ -421,90 +360,60 @@ static struct buf_chunk *get_non_full_tail(struct bufq *q)
   return chunk;
   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;
   struct buf_chunk *tail;
-  ssize_t nwritten = 0;
   size_t n;
   size_t n;
 
 
   DEBUGASSERT(q->max_chunks > 0);
   DEBUGASSERT(q->max_chunks > 0);
+  *pnwritten = 0;
   while(len) {
   while(len) {
     tail = get_non_full_tail(q);
     tail = get_non_full_tail(q);
     if(!tail) {
     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;
       break;
     }
     }
     n = chunk_append(tail, buf, len);
     n = chunk_append(tail, buf, len);
     if(!n)
     if(!n)
       break;
       break;
-    nwritten += n;
+    *pnwritten += n;
     buf += n;
     buf += n;
     len -= 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,
 CURLcode Curl_bufq_cwrite(struct bufq *q,
                           const char *buf, size_t len,
                           const char *buf, size_t len,
                           size_t *pnwritten)
                           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) {
   while(len && q->head) {
-    n = chunk_read(q->head, buf, len);
+    size_t n = chunk_read(q->head, buf, len);
     if(n) {
     if(n) {
-      nread += n;
+      *pnread += n;
       buf += n;
       buf += n;
       len -= n;
       len -= n;
     }
     }
     prune_head(q);
     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,
 CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len,
                          size_t *pnread)
                          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,
 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;
   const unsigned char *buf;
   size_t blen;
   size_t blen;
-  ssize_t nwritten = 0;
+  CURLcode result = CURLE_OK;
 
 
+  *pwritten = 0;
   while(Curl_bufq_peek(q, &buf, &blen)) {
   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;
       break;
     }
     }
     if(!chunk_written) {
     if(!chunk_written) {
-      if(!nwritten) {
+      if(!*pwritten) {
         /* treat as blocked */
         /* treat as blocked */
-        *err = CURLE_AGAIN;
-        nwritten = -1;
+        result = CURLE_AGAIN;
       }
       }
       break;
       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) {
   while(len) {
     if(Curl_bufq_is_full(q)) {
     if(Curl_bufq_is_full(q)) {
       /* try to make room in case we are full */
       /* 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 */
           /* real error, fail */
-          return -1;
+          return result;
         }
         }
         /* would block, bufq is full, give up */
         /* would block, bufq is full, give up */
         break;
         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 */
         /* 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;
       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;
   struct buf_chunk *tail = NULL;
-  ssize_t nread;
 
 
-  *err = CURLE_AGAIN;
+  *pnread = 0;
   tail = get_non_full_tail(q);
   tail = get_non_full_tail(q);
   if(!tail) {
   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 */
     /* 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.
  * 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
  * if `max_len` is 0, no limit is imposed and the call behaves exactly
  * the same as `Curl_bufq_slurp()`.
  * 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.
  * 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) {
   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 */
         /* blocked on first read or real error, fail */
-        nread = -1;
+        return result;
       }
       }
-      else
-        *err = CURLE_OK;
+      result = CURLE_OK;
       break;
       break;
     }
     }
     else if(n == 0) {
     else if(n == 0) {
       /* eof */
       /* eof */
-      *err = CURLE_OK;
+      result = CURLE_OK;
       break;
       break;
     }
     }
-    nread += (size_t)n;
+    *pnread += n;
     if(max_len) {
     if(max_len) {
-      DEBUGASSERT((size_t)n <= max_len);
-      max_len -= (size_t)n;
+      DEBUGASSERT(n <= max_len);
+      max_len -= n;
       if(!max_len)
       if(!max_len)
         break;
         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))
     if(q->tail && !chunk_is_full(q->tail))
       break;
       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
  * Write buf to the end of the buffer queue. The buf is copied
  * and the amount of copied bytes is returned.
  * 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);
                          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
  * Read buf from the start of the buffer queue. The buf is copied
  * and the amount of copied bytes is returned.
  * 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,
 CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len,
                          size_t *pnread);
                          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);
 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
  * 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
  * 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
  * Note that in case of a -1 chunks may have been written and
  * the buffer queue will have different length than before.
  * 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
  * 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
  * 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.
  * 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.
  * 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
  * Returns the total amount of buf read (may be 0) or -1 on other
  * reader errors.
  * 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.
  * 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
  * on or is placed into the buffer, depending on `len` and current
  * amount buffered, chunk size, etc.
  * 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 */
 #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 request_len = curlx_dyn_len(&ts->request_data);
   size_t blen = request_len;
   size_t blen = request_len;
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
-  ssize_t nwritten;
+  size_t nwritten;
 
 
   if(blen <= ts->nsent)
   if(blen <= ts->nsent)
     goto out;  /* we are done */
     goto out;  /* we are done */
@@ -260,16 +260,15 @@ static CURLcode send_CONNECT(struct Curl_cfilter *cf,
   blen -= ts->nsent;
   blen -= ts->nsent;
   buf += 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;
       result = CURLE_OK;
-    }
     goto out;
     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);
   Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)nwritten);
 
 
 out:
 out:
@@ -375,11 +374,8 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
   error = SELECT_OK;
   error = SELECT_OK;
   *done = FALSE;
   *done = FALSE;
 
 
-  if(!Curl_conn_data_pending(data, cf->sockindex))
-    return CURLE_OK;
-
   while(ts->keepon) {
   while(ts->keepon) {
-    ssize_t nread;
+    size_t nread;
     char byte;
     char byte;
 
 
     /* Read one byte at a time to avoid a race condition. Wait at most one
     /* 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;
       break;
     }
     }
 
 
-    if(nread <= 0) {
+    if(!nread) {
       if(data->set.proxyauth && data->state.authproxy.avail &&
       if(data->set.proxyauth && data->state.authproxy.avail &&
          data->state.aptr.proxyuserpwd) {
          data->state.aptr.proxyuserpwd) {
         /* proxy auth was requested and there was proxy auth available,
         /* 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,
 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;
   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_connect,
   cf_h1_proxy_close,
   cf_h1_proxy_close,
   Curl_cf_def_shutdown,
   Curl_cf_def_shutdown,
-  Curl_cf_http_proxy_get_host,
   cf_h1_proxy_adjust_pollset,
   cf_h1_proxy_adjust_pollset,
   Curl_cf_def_data_pending,
   Curl_cf_def_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_send,
@@ -749,7 +744,7 @@ struct Curl_cftype Curl_cft_h1_proxy = {
   Curl_cf_def_cntrl,
   Curl_cf_def_cntrl,
   Curl_cf_def_conn_is_alive,
   Curl_cf_def_conn_is_alive,
   Curl_cf_def_conn_keep_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,
 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 <nghttp2/nghttp2.h>
 #include "urldata.h"
 #include "urldata.h"
+#include "url.h"
 #include "cfilters.h"
 #include "cfilters.h"
 #include "connect.h"
 #include "connect.h"
 #include "curl_trc.h"
 #include "curl_trc.h"
@@ -80,7 +81,7 @@ struct tunnel_stream {
 };
 };
 
 
 static CURLcode tunnel_stream_init(struct Curl_cfilter *cf,
 static CURLcode tunnel_stream_init(struct Curl_cfilter *cf,
-                                    struct tunnel_stream *ts)
+                                   struct tunnel_stream *ts)
 {
 {
   const char *hostname;
   const char *hostname;
   int port;
   int port;
@@ -218,58 +219,28 @@ static void drain_tunnel(struct Curl_cfilter *cf,
                          struct tunnel_stream *tunnel)
                          struct tunnel_stream *tunnel)
 {
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
-  unsigned char bits;
-
   (void)cf;
   (void)cf;
-  bits = CURL_CSELECT_IN;
   if(!tunnel->closed && !tunnel->reset &&
   if(!tunnel->closed && !tunnel->reset &&
      !Curl_bufq_is_empty(&ctx->tunnel.sendbuf))
      !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;
   struct Curl_cfilter *cf = writer_ctx;
-  ssize_t nwritten;
-
+  *pnwritten = 0;
   if(cf) {
   if(cf) {
     struct Curl_easy *data = CF_DATA_CURRENT(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,
 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,
 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,
 static int proxy_h2_on_frame_recv(nghttp2_session *session,
                                   const nghttp2_frame *frame,
                                   const nghttp2_frame *frame,
                                   void *userp);
                                   void *userp);
@@ -414,16 +385,16 @@ static CURLcode proxy_h2_nw_out_flush(struct Curl_cfilter *cf,
                                       struct Curl_easy *data)
                                       struct Curl_easy *data)
 {
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
-  ssize_t nwritten;
+  size_t nwritten;
   CURLcode result;
   CURLcode result;
 
 
   (void)data;
   (void)data;
   if(Curl_bufq_is_empty(&ctx->outbufq))
   if(Curl_bufq_is_empty(&ctx->outbufq))
     return CURLE_OK;
     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) {
     if(result == CURLE_AGAIN) {
       CURL_TRC_CF(data, cf, "[0] flush nw send buffer(%zu) -> EAGAIN",
       CURL_TRC_CF(data, cf, "[0] flush nw send buffer(%zu) -> EAGAIN",
                   Curl_bufq_len(&ctx->outbufq));
                   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;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
-  ssize_t nread;
+  size_t nread;
 
 
   /* Process network input buffer fist */
   /* Process network input buffer fist */
   if(!Curl_bufq_is_empty(&ctx->inbufq)) {
   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_empty(&ctx->inbufq) && /* and we consumed our input */
         !Curl_bufq_is_full(&ctx->tunnel.recvbuf)) {
         !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) {
       if(result != CURLE_AGAIN) {
         failf(data, "Failed receiving HTTP2 data");
         failf(data, "Failed receiving HTTP2 data");
         return result;
         return result;
@@ -547,17 +518,18 @@ static ssize_t on_session_send(nghttp2_session *h2,
   struct Curl_cfilter *cf = userp;
   struct Curl_cfilter *cf = userp;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
-  ssize_t nwritten;
+  size_t nwritten;
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
 
 
   (void)h2;
   (void)h2;
   (void)flags;
   (void)flags;
   DEBUGASSERT(data);
   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) {
     if(result == CURLE_AGAIN) {
+      ctx->nw_out_blocked = 1;
       return NGHTTP2_ERR_WOULDBLOCK;
       return NGHTTP2_ERR_WOULDBLOCK;
     }
     }
     failf(data, "Failed sending HTTP2 data");
     failf(data, "Failed sending HTTP2 data");
@@ -567,7 +539,8 @@ static ssize_t on_session_send(nghttp2_session *h2,
   if(!nwritten)
   if(!nwritten)
     return NGHTTP2_ERR_WOULDBLOCK;
     return NGHTTP2_ERR_WOULDBLOCK;
 
 
-  return nwritten;
+  return (nwritten  > SSIZE_T_MAX) ?
+    NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nwritten;
 }
 }
 
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 #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 Curl_easy *data = CF_DATA_CURRENT(cf);
   struct tunnel_stream *ts;
   struct tunnel_stream *ts;
   CURLcode result;
   CURLcode result;
-  ssize_t nread;
+  size_t nread;
 
 
   (void)source;
   (void)source;
   (void)data;
   (void)data;
@@ -831,8 +804,8 @@ static ssize_t tunnel_send_callback(nghttp2_session *session,
     return NGHTTP2_ERR_CALLBACK_FAILURE;
     return NGHTTP2_ERR_CALLBACK_FAILURE;
   DEBUGASSERT(ts == &ctx->tunnel);
   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)
     if(result != CURLE_AGAIN)
       return NGHTTP2_ERR_CALLBACK_FAILURE;
       return NGHTTP2_ERR_CALLBACK_FAILURE;
     return NGHTTP2_ERR_DEFERRED;
     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",
   CURL_TRC_CF(data, cf, "[%d] tunnel_send_callback -> %zd",
               ts->stream_id, nread);
               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,
 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 Curl_cfilter *cf = userp;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
-  ssize_t nwritten;
+  size_t nwritten;
   CURLcode result;
   CURLcode result;
 
 
   (void)flags;
   (void)flags;
@@ -861,14 +835,15 @@ static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags,
   if(stream_id != ctx->tunnel.stream_id)
   if(stream_id != ctx->tunnel.stream_id)
     return NGHTTP2_ERR_CALLBACK_FAILURE;
     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)
     if(result != CURLE_AGAIN)
       return NGHTTP2_ERR_CALLBACK_FAILURE;
       return NGHTTP2_ERR_CALLBACK_FAILURE;
 #ifdef DEBUGBUILD
 #ifdef DEBUGBUILD
     nwritten = 0;
     nwritten = 0;
 #endif
 #endif
   }
   }
+  /* tunnel.recbuf has soft limit, any success MUST add all data */
   DEBUGASSERT((size_t)nwritten == len);
   DEBUGASSERT((size_t)nwritten == len);
   return 0;
   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;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
-  ssize_t rv = 0;
 
 
+  *pnread = 0;
   if(ctx->tunnel.error == NGHTTP2_REFUSED_STREAM) {
   if(ctx->tunnel.error == NGHTTP2_REFUSED_STREAM) {
     CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new "
     CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new "
                 "connection", ctx->tunnel.stream_id);
                 "connection", ctx->tunnel.stream_id);
     connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */
     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) {
   else if(ctx->tunnel.error != NGHTTP2_NO_ERROR) {
     failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)",
     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.stream_id, nghttp2_http2_strerror(ctx->tunnel.error),
           ctx->tunnel.error);
           ctx->tunnel.error);
-    *err = CURLE_HTTP2_STREAM;
-    return -1;
+    return CURLE_HTTP2_STREAM;
   }
   }
   else if(ctx->tunnel.reset) {
   else if(ctx->tunnel.reset) {
     failf(data, "HTTP/2 stream %u was reset", ctx->tunnel.stream_id);
     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;
   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) {
     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 ||
     else if(ctx->tunnel.reset ||
             (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) ||
             (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) ||
             (ctx->rcvd_goaway &&
             (ctx->rcvd_goaway &&
              ctx->last_stream_id < ctx->tunnel.stream_id)) {
              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;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
-  ssize_t nread = -1;
   struct cf_call_data save;
   struct cf_call_data save;
   CURLcode result;
   CURLcode result;
 
 
+  *pnread = 0;
+  CF_DATA_SAVE(save, cf, data);
+
   if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) {
   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)) {
   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;
       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:
 out:
   if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) &&
   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
     /* data pending and no fatal error to report. Need to trigger
      * draining to avoid stalling when no socket events happen. */
      * draining to avoid stalling when no socket events happen. */
     drain_tunnel(cf, data, &ctx->tunnel);
     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);
   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_h2_proxy_ctx *ctx = cf->ctx;
   struct cf_call_data save;
   struct cf_call_data save;
   int rv;
   int rv;
-  ssize_t nwritten;
   CURLcode result;
   CURLcode result;
 
 
   (void)eos;
   (void)eos;
+  *pnwritten = 0;
+  CF_DATA_SAVE(save, cf, data);
+
   if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) {
   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) {
   if(ctx->tunnel.closed) {
-    nwritten = -1;
-    *err = CURLE_SEND_ERROR;
+    result = CURLE_SEND_ERROR;
     goto out;
     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)) {
   if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) {
     /* req body data is buffered, resume the potentially suspended stream */
     /* req body data is buffered, resume the potentially suspended stream */
     rv = nghttp2_session_resume_data(ctx->h2, ctx->tunnel.stream_id);
     rv = nghttp2_session_resume_data(ctx->h2, ctx->tunnel.stream_id);
     if(nghttp2_is_fatal(rv)) {
     if(nghttp2_is_fatal(rv)) {
-      *err = CURLE_SEND_ERROR;
-      nwritten = -1;
+      result = CURLE_SEND_ERROR;
       goto out;
       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
     /* nghttp2 thinks this session is done. If the stream has not been
      * closed, this is an error state for out transfer */
      * closed, this is an error state for out transfer */
     if(ctx->tunnel.closed) {
     if(ctx->tunnel.closed) {
-      *err = CURLE_SEND_ERROR;
-      nwritten = -1;
+      result = CURLE_SEND_ERROR;
     }
     }
     else {
     else {
       CURL_TRC_CF(data, cf, "[0] send: nothing to do in this session");
       CURL_TRC_CF(data, cf, "[0] send: nothing to do in this session");
-      *err = CURLE_HTTP2;
-      nwritten = -1;
+      result = CURLE_HTTP2;
     }
     }
   }
   }
 
 
 out:
 out:
   if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) &&
   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
     /* data pending and no fatal error to report. Need to trigger
      * draining to avoid stalling when no socket events happen. */
      * draining to avoid stalling when no socket events happen. */
     drain_tunnel(cf, data, &ctx->tunnel);
     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)",
               "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(
               nghttp2_session_get_stream_remote_window_size(
                   ctx->h2, ctx->tunnel.stream_id),
                   ctx->h2, ctx->tunnel.stream_id),
               nghttp2_session_get_remote_window_size(ctx->h2),
               nghttp2_session_get_remote_window_size(ctx->h2),
               Curl_bufq_len(&ctx->tunnel.sendbuf),
               Curl_bufq_len(&ctx->tunnel.sendbuf),
               Curl_bufq_len(&ctx->outbufq));
               Curl_bufq_len(&ctx->outbufq));
   CF_DATA_RESTORE(cf, save);
   CF_DATA_RESTORE(cf, save);
-  return nwritten;
+  return result;
 }
 }
 
 
 static CURLcode cf_h2_proxy_flush(struct Curl_cfilter *cf,
 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,
        not in use by any other transfer, there should not be any data here,
        only "protocol frames" */
        only "protocol frames" */
     CURLcode result;
     CURLcode result;
-    ssize_t nread = -1;
+    size_t nread;
 
 
     *input_pending = FALSE;
     *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)
       if(proxy_h2_process_pending_input(cf, data, &result) < 0)
         /* immediate error, considered dead */
         /* immediate error, considered dead */
         alive = FALSE;
         alive = FALSE;
@@ -1559,15 +1501,16 @@ static bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf,
                                  bool *input_pending)
                                  bool *input_pending)
 {
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
-  CURLcode result;
+  bool alive;
   struct cf_call_data save;
   struct cf_call_data save;
 
 
+  *input_pending = FALSE;
   CF_DATA_SAVE(save, cf, data);
   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",
   CURL_TRC_CF(data, cf, "[0] conn alive -> %d, input_pending=%d",
-              result, *input_pending);
+              alive, *input_pending);
   CF_DATA_RESTORE(cf, save);
   CF_DATA_RESTORE(cf, save);
-  return result;
+  return alive;
 }
 }
 
 
 static CURLcode cf_h2_proxy_query(struct Curl_cfilter *cf,
 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;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
 
 
   switch(query) {
   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: {
   case CF_QUERY_NEED_FLUSH: {
     if(!Curl_bufq_is_empty(&ctx->outbufq) ||
     if(!Curl_bufq_is_empty(&ctx->outbufq) ||
        !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) {
        !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_connect,
   cf_h2_proxy_close,
   cf_h2_proxy_close,
   cf_h2_proxy_shutdown,
   cf_h2_proxy_shutdown,
-  Curl_cf_http_proxy_get_host,
   cf_h2_proxy_adjust_pollset,
   cf_h2_proxy_adjust_pollset,
   cf_h2_proxy_data_pending,
   cf_h2_proxy_data_pending,
   cf_h2_proxy_send,
   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:
   case HAPROXY_SEND:
     len = curlx_dyn_len(&ctx->data_out);
     len = curlx_dyn_len(&ctx->data_out);
     if(len > 0) {
     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)
         if(result != CURLE_AGAIN)
           goto out;
           goto out;
         result = CURLE_OK;
         result = CURLE_OK;
         nwritten = 0;
         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) {
       if(curlx_dyn_len(&ctx->data_out) > 0) {
         result = CURLE_OK;
         result = CURLE_OK;
         goto out;
         goto out;
@@ -197,7 +197,6 @@ struct Curl_cftype Curl_cft_haproxy = {
   cf_haproxy_connect,
   cf_haproxy_connect,
   cf_haproxy_close,
   cf_haproxy_close,
   Curl_cf_def_shutdown,
   Curl_cf_def_shutdown,
-  Curl_cf_def_get_host,
   cf_haproxy_adjust_pollset,
   cf_haproxy_adjust_pollset,
   Curl_cf_def_data_pending,
   Curl_cf_def_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_send,

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

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

+ 51 - 76
lib/cf-socket.c

@@ -73,7 +73,6 @@
 #include "url.h" /* for Curl_safefree() */
 #include "url.h" /* for Curl_safefree() */
 #include "multiif.h"
 #include "multiif.h"
 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
-#include "inet_ntop.h"
 #include "curlx/inet_pton.h"
 #include "curlx/inet_pton.h"
 #include "progress.h"
 #include "progress.h"
 #include "curlx/warnless.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,
 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;
   struct Curl_sockaddr_ex dummy;
   CURLcode result;
   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;
       cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
     socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
     socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
     ctx->sock = CURL_SOCKET_BAD;
     ctx->sock = CURL_SOCKET_BAD;
-    if(ctx->active && cf->sockindex == FIRSTSOCKET)
-      cf->conn->remote_addr = NULL;
     ctx->active = FALSE;
     ctx->active = FALSE;
     memset(&ctx->started_at, 0, sizeof(ctx->started_at));
     memset(&ctx->started_at, 0, sizeof(ctx->started_at));
     memset(&ctx->connected_at, 0, sizeof(ctx->connected_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,
 static CURLcode cf_socket_open(struct Curl_cfilter *cf,
-                              struct Curl_easy *data)
+                               struct Curl_easy *data)
 {
 {
   struct cf_socket_ctx *ctx = cf->ctx;
   struct cf_socket_ctx *ctx = cf->ctx;
   int error = 0;
   int error = 0;
@@ -1378,22 +1375,9 @@ out:
   return result;
   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,
 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;
   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
 #ifdef USE_WINSOCK
 
 
 #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
 #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
@@ -1457,17 +1430,18 @@ static void win_update_sndbuf_size(struct cf_socket_ctx *ctx)
 
 
 #endif /* USE_WINSOCK */
 #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;
   struct cf_socket_ctx *ctx = cf->ctx;
   curl_socket_t fdsave;
   curl_socket_t fdsave;
   ssize_t nwritten;
   ssize_t nwritten;
   size_t orig_len = len;
   size_t orig_len = len;
+  CURLcode result = CURLE_OK;
 
 
   (void)eos; /* unused */
   (void)eos; /* unused */
-  *err = CURLE_OK;
+  *pnwritten = 0;
   fdsave = cf->conn->sock[cf->sockindex];
   fdsave = cf->conn->sock[cf->sockindex];
   cf->conn->sock[cf->sockindex] = ctx->sock;
   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);
     Curl_rand_bytes(data, FALSE, &c, 1);
     if(c >= ((100-ctx->wblock_percent)*256/100)) {
     if(c >= ((100-ctx->wblock_percent)*256/100)) {
       CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len);
       CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len);
-      *err = CURLE_AGAIN;
-      nwritten = -1;
       cf->conn->sock[cf->sockindex] = fdsave;
       cf->conn->sock[cf->sockindex] = fdsave;
-      return nwritten;
+      return CURLE_AGAIN;
     }
     }
   }
   }
   if(cf->cft != &Curl_cft_udp && ctx->wpartial_percent > 0 && len > 8) {
   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 defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
   if(cf->conn->bits.tcp_fastopen) {
   if(cf->conn->bits.tcp_fastopen) {
     nwritten = sendto(ctx->sock, buf, len, MSG_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;
     cf->conn->bits.tcp_fastopen = FALSE;
   }
   }
   else
   else
 #endif
 #endif
     nwritten = swrite(ctx->sock, buf, len);
     nwritten = swrite(ctx->sock, buf, len);
 
 
-  if(-1 == nwritten) {
+  if(nwritten < 0) {
     int sockerr = SOCKERRNO;
     int sockerr = SOCKERRNO;
 
 
     if(
     if(
@@ -1521,36 +1492,38 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
 #endif
 #endif
       ) {
       ) {
       /* this is just a case of EWOULDBLOCK */
       /* this is just a case of EWOULDBLOCK */
-      *err = CURLE_AGAIN;
+      result = CURLE_AGAIN;
     }
     }
     else {
     else {
       char buffer[STRERROR_LEN];
       char buffer[STRERROR_LEN];
       failf(data, "Send failure: %s",
       failf(data, "Send failure: %s",
             Curl_strerror(sockerr, buffer, sizeof(buffer)));
             Curl_strerror(sockerr, buffer, sizeof(buffer)));
       data->state.os_errno = sockerr;
       data->state.os_errno = sockerr;
-      *err = CURLE_SEND_ERROR;
+      result = CURLE_SEND_ERROR;
     }
     }
   }
   }
+  else
+    *pnwritten = (size_t)nwritten;
 
 
 #if defined(USE_WINSOCK)
 #if defined(USE_WINSOCK)
-  if(!*err)
+  if(!result)
     win_update_sndbuf_size(ctx);
     win_update_sndbuf_size(ctx);
 #endif
 #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;
   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;
   struct cf_socket_ctx *ctx = cf->ctx;
+  CURLcode result = CURLE_OK;
   ssize_t nread;
   ssize_t nread;
 
 
-  *err = CURLE_OK;
-
+  *pnread = 0;
 #ifdef DEBUGBUILD
 #ifdef DEBUGBUILD
   /* simulate network blocking/partial reads */
   /* simulate network blocking/partial reads */
   if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) {
   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);
     Curl_rand(data, &c, 1);
     if(c >= ((100-ctx->rblock_percent)*256/100)) {
     if(c >= ((100-ctx->rblock_percent)*256/100)) {
       CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len);
       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) {
   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
 #endif
 
 
-  *err = CURLE_OK;
   nread = sread(ctx->sock, buf, len);
   nread = sread(ctx->sock, buf, len);
 
 
-  if(-1 == nread) {
+  if(nread < 0) {
     int sockerr = SOCKERRNO;
     int sockerr = SOCKERRNO;
 
 
     if(
     if(
@@ -1589,25 +1560,25 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
 #endif
 #endif
       ) {
       ) {
       /* this is just a case of EWOULDBLOCK */
       /* this is just a case of EWOULDBLOCK */
-      *err = CURLE_AGAIN;
+      result = CURLE_AGAIN;
     }
     }
     else {
     else {
       char buffer[STRERROR_LEN];
       char buffer[STRERROR_LEN];
-
       failf(data, "Recv failure: %s",
       failf(data, "Recv failure: %s",
             Curl_strerror(sockerr, buffer, sizeof(buffer)));
             Curl_strerror(sockerr, buffer, sizeof(buffer)));
       data->state.os_errno = sockerr;
       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->first_byte_at = curlx_now();
     ctx->got_first_byte = TRUE;
     ctx->got_first_byte = TRUE;
   }
   }
-  return nread;
+  return result;
 }
 }
 
 
 static void cf_socket_update_data(struct Curl_cfilter *cf,
 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);
   set_local_ip(cf, data);
   if(cf->sockindex == FIRSTSOCKET) {
   if(cf->sockindex == FIRSTSOCKET) {
     cf->conn->primary = ctx->ip;
     cf->conn->primary = ctx->ip;
-    cf->conn->remote_addr = &ctx->addr;
   #ifdef USE_IPV6
   #ifdef USE_IPV6
     cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6);
     cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6);
   #endif
   #endif
@@ -1713,6 +1683,15 @@ static CURLcode cf_socket_query(struct Curl_cfilter *cf,
     DEBUGASSERT(pres2);
     DEBUGASSERT(pres2);
     *((curl_socket_t *)pres2) = ctx->sock;
     *((curl_socket_t *)pres2) = ctx->sock;
     return CURLE_OK;
     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:
   case CF_QUERY_CONNECT_REPLY_MS:
     if(ctx->got_first_byte) {
     if(ctx->got_first_byte) {
       timediff_t ms = curlx_timediff(ctx->first_byte_at, ctx->started_at);
       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_tcp_connect,
   cf_socket_close,
   cf_socket_close,
   cf_socket_shutdown,
   cf_socket_shutdown,
-  cf_socket_get_host,
   cf_socket_adjust_pollset,
   cf_socket_adjust_pollset,
-  cf_socket_data_pending,
+  Curl_cf_def_data_pending,
   cf_socket_send,
   cf_socket_send,
   cf_socket_recv,
   cf_socket_recv,
   cf_socket_cntrl,
   cf_socket_cntrl,
@@ -1918,9 +1896,8 @@ struct Curl_cftype Curl_cft_udp = {
   cf_udp_connect,
   cf_udp_connect,
   cf_socket_close,
   cf_socket_close,
   cf_socket_shutdown,
   cf_socket_shutdown,
-  cf_socket_get_host,
   cf_socket_adjust_pollset,
   cf_socket_adjust_pollset,
-  cf_socket_data_pending,
+  Curl_cf_def_data_pending,
   cf_socket_send,
   cf_socket_send,
   cf_socket_recv,
   cf_socket_recv,
   cf_socket_cntrl,
   cf_socket_cntrl,
@@ -1973,9 +1950,8 @@ struct Curl_cftype Curl_cft_unix = {
   cf_tcp_connect,
   cf_tcp_connect,
   cf_socket_close,
   cf_socket_close,
   cf_socket_shutdown,
   cf_socket_shutdown,
-  cf_socket_get_host,
   cf_socket_adjust_pollset,
   cf_socket_adjust_pollset,
-  cf_socket_data_pending,
+  Curl_cf_def_data_pending,
   cf_socket_send,
   cf_socket_send,
   cf_socket_recv,
   cf_socket_recv,
   cf_socket_cntrl,
   cf_socket_cntrl,
@@ -2020,7 +1996,7 @@ out:
 }
 }
 
 
 static timediff_t cf_tcp_accept_timeleft(struct Curl_cfilter *cf,
 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;
   struct cf_socket_ctx *ctx = cf->ctx;
   timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
   timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
@@ -2194,9 +2170,8 @@ struct Curl_cftype Curl_cft_tcp_accept = {
   cf_tcp_accept_connect,
   cf_tcp_accept_connect,
   cf_socket_close,
   cf_socket_close,
   cf_socket_shutdown,
   cf_socket_shutdown,
-  cf_socket_get_host,
   cf_socket_adjust_pollset,
   cf_socket_adjust_pollset,
-  cf_socket_data_pending,
+  Curl_cf_def_data_pending,
   cf_socket_send,
   cf_socket_send,
   cf_socket_recv,
   cf_socket_recv,
   cf_socket_cntrl,
   cf_socket_cntrl,
@@ -2222,7 +2197,7 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
     result = CURLE_OUT_OF_MEMORY;
     result = CURLE_OUT_OF_MEMORY;
     goto out;
     goto out;
   }
   }
-  ctx->transport = conn->transport;
+  ctx->transport = TRNSPRT_TCP;
   ctx->sock = *s;
   ctx->sock = *s;
   ctx->listening = TRUE;
   ctx->listening = TRUE;
   ctx->accepted = FALSE;
   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,
 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,
 int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
                       curl_socket_t sock);
                       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,
 static void conn_report_connect_stats(struct Curl_easy *data,
                                       struct connectdata *conn);
                                       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,
 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 */
   /* NOP */
   (void)cf;
   (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;
     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,
                           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,
 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;
   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;
   struct Curl_cfilter *cf;
 
 
   DEBUGASSERT(data);
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
   DEBUGASSERT(data->conn);
-  *code = CURLE_OK;
   cf = data->conn->cfilter[num];
   cf = data->conn->cfilter[num];
-  while(cf && !cf->connected) {
+  while(cf && !cf->connected)
     cf = cf->next;
     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");
   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;
   struct Curl_cfilter *cf;
 
 
   DEBUGASSERT(data);
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
   DEBUGASSERT(data->conn);
-  *code = CURLE_OK;
   cf = data->conn->cfilter[num];
   cf = data->conn->cfilter[num];
-  while(cf && !cf->connected) {
+  while(cf && !cf->connected)
     cf = cf->next;
     cf = cf->next;
-  }
   if(cf) {
   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");
   failf(data, "send: no filter connected");
   DEBUGASSERT(0);
   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,
 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);
     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)
   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)
   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,
 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;
   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)
 bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex)
 {
 {
   struct Curl_cfilter *cf = conn ? conn->cfilter[sockindex] : NULL;
   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;
   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,
 unsigned char Curl_conn_http_version(struct Curl_easy *data,
                                      struct connectdata *conn)
                                      struct connectdata *conn)
 {
 {
@@ -678,31 +737,35 @@ int Curl_conn_cf_poll(struct Curl_cfilter *cf,
   return Curl_poll(pfds, npfds, timeout_ms);
   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);
   DEBUGASSERT(data->conn);
   cf = data->conn->cfilter[sockindex];
   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;
     *phost = data->conn->host.name;
-    *pdisplay_host = data->conn->host.dispname;
     *pport = data->conn->remote_port;
     *pport = data->conn->remote_port;
   }
   }
 }
 }
 
 
 CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf,
 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)cf;
   (void)data;
   (void)data;
@@ -738,6 +801,26 @@ curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf,
   return CURL_SOCKET_BAD;
   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,
 CURLcode Curl_conn_cf_get_ip_info(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
                                   struct Curl_easy *data,
                                   int *is_ipv6, struct ip_quadruple *ipquad)
                                   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;
   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)
 void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex)
 {
 {
   if(data->conn) {
   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,
 size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
-                                     struct connectdata *conn,
-                                     int sockindex)
+                                    struct connectdata *conn,
+                                    int sockindex)
 {
 {
   CURLcode result;
   CURLcode result;
   int n = 0;
   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,
 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);
   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,
 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 *pnwritten)
 {
 {
   size_t write_len = blen;
   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);
   DEBUGASSERT(data->conn);
   DEBUGASSERT(data->conn);
-  conn = data->conn;
+  DEBUGASSERT(sockindex >= 0 && sockindex < 2);
 #ifdef DEBUGBUILD
 #ifdef DEBUGBUILD
   if(write_len) {
   if(write_len) {
     /* Allow debug builds to override this logic to force short sends
     /* 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
 #endif
   if(write_len != blen)
   if(write_len != blen)
     eos = FALSE;
     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,
 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,
 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;
   unsigned int i;
 
 
@@ -1085,3 +1167,16 @@ void Curl_pollset_check(struct Curl_easy *data,
   }
   }
   *pwant_read = *pwant_write = FALSE;
   *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"
 #include "curlx/timediff.h"
 
 
+struct bufq;
 struct Curl_cfilter;
 struct Curl_cfilter;
 struct Curl_easy;
 struct Curl_easy;
 struct Curl_dns_entry;
 struct Curl_dns_entry;
 struct connectdata;
 struct connectdata;
 struct ip_quadruple;
 struct ip_quadruple;
+struct curl_tlssessioninfo;
 
 
 /* Callback to destroy resources held by this filter instance.
 /* Callback to destroy resources held by this filter instance.
  * Implementations MUST NOT chain calls to cf->next.
  * 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,
                                   struct Curl_easy *data,
                                   bool *done);
                                   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;
 struct easy_pollset;
 
 
 /* Passing in an easy_pollset for monitoring of sockets, let
 /* 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,
 typedef bool     Curl_cft_data_pending(struct Curl_cfilter *cf,
                                        const struct Curl_easy *data);
                                        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 */
                                struct Curl_easy *data, /* transfer */
                                const void *buf,        /* data to write */
                                const void *buf,        /* data to write */
                                size_t len,             /* amount to write */
                                size_t len,             /* amount to write */
                                bool eos,               /* last chunk */
                                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 */
                                struct Curl_easy *data, /* transfer */
                                char *buf,              /* store data here */
                                char *buf,              /* store data here */
                                size_t len,             /* amount to read */
                                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,
 typedef bool     Curl_cft_conn_is_alive(struct Curl_cfilter *cf,
                                         struct Curl_easy *data,
                                         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_NEED_FLUSH: TRUE iff any of the filters have unsent data
  * - CF_QUERY_IP_INFO: res1 says if connection used IPv6, res2 is the
  * - CF_QUERY_IP_INFO: res1 says if connection used IPv6, res2 is the
  *                   ip quadruple
  *                   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     */
 /*      query                             res1       res2     */
 #define CF_QUERY_MAX_CONCURRENT     1  /* number     -        */
 #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_NEED_FLUSH         7  /* TRUE/FALSE - */
 #define CF_QUERY_IP_INFO            8  /* TRUE/FALSE struct ip_quadruple */
 #define CF_QUERY_IP_INFO            8  /* TRUE/FALSE struct ip_quadruple */
 #define CF_QUERY_HTTP_VERSION       9  /* number (10/11/20/30)   -  */
 #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
  * 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_connect *do_connect;           /* establish connection */
   Curl_cft_close *do_close;               /* close conn */
   Curl_cft_close *do_close;               /* close conn */
   Curl_cft_shutdown *do_shutdown;         /* shutdown 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_adjust_pollset *adjust_pollset; /* adjust transfer poll set */
   Curl_cft_data_pending *has_data_pending;/* conn has data pending */
   Curl_cft_data_pending *has_data_pending;/* conn has data pending */
   Curl_cft_send *do_send;                 /* send data */
   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
 /* Default implementations for the type functions, implementing pass-through
  * the filter chain. */
  * 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,
 void     Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf,
                                     struct Curl_easy *data,
                                     struct Curl_easy *data,
                                     struct easy_pollset *ps);
                                     struct easy_pollset *ps);
 bool     Curl_cf_def_data_pending(struct Curl_cfilter *cf,
 bool     Curl_cf_def_data_pending(struct Curl_cfilter *cf,
                                   const struct Curl_easy *data);
                                   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,
                           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,
 CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf,
                            struct Curl_easy *data,
                            struct Curl_easy *data,
                            int event, int arg1, void *arg2);
                            int event, int arg1, void *arg2);
@@ -326,11 +321,11 @@ CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
                               struct Curl_easy *data,
                               struct Curl_easy *data,
                               bool *done);
                               bool *done);
 void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data);
 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,
 CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf,
                             struct Curl_easy *data,
                             struct Curl_easy *data,
                             bool ignore_result,
                             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,
 bool Curl_conn_cf_needs_flush(struct Curl_cfilter *cf,
                               struct Curl_easy *data);
                               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_DEFAULT  -1
 #define CURL_CF_SSL_DISABLE  0
 #define CURL_CF_SSL_DISABLE  0
 #define CURL_CF_SSL_ENABLE   1
 #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);
 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`.
  * 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,
 unsigned char Curl_conn_http_version(struct Curl_easy *data,
                                      struct connectdata *conn);
                                      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`.
  * Close the filter chain at `sockindex` for connection `data->conn`.
   * Filters remain in place and may be connected again afterwards.
   * 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);
 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.
  * Tell filters to forget about the socket at sockindex.
  */
  */
 void Curl_conn_forget_socket(struct Curl_easy *data, int 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,
 void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
                                  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
  * Receive data through the filter chain at `sockindex` for connection
  * `data->conn`. Copy at most `len` bytes into `buf`. Return the
  * `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`
  * Send `len` bytes of data from `buf` through the filter chain `sockindex`
  * at connection `data->conn`. Return the actual number of bytes written
  * 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
  * 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
 #ifdef UNITTESTS
 void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data);
 void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data);
 #endif
 #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
  * 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,
 CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex,
                         char *buf, size_t buffersize,
                         char *buf, size_t buffersize,
-                        ssize_t *pnread);
+                        size_t *pnread);
 
 
 /*
 /*
  * Send data on the connection, using FIRSTSOCKET/SECONDARYSOCKET.
  * 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,
                         struct easy_pollset *ps, curl_socket_t sock,
                         bool *pwant_read, bool *pwant_write);
                         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,
  * Types and macros used to keep the current easy handle in filter calls,
  * allowing for nested invocations. See #10336.
  * allowing for nested invocations. See #10336.

+ 0 - 1
lib/conncache.c

@@ -42,7 +42,6 @@
 #include "sigpipe.h"
 #include "sigpipe.h"
 #include "connect.h"
 #include "connect.h"
 #include "select.h"
 #include "select.h"
-#include "strcase.h"
 #include "curlx/strparse.h"
 #include "curlx/strparse.h"
 #include "uint-table.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`.
  * Find a connection in the pool matching `destination`.
  * All callbacks are invoked while the pool's lock is held.
  * All callbacks are invoked while the pool's lock is held.
  * @param data        current transfer
  * @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
  * @param conn_cb     must be present, called for each connection in the
  *                    bundle until it returns TRUE
  *                    bundle until it returns TRUE
  * @return combined result of last conn_db and result_cb or FALSE if no
  * @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 "url.h" /* for Curl_safefree() */
 #include "multiif.h"
 #include "multiif.h"
 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
-#include "inet_ntop.h"
+#include "curlx/inet_ntop.h"
 #include "curlx/inet_pton.h"
 #include "curlx/inet_pton.h"
 #include "vtls/vtls.h" /* for vtsl cfilters */
 #include "vtls/vtls.h" /* for vtsl cfilters */
 #include "progress.h"
 #include "progress.h"
@@ -78,7 +78,6 @@
 #include "vquic/vquic.h" /* for quic cfilters */
 #include "vquic/vquic.h" /* for quic cfilters */
 #include "http_proxy.h"
 #include "http_proxy.h"
 #include "socks.h"
 #include "socks.h"
-#include "strcase.h"
 
 
 /* The last 3 #include files should be in this order */
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_printf.h"
@@ -90,15 +89,15 @@
 enum alpnid Curl_alpn2alpnid(const char *name, size_t len)
 enum alpnid Curl_alpn2alpnid(const char *name, size_t len)
 {
 {
   if(len == 2) {
   if(len == 2) {
-    if(strncasecompare(name, "h1", 2))
+    if(curl_strnequal(name, "h1", 2))
       return ALPN_h1;
       return ALPN_h1;
-    if(strncasecompare(name, "h2", 2))
+    if(curl_strnequal(name, "h2", 2))
       return ALPN_h2;
       return ALPN_h2;
-    if(strncasecompare(name, "h3", 2))
+    if(curl_strnequal(name, "h3", 2))
       return ALPN_h3;
       return ALPN_h3;
   }
   }
   else if(len == 8) {
   else if(len == 8) {
-    if(strncasecompare(name, "http/1.1", 8))
+    if(curl_strnequal(name, "http/1.1", 8))
       return ALPN_h1;
       return ALPN_h1;
   }
   }
   return ALPN_none; /* unknown, probably rubbish input */
   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.start[sockindex] = *nowp;
   data->conn->shutdown.timeout_ms = (timeout_ms > 0) ?
   data->conn->shutdown.timeout_ms = (timeout_ms > 0) ?
-    (unsigned int)timeout_ms :
+    (timediff_t)timeout_ms :
     ((data->set.shutdowntimeout > 0) ?
     ((data->set.shutdowntimeout > 0) ?
      data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS);
      data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS);
   /* Set a timer, unless we operate on the admin handle */
   /* 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,
     Curl_expire_ex(data, nowp, data->conn->shutdown.timeout_ms,
                    EXPIRE_SHUTDOWN);
                    EXPIRE_SHUTDOWN);
 }
 }
@@ -187,7 +186,8 @@ timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
   struct curltime now;
   struct curltime now;
   timediff_t left_ms;
   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 */
     return 0; /* not started or no limits */
 
 
   if(!nowp) {
   if(!nowp) {
@@ -254,8 +254,8 @@ addr_next_match(const struct Curl_addrinfo *addr, int family)
   return NULL;
   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,
 bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
                       char *addr, int *port)
                       char *addr, int *port)
 {
 {
@@ -272,7 +272,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
   switch(sa->sa_family) {
   switch(sa->sa_family) {
     case AF_INET:
     case AF_INET:
       si = (struct sockaddr_in *)(void *) sa;
       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);
         unsigned short us_port = ntohs(si->sin_port);
         *port = us_port;
         *port = us_port;
         return TRUE;
         return TRUE;
@@ -281,7 +281,8 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
 #ifdef USE_IPV6
 #ifdef USE_IPV6
     case AF_INET6:
     case AF_INET6:
       si6 = (struct sockaddr_in6 *)(void *) sa;
       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);
         unsigned short us_port = ntohs(si6->sin6_port);
         *port = us_port;
         *port = us_port;
         return TRUE;
         return TRUE;
@@ -389,7 +390,6 @@ struct eyeballer {
   expire_id timeout_id;              /* ID for Curl_expire() */
   expire_id timeout_id;              /* ID for Curl_expire() */
   CURLcode result;
   CURLcode result;
   int error;
   int error;
-  BIT(rewinded);                     /* if we rewinded the addr list */
   BIT(has_started);                  /* attempts have started */
   BIT(has_started);                  /* attempts have started */
   BIT(is_done);                      /* out of addresses/time */
   BIT(is_done);                      /* out of addresses/time */
   BIT(connected);                    /* cf has connected */
   BIT(connected);                    /* cf has connected */
@@ -455,7 +455,7 @@ static CURLcode eyeballer_new(struct eyeballer **pballer,
 }
 }
 
 
 static void baller_close(struct eyeballer *baller,
 static void baller_close(struct eyeballer *baller,
-                          struct Curl_easy *data)
+                         struct Curl_easy *data)
 {
 {
   if(baller && baller->cf) {
   if(baller && baller->cf) {
     Curl_conn_cf_discard_chain(&baller->cf, data);
     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,
 static void baller_free(struct eyeballer *baller,
-                         struct Curl_easy *data)
+                        struct Curl_easy *data)
 {
 {
   if(baller) {
   if(baller) {
     baller_close(baller, data);
     baller_close(baller, data);
@@ -473,7 +473,6 @@ static void baller_free(struct eyeballer *baller,
 
 
 static void baller_rewind(struct eyeballer *baller)
 static void baller_rewind(struct eyeballer *baller)
 {
 {
-  baller->rewinded = TRUE;
   baller->addr = baller->first;
   baller->addr = baller->first;
   baller->inconclusive = FALSE;
   baller->inconclusive = FALSE;
 }
 }
@@ -682,7 +681,7 @@ evaluate:
         /* next attempt was started */
         /* next attempt was started */
         CURL_TRC_CF(data, cf, "%s trying next", baller->name);
         CURL_TRC_CF(data, cf, "%s trying next", baller->name);
         ++ongoing;
         ++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);
     addr1 = addr_first_match(dns->addr, ai_family1);
     /* no ip address families, probably AF_UNIX or something, use the
     /* no ip address families, probably AF_UNIX or something, use the
      * address family given to us */
      * address family given to us */
-    if(!addr1  && !addr0 && dns->addr) {
+    if(!addr1 && !addr0 && dns->addr) {
       ai_family0 = dns->addr->ai_family;
       ai_family0 = dns->addr->ai_family;
       addr0 = addr_first_match(dns->addr, ai_family0);
       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,
 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;
   struct cf_he_ctx *ctx = cf->ctx;
   size_t i;
   size_t i;
@@ -993,11 +992,11 @@ static CURLcode cf_he_connect(struct Curl_cfilter *cf,
           struct ip_quadruple ipquad;
           struct ip_quadruple ipquad;
           int is_ipv6;
           int is_ipv6;
           if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) {
           if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) {
-            const char *host, *disphost;
+            const char *host;
             int port;
             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",
             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 */
         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,
 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 cf_he_ctx *ctx = cf->ctx;
   struct curltime t, tmax;
   struct curltime t, tmax;
@@ -1134,7 +1133,6 @@ struct Curl_cftype Curl_cft_happy_eyeballs = {
   cf_he_connect,
   cf_he_connect,
   cf_he_close,
   cf_he_close,
   cf_he_shutdown,
   cf_he_shutdown,
-  Curl_cf_def_get_host,
   cf_he_adjust_pollset,
   cf_he_adjust_pollset,
   cf_he_data_pending,
   cf_he_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_send,
@@ -1398,7 +1396,6 @@ struct Curl_cftype Curl_cft_setup = {
   cf_setup_connect,
   cf_setup_connect,
   cf_setup_close,
   cf_setup_close,
   Curl_cf_def_shutdown,
   Curl_cf_def_shutdown,
-  Curl_cf_def_get_host,
   Curl_cf_def_adjust_pollset,
   Curl_cf_def_adjust_pollset,
   Curl_cf_def_data_pending,
   Curl_cf_def_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_send,
@@ -1518,7 +1515,8 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
 
 
   /* Still no cfilter set, apply default. */
   /* Still no cfilter set, apply default. */
   if(!conn->cfilter[sockindex]) {
   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)
     if(result)
       goto out;
       goto out;
   }
   }

+ 12 - 13
lib/content_encoding.c

@@ -52,7 +52,6 @@
 #include "http.h"
 #include "http.h"
 #include "content_encoding.h"
 #include "content_encoding.h"
 #include "strdup.h"
 #include "strdup.h"
-#include "strcase.h"
 
 
 /* The last 3 #include files should be in this order */
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #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,
 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;
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
   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,
 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;
   struct brotli_writer *bp = (struct brotli_writer *) writer;
   (void) data;
   (void) data;
@@ -567,7 +566,7 @@ static CURLcode zstd_do_write(struct Curl_easy *data,
 }
 }
 
 
 static void zstd_do_close(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;
   struct zstd_writer *zp = (struct zstd_writer *) writer;
   (void)data;
   (void)data;
@@ -636,7 +635,7 @@ void Curl_all_content_encodings(char *buf, size_t blen)
 
 
   for(cep = general_unencoders; *cep; cep++) {
   for(cep = general_unencoders; *cep; cep++) {
     ce = *cep;
     ce = *cep;
-    if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT))
+    if(!curl_strequal(ce->name, CONTENT_ENCODING_DEFAULT))
       len += strlen(ce->name) + 2;
       len += strlen(ce->name) + 2;
   }
   }
 
 
@@ -648,7 +647,7 @@ void Curl_all_content_encodings(char *buf, size_t blen)
     char *p = buf;
     char *p = buf;
     for(cep = general_unencoders; *cep; cep++) {
     for(cep = general_unencoders; *cep; cep++) {
       ce = *cep;
       ce = *cep;
-      if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) {
+      if(!curl_strequal(ce->name, CONTENT_ENCODING_DEFAULT)) {
         strcpy(p, ce->name);
         strcpy(p, ce->name);
         p += strlen(p);
         p += strlen(p);
         *p++ = ',';
         *p++ = ',';
@@ -688,7 +687,7 @@ static CURLcode error_do_write(struct Curl_easy *data,
 }
 }
 
 
 static void error_do_close(struct Curl_easy *data,
 static void error_do_close(struct Curl_easy *data,
-                               struct Curl_cwriter *writer)
+                           struct Curl_cwriter *writer)
 {
 {
   (void) data;
   (void) data;
   (void) writer;
   (void) writer;
@@ -713,8 +712,8 @@ static const struct Curl_cwtype *find_unencode_writer(const char *name,
   if(phase == CURL_CW_TRANSFER_DECODE) {
   if(phase == CURL_CW_TRANSFER_DECODE) {
     for(cep = transfer_unencoders; *cep; cep++) {
     for(cep = transfer_unencoders; *cep; cep++) {
       const struct Curl_cwtype *ce = *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]))
                     && !ce->alias[len]))
         return ce;
         return ce;
     }
     }
@@ -722,8 +721,8 @@ static const struct Curl_cwtype *find_unencode_writer(const char *name,
   /* look among the general decoders */
   /* look among the general decoders */
   for(cep = general_unencoders; *cep; cep++) {
   for(cep = general_unencoders; *cep; cep++) {
     const struct Curl_cwtype *ce = *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 ce;
   }
   }
   return NULL;
   return NULL;
@@ -761,12 +760,12 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
       CURL_TRC_WRITE(data, "looking for %s decoder: %.*s",
       CURL_TRC_WRITE(data, "looking for %s decoder: %.*s",
                      is_transfer ? "transfer" : "content", (int)namelen, name);
                      is_transfer ? "transfer" : "content", (int)namelen, name);
       is_chunked = (is_transfer && (namelen == 7) &&
       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.
       /* if we skip the decoding in this phase, do not look further.
        * Exception is "chunked" transfer-encoding which always must happen */
        * Exception is "chunked" transfer-encoding which always must happen */
       if((is_transfer && !data->set.http_transfer_encoding && !is_chunked) ||
       if((is_transfer && !data->set.http_transfer_encoding && !is_chunked) ||
          (!is_transfer && data->set.http_ce_skip)) {
          (!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 */
         /* not requested, ignore */
         CURL_TRC_WRITE(data, "decoder not requested, ignored: %.*s",
         CURL_TRC_WRITE(data, "decoder not requested, ignored: %.*s",
                        (int)namelen, name);
                        (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)
   if(hostname_len < cookie_domain_len)
     return FALSE;
     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;
     return FALSE;
 
 
   /*
   /*
@@ -409,7 +409,7 @@ static void remove_expired(struct CookieInfo *ci)
 /* Make sure domain contains a dot or is localhost. */
 /* Make sure domain contains a dot or is localhost. */
 static bool bad_domain(const char *domain, size_t len)
 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;
     return FALSE;
   else {
   else {
     /* there must be a dot present, but that dot must not be a trailing dot */
     /* 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
        * domain can access the variable. Set TRUE when the cookie says
        * .example.com and to false when the domain is complete www.example.com
        * .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;
       break;
     case 2:
     case 2:
       /* The file format allows the path field to remain not filled in */
       /* The file format allows the path field to remain not filled in */
@@ -842,7 +842,7 @@ parse_netscape(struct Cookie *co,
       FALLTHROUGH();
       FALLTHROUGH();
     case 3:
     case 3:
       co->secure = FALSE;
       co->secure = FALSE;
-      if(strncasecompare(ptr, "TRUE", len)) {
+      if(curl_strnequal(ptr, "TRUE", len)) {
         if(secure || ci->running)
         if(secure || ci->running)
           co->secure = TRUE;
           co->secure = TRUE;
         else
         else
@@ -859,9 +859,9 @@ parse_netscape(struct Cookie *co,
         return CERR_OUT_OF_MEMORY;
         return CERR_OUT_OF_MEMORY;
       else {
       else {
         /* For Netscape file format cookies we check prefix on the name */
         /* 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;
           co->prefix_secure = TRUE;
-        else if(strncasecompare("__Host-", co->name, 7))
+        else if(curl_strnequal("__Host-", co->name, 7))
           co->prefix_host = TRUE;
           co->prefix_host = TRUE;
       }
       }
       break;
       break;
@@ -917,7 +917,7 @@ is_public_suffix(struct Curl_easy *data,
         Curl_psl_release(data);
         Curl_psl_release(data);
       }
       }
       else
       else
-        infof(data, "libpsl problem, rejecting cookie for satety");
+        infof(data, "libpsl problem, rejecting cookie for safety");
     }
     }
 
 
     if(!acceptable) {
     if(!acceptable) {
@@ -954,7 +954,7 @@ replace_existing(struct Curl_easy *data,
       bool matching_domains = FALSE;
       bool matching_domains = FALSE;
 
 
       if(clist->domain && co->domain) {
       if(clist->domain && co->domain) {
-        if(strcasecompare(clist->domain, co->domain))
+        if(curl_strequal(clist->domain, co->domain))
           /* The domains are identical */
           /* The domains are identical */
           matching_domains = TRUE;
           matching_domains = TRUE;
       }
       }
@@ -981,7 +981,7 @@ replace_existing(struct Curl_easy *data,
         else
         else
           cllen = strlen(clist->spath);
           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 "
           infof(data, "cookie '%s' for domain '%s' dropped, would "
                 "overlay an existing cookie", co->name, co->domain);
                 "overlay an existing cookie", co->name, co->domain);
           return CERR_BAD_SECURE;
           return CERR_BAD_SECURE;
@@ -993,7 +993,7 @@ replace_existing(struct Curl_easy *data,
       /* the names are identical */
       /* the names are identical */
 
 
       if(clist->domain && co->domain) {
       if(clist->domain && co->domain) {
-        if(strcasecompare(clist->domain, co->domain) &&
+        if(curl_strequal(clist->domain, co->domain) &&
           (clist->tailmatch == co->tailmatch))
           (clist->tailmatch == co->tailmatch))
           /* The domains are identical */
           /* The domains are identical */
           replace_old = TRUE;
           replace_old = TRUE;
@@ -1005,7 +1005,7 @@ replace_existing(struct Curl_easy *data,
         /* the domains were identical */
         /* the domains were identical */
 
 
         if(clist->spath && co->spath &&
         if(clist->spath && co->spath &&
-           !strcasecompare(clist->spath, co->spath))
+           !curl_strequal(clist->spath, co->spath))
           replace_old = FALSE;
           replace_old = FALSE;
         else if(!clist->spath != !co->spath)
         else if(!clist->spath != !co->spath)
           replace_old = FALSE;
           replace_old = FALSE;
@@ -1337,7 +1337,7 @@ int Curl_cookie_getlist(struct Curl_easy *data,
       if(!co->domain ||
       if(!co->domain ||
          (co->tailmatch && !is_ip &&
          (co->tailmatch && !is_ip &&
           cookie_tailmatch(co->domain, strlen(co->domain), host)) ||
           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
          * the right part of the host matches the domain stuff in the
          * cookie data
          * cookie data

+ 11 - 18
lib/cshutdn.c

@@ -40,7 +40,6 @@
 #include "sigpipe.h"
 #include "sigpipe.h"
 #include "connect.h"
 #include "connect.h"
 #include "select.h"
 #include "select.h"
-#include "strcase.h"
 #include "curlx/strparse.h"
 #include "curlx/strparse.h"
 
 
 /* The last 3 #include files should be in this order */
 /* 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) {
   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) {
     if(conn->handler && conn->handler->disconnect) {
       /* Some disconnect handlers do a blocking wait on server responses.
       /* Some disconnect handlers do a blocking wait on server responses.
        * FTP/IMAP/SMTP and SFTP are among them. When using the internal
        * 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,
 void Curl_cshutdn_run_once(struct Curl_easy *data,
-                      struct connectdata *conn,
-                      bool *done)
+                           struct connectdata *conn,
+                           bool *done)
 {
 {
   DEBUGASSERT(!data->conn);
   DEBUGASSERT(!data->conn);
   Curl_attach_connection(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
 #define NUM_POLLS_ON_STACK 10
 
 
 static CURLcode cshutdn_wait(struct cshutdn *cshutdn,
 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 pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
   struct curl_pollfds cpfds;
   struct curl_pollfds cpfds;
@@ -241,7 +234,7 @@ out:
 
 
 
 
 static void cshutdn_perform(struct cshutdn *cshutdn,
 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 *e = Curl_llist_head(&cshutdn->list);
   struct Curl_llist_node *enext;
   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,
 static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn,
-                                     struct Curl_easy *data,
-                                     struct connectdata *conn)
+                                   struct Curl_easy *data,
+                                   struct connectdata *conn)
 {
 {
   CURLMcode mresult;
   CURLMcode mresult;
 
 
@@ -418,8 +411,8 @@ static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn,
 
 
 
 
 void Curl_cshutdn_add(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;
   struct Curl_easy *data = cshutdn->multi->admin;
   size_t max_total = (cshutdn->multi->max_total_connections > 0) ?
   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,
 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 Curl_llist_node *e;
   struct connectdata *conn;
   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. */
 /* Define to 1 if you have the <sys/select.h> header file. */
 #cmakedefine HAVE_SYS_SELECT_H 1
 #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. */
 /* Define to 1 if you have the <sys/sockio.h> header file. */
 #cmakedefine HAVE_SYS_SOCKIO_H 1
 #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. */
 /* Define to 1 if you have the <sys/types.h> header file. */
 #cmakedefine HAVE_SYS_TYPES_H 1
 #cmakedefine HAVE_SYS_TYPES_H 1
 
 
@@ -685,18 +676,12 @@ ${SIZEOF_TIME_T_CODE}
 /* if GnuTLS is enabled */
 /* if GnuTLS is enabled */
 #cmakedefine USE_GNUTLS 1
 #cmakedefine USE_GNUTLS 1
 
 
-/* if Secure Transport is enabled */
-#cmakedefine USE_SECTRANSP 1
-
 /* if SSL session export support is available */
 /* if SSL session export support is available */
 #cmakedefine USE_SSLS_EXPORT 1
 #cmakedefine USE_SSLS_EXPORT 1
 
 
 /* if mbedTLS is enabled */
 /* if mbedTLS is enabled */
 #cmakedefine USE_MBEDTLS 1
 #cmakedefine USE_MBEDTLS 1
 
 
-/* if BearSSL is enabled */
-#cmakedefine USE_BEARSSL 1
-
 /* if Rustls is enabled */
 /* if Rustls is enabled */
 #cmakedefine USE_RUSTLS 1
 #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. */
 /* Number of bits in a file offset, on hosts where this is settable. */
 #cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS}
 #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 */
 /* the signed version of size_t */
 #cmakedefine ssize_t ${ssize_t}
 #cmakedefine ssize_t ${ssize_t}
 
 

+ 0 - 1
lib/curl_des.c

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

+ 0 - 1
lib/curl_des.h

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

+ 25 - 10
lib/curl_get_line.c

@@ -32,6 +32,15 @@
 /* The last #include file should be: */
 /* The last #include file should be: */
 #include "memdebug.h"
 #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
  * Curl_get_line() makes sure to only return complete whole lines that end
  * newlines.
  * newlines.
@@ -43,9 +52,10 @@ int Curl_get_line(struct dynbuf *buf, FILE *input)
   curlx_dyn_reset(buf);
   curlx_dyn_reset(buf);
   while(1) {
   while(1) {
     char *b = fgets(buffer, sizeof(buffer), input);
     char *b = fgets(buffer, sizeof(buffer), input);
+    size_t rlen;
 
 
     if(b) {
     if(b) {
-      size_t rlen = strlen(b);
+      rlen = strlen(b);
 
 
       if(!rlen)
       if(!rlen)
         break;
         break;
@@ -59,19 +69,24 @@ int Curl_get_line(struct dynbuf *buf, FILE *input)
         /* end of the line */
         /* end of the line */
         return 1; /* all good */
         return 1; /* all good */
 
 
-      else if(feof(input)) {
+      else if(feof(input))
         /* append a newline */
         /* 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 */
         return 1; /* all good */
       }
       }
+      else
+        break;
     }
     }
-    else if(curlx_dyn_len(buf))
-      return 1; /* all good */
-    else
-      break;
   }
   }
   return 0;
   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")
   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;
   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;
     req_flags |= GSS_C_DELEG_POLICY_FLAG;
 #else
 #else
     infof(data, "WARNING: support for CURLGSSAPI_DELEGATION_POLICY_FLAG not "
     infof(data, "WARNING: support for CURLGSSAPI_DELEGATION_POLICY_FLAG not "
-        "compiled in");
+          "compiled in");
 #endif
 #endif
   }
   }
 
 
   if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_FLAG)
   if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_FLAG)
     req_flags |= GSS_C_DELEG_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,
   return gss_init_sec_context(minor_status,
                               GSS_C_NO_CREDENTIAL, /* cred_handle */
                               GSS_C_NO_CREDENTIAL, /* cred_handle */
                               context,
                               context,
@@ -96,6 +356,20 @@ OM_uint32 Curl_gss_init_sec_context(
                               NULL /* time_rec */);
                               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
 #define GSS_LOG_BUFFER_LEN 1024
 static size_t display_gss_error(OM_uint32 status, int type,
 static size_t display_gss_error(OM_uint32 status, int type,
                                 char *buf, size_t len) {
                                 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;
 extern gss_OID_desc Curl_krb5_mech_oid;
 
 
 /* Common method for using GSS-API */
 /* 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 */
 /* Helper to log a GSS-API error status */
 void Curl_gss_log_error(struct Curl_easy *data, const char *prefix,
 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
 #ifdef HEADER_CURL_MEMDEBUG_H
 /* cleanup after memdebug.h */
 /* cleanup after memdebug.h */
 
 
-#ifdef MEMDEBUG_NODEFINES
 #ifdef CURLDEBUG
 #ifdef CURLDEBUG
 
 
 #undef strdup
 #undef strdup
@@ -69,28 +68,28 @@
 #undef recv
 #undef recv
 
 
 #ifdef _WIN32
 #ifdef _WIN32
-#  ifdef UNICODE
-#    undef wcsdup
-#    undef _wcsdup
-#    undef _tcsdup
-#  else
-#    undef _tcsdup
-#  endif
+#undef _tcsdup
 #endif
 #endif
 
 
 #undef socket
 #undef socket
 #undef accept
 #undef accept
+#ifdef HAVE_ACCEPT4
+#undef accept4
+#endif
 #ifdef HAVE_SOCKETPAIR
 #ifdef HAVE_SOCKETPAIR
 #undef socketpair
 #undef socketpair
 #endif
 #endif
 
 
 /* sclose is probably already defined, redefine it! */
 /* sclose is probably already defined, redefine it! */
 #undef sclose
 #undef sclose
+#define sclose(x)  CURL_SCLOSE(x)
 #undef fopen
 #undef fopen
+#ifdef CURL_FOPEN
+#define fopen(fname, mode)  CURL_FOPEN(fname, mode)
+#endif
 #undef fdopen
 #undef fdopen
 #undef fclose
 #undef fclose
 
 
-#endif /* MEMDEBUG_NODEFINES */
 #endif /* CURLDEBUG */
 #endif /* CURLDEBUG */
 
 
 #undef HEADER_CURL_MEMDEBUG_H
 #undef HEADER_CURL_MEMDEBUG_H
@@ -122,9 +121,6 @@ extern curl_free_callback Curl_cfree;
 extern curl_realloc_callback Curl_crealloc;
 extern curl_realloc_callback Curl_crealloc;
 extern curl_strdup_callback Curl_cstrdup;
 extern curl_strdup_callback Curl_cstrdup;
 extern curl_calloc_callback Curl_ccalloc;
 extern curl_calloc_callback Curl_ccalloc;
-#if defined(_WIN32) && defined(UNICODE)
-extern curl_wcsdup_callback Curl_cwcsdup;
-#endif
 
 
 #ifndef CURLDEBUG
 #ifndef CURLDEBUG
 
 
@@ -149,18 +145,13 @@ extern curl_wcsdup_callback Curl_cwcsdup;
 #define free(ptr) Curl_cfree(ptr)
 #define free(ptr) Curl_cfree(ptr)
 
 
 #ifdef _WIN32
 #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
+#endif /* _WIN32 */
 
 
 #endif /* CURLDEBUG */
 #endif /* CURLDEBUG */
 #endif /* HEADER_CURL_MEMORY_H */
 #endif /* HEADER_CURL_MEMORY_H */

+ 7 - 36
lib/curl_ntlm_core.c

@@ -40,9 +40,8 @@
    3. USE_GNUTLS
    3. USE_GNUTLS
    4. -
    4. -
    5. USE_MBEDTLS
    5. USE_MBEDTLS
-   6. USE_SECTRANSP
-   7. USE_OS400CRYPTO
-   8. USE_WIN32_CRYPTO
+   6. USE_OS400CRYPTO
+   7. USE_WIN32_CRYPTO
 
 
    This ensures that:
    This ensures that:
    - the same SSL branch gets activated throughout this source
    - the same SSL branch gets activated throughout this source
@@ -107,11 +106,6 @@
 
 
 #  include <mbedtls/des.h>
 #  include <mbedtls/des.h>
 
 
-#elif defined(USE_SECTRANSP)
-
-#  include <CommonCrypto/CommonCryptor.h>
-#  include <CommonCrypto/CommonDigest.h>
-
 #elif defined(USE_OS400CRYPTO)
 #elif defined(USE_OS400CRYPTO)
 #  include "cipher.mih"  /* mih/cipher */
 #  include "cipher.mih"  /* mih/cipher */
 #elif defined(USE_WIN32_CRYPTO)
 #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;
   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)
 #elif defined(USE_OS400CRYPTO)
 
 
 static bool encrypt_des(const unsigned char *in, unsigned char *out,
 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);
   des_encrypt(&des, 8, results + 8, plaintext);
   setup_des_key(keys + 14, &des);
   setup_des_key(keys + 14, &des);
   des_encrypt(&des, 8, results + 16, plaintext);
   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, keys);
   encrypt_des(plaintext, results + 8, keys + 7);
   encrypt_des(plaintext, results + 8, keys + 7);
   encrypt_des(plaintext, results + 16, keys + 14);
   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);
     des_encrypt(&des, 8, lmbuffer, magic);
     setup_des_key(pw + 7, &des);
     setup_des_key(pw + 7, &des);
     des_encrypt(&des, 8, lmbuffer + 8, magic);
     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, pw);
     encrypt_des(magic, lmbuffer + 8, pw + 7);
     encrypt_des(magic, lmbuffer + 8, pw + 7);
 #endif
 #endif
@@ -466,7 +437,7 @@ struct ms_filetime {
 static void time2filetime(struct ms_filetime *ft, time_t t)
 static void time2filetime(struct ms_filetime *ft, time_t t)
 {
 {
 #if SIZEOF_TIME_T > 4
 #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->dwLowDateTime = (unsigned int) (t & 0xFFFFFFFF);
   ft->dwHighDateTime = (unsigned int) (t >> 32);
   ft->dwHighDateTime = (unsigned int) (t >> 32);
 #else
 #else

+ 4 - 0
lib/curl_ntlm_core.h

@@ -28,6 +28,10 @@
 
 
 #if defined(USE_CURL_NTLM_CORE)
 #if defined(USE_CURL_NTLM_CORE)
 
 
+#include "vauth/vauth.h"
+
+struct ntlmdata;
+
 /* Helpers to generate function byte arguments in little endian order */
 /* Helpers to generate function byte arguments in little endian order */
 #define SHORTPAIR(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff))
 #define SHORTPAIR(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff))
 #define LONGQUARTET(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;
   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;
   struct connectdata *conn = data->conn;
   RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
   RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
+  CURLcode result = CURLE_OK;
   ssize_t nread;
   ssize_t nread;
 
 
   (void)sockindex; /* unused */
   (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));
   nread = RTMP_Read(r, buf, curlx_uztosi(len));
   if(nread < 0) {
   if(nread < 0) {
     if(r->m_read.status == RTMP_READ_COMPLETE ||
     if(r->m_read.status == RTMP_READ_COMPLETE ||
        r->m_read.status == RTMP_READ_EOF) {
        r->m_read.status == RTMP_READ_EOF) {
       data->req.size = data->req.bytecount;
       data->req.size = data->req.bytecount;
-      nread = 0;
     }
     }
     else
     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;
   struct connectdata *conn = data->conn;
   RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
   RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
-  ssize_t num;
+  ssize_t nwritten;
 
 
   (void)sockindex; /* unused */
   (void)sockindex; /* unused */
   (void)eos; /* 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)
 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 }
   { 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()
  * Curl_sasl_decode_mech()
  *
  *
@@ -334,6 +296,241 @@ bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data)
   return FALSE;
   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()
  * 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,
 CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
                          bool force_ir, saslprogress *progress)
                          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->force_ir = force_ir;    /* Latch for future use */
   sasl->authused = 0;           /* No mechanism used yet */
   sasl->authused = 0;           /* No mechanism used yet */
-  enabledmechs = sasl->authmechs & sasl->prefmech;
   *progress = SASL_IDLE;
   *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
   /* Calculate the supported authentication mechanism, by decreasing order of
      security, as well as the initial response where appropriate */
      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 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
 #endif
 #ifdef USE_GSASL
 #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
 #endif
 #ifndef CURL_DISABLE_DIGEST_AUTH
 #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
 #endif
 #ifdef USE_NTLM
 #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
 #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 &&
     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;
       *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;
   struct connectdata *conn = data->conn;
   saslstate newstate = SASL_FINAL;
   saslstate newstate = SASL_FINAL;
   struct bufref resp;
   struct bufref resp;
-  const char *hostname, *disp_hostname;
+  const char *hostname;
   int port;
   int port;
 #if defined(USE_KERBEROS5) || defined(USE_NTLM) \
 #if defined(USE_KERBEROS5) || defined(USE_NTLM) \
     || !defined(CURL_DISABLE_DIGEST_AUTH)
     || !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];
   const char *oauth_bearer = data->set.str[STRING_BEARER];
   struct bufref serverdata;
   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(&serverdata);
   Curl_bufref_init(&resp);
   Curl_bufref_init(&resp);
   *progress = SASL_INPROGRESS;
   *progress = SASL_INPROGRESS;
@@ -587,8 +665,11 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
 #ifdef USE_GSASL
 #ifdef USE_GSASL
   case SASL_GSASL:
   case SASL_GSASL:
     result = get_server_message(sasl, data, &serverdata);
     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)
     if(!result && Curl_bufref_len(&resp) > 0)
       newstate = SASL_GSASL;
       newstate = SASL_GSASL;
     break;
     break;
@@ -615,50 +696,57 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
 #endif
 #endif
 
 
 #ifdef USE_NTLM
 #ifdef USE_NTLM
-  case SASL_NTLM:
+  case SASL_NTLM: {
     /* Create the type-1 message */
     /* 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;
     newstate = SASL_NTLM_TYPE2MSG;
     break;
     break;
-  case SASL_NTLM_TYPE2MSG:
+  }
+  case SASL_NTLM_TYPE2MSG: {
     /* Decode the type-2 message */
     /* 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)
     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)
     if(!result)
       result = Curl_auth_create_ntlm_type3_message(data, conn->user,
       result = Curl_auth_create_ntlm_type3_message(data, conn->user,
-                                                   conn->passwd, &conn->ntlm,
+                                                   conn->passwd, ntlm,
                                                    &resp);
                                                    &resp);
     break;
     break;
+  }
 #endif
 #endif
 
 
 #if defined(USE_KERBEROS5)
 #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,
                                                   sasl->mutual_auth, NULL,
-                                                  &conn->krb5,
-                                                  &resp);
+                                                  krb5, &resp);
     newstate = SASL_GSSAPI_TOKEN;
     newstate = SASL_GSSAPI_TOKEN;
     break;
     break;
+  }
   case SASL_GSSAPI_TOKEN:
   case SASL_GSSAPI_TOKEN:
     result = get_server_message(sasl, data, &serverdata);
     result = get_server_message(sasl, data, &serverdata);
     if(!result) {
     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
         /* Decode the user token challenge and create the optional response
            message */
            message */
         result = Curl_auth_create_gssapi_user_message(data, NULL, NULL,
         result = Curl_auth_create_gssapi_user_message(data, NULL, NULL,
                                                       NULL, NULL,
                                                       NULL, NULL,
                                                       sasl->mutual_auth,
                                                       sasl->mutual_auth,
                                                       &serverdata,
                                                       &serverdata,
-                                                      &conn->krb5,
-                                                      &resp);
+                                                      krb5, &resp);
         newstate = SASL_GSSAPI_NO_DATA;
         newstate = SASL_GSSAPI_NO_DATA;
       }
       }
       else
       else
@@ -666,19 +754,22 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
         result = Curl_auth_create_gssapi_security_message(data,
         result = Curl_auth_create_gssapi_security_message(data,
                                                           conn->sasl_authzid,
                                                           conn->sasl_authzid,
                                                           &serverdata,
                                                           &serverdata,
-                                                          &conn->krb5,
-                                                          &resp);
+                                                          krb5, &resp);
     }
     }
     break;
     break;
   case SASL_GSSAPI_NO_DATA:
   case SASL_GSSAPI_NO_DATA:
     /* Decode the security challenge and create the response message */
     /* Decode the security challenge and create the response message */
     result = get_server_message(sasl, data, &serverdata);
     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;
     break;
 #endif
 #endif
 
 

+ 0 - 4
lib/curl_sasl.h

@@ -135,10 +135,6 @@ struct SASL {
   (wordlen == (sizeof(mech) - 1) / sizeof(char) && \
   (wordlen == (sizeof(mech) - 1) / sizeof(char) && \
    !memcmp(line, mech, wordlen))
    !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 */
 /* Convert a mechanism name to a token */
 unsigned short Curl_sasl_decode_mech(const char *ptr,
 unsigned short Curl_sasl_decode_mech(const char *ptr,
                                      size_t maxlen, size_t *len);
                                      size_t maxlen, size_t *len);

+ 51 - 71
lib/curl_setup.h

@@ -224,46 +224,46 @@
 
 
 #ifdef HTTP_ONLY
 #ifdef HTTP_ONLY
 #  ifndef CURL_DISABLE_DICT
 #  ifndef CURL_DISABLE_DICT
-#    define CURL_DISABLE_DICT
+#  define CURL_DISABLE_DICT
 #  endif
 #  endif
 #  ifndef CURL_DISABLE_FILE
 #  ifndef CURL_DISABLE_FILE
-#    define CURL_DISABLE_FILE
+#  define CURL_DISABLE_FILE
 #  endif
 #  endif
 #  ifndef CURL_DISABLE_FTP
 #  ifndef CURL_DISABLE_FTP
-#    define CURL_DISABLE_FTP
+#  define CURL_DISABLE_FTP
 #  endif
 #  endif
 #  ifndef CURL_DISABLE_GOPHER
 #  ifndef CURL_DISABLE_GOPHER
-#    define CURL_DISABLE_GOPHER
+#  define CURL_DISABLE_GOPHER
 #  endif
 #  endif
 #  ifndef CURL_DISABLE_IMAP
 #  ifndef CURL_DISABLE_IMAP
-#    define CURL_DISABLE_IMAP
+#  define CURL_DISABLE_IMAP
 #  endif
 #  endif
 #  ifndef CURL_DISABLE_LDAP
 #  ifndef CURL_DISABLE_LDAP
-#    define CURL_DISABLE_LDAP
+#  define CURL_DISABLE_LDAP
 #  endif
 #  endif
 #  ifndef CURL_DISABLE_LDAPS
 #  ifndef CURL_DISABLE_LDAPS
-#    define CURL_DISABLE_LDAPS
+#  define CURL_DISABLE_LDAPS
 #  endif
 #  endif
 #  ifndef CURL_DISABLE_MQTT
 #  ifndef CURL_DISABLE_MQTT
-#    define CURL_DISABLE_MQTT
+#  define CURL_DISABLE_MQTT
 #  endif
 #  endif
 #  ifndef CURL_DISABLE_POP3
 #  ifndef CURL_DISABLE_POP3
-#    define CURL_DISABLE_POP3
+#  define CURL_DISABLE_POP3
 #  endif
 #  endif
 #  ifndef CURL_DISABLE_RTSP
 #  ifndef CURL_DISABLE_RTSP
-#    define CURL_DISABLE_RTSP
+#  define CURL_DISABLE_RTSP
 #  endif
 #  endif
 #  ifndef CURL_DISABLE_SMB
 #  ifndef CURL_DISABLE_SMB
-#    define CURL_DISABLE_SMB
+#  define CURL_DISABLE_SMB
 #  endif
 #  endif
 #  ifndef CURL_DISABLE_SMTP
 #  ifndef CURL_DISABLE_SMTP
-#    define CURL_DISABLE_SMTP
+#  define CURL_DISABLE_SMTP
 #  endif
 #  endif
 #  ifndef CURL_DISABLE_TELNET
 #  ifndef CURL_DISABLE_TELNET
-#    define CURL_DISABLE_TELNET
+#  define CURL_DISABLE_TELNET
 #  endif
 #  endif
 #  ifndef CURL_DISABLE_TFTP
 #  ifndef CURL_DISABLE_TFTP
-#    define CURL_DISABLE_TFTP
+#  define CURL_DISABLE_TFTP
 #  endif
 #  endif
 #endif
 #endif
 
 
@@ -470,62 +470,46 @@
 #include <curl/stdcheaders.h>
 #include <curl/stdcheaders.h>
 #endif
 #endif
 
 
-/*
- * Large file (>2Gb) support using Win32 functions.
- */
-
-#ifdef USE_WIN32_LARGE_FILES
+#ifdef _WIN32
 #  ifdef HAVE_IO_H
 #  ifdef HAVE_IO_H
 #  include <io.h>
 #  include <io.h>
 #  endif
 #  endif
 #  include <sys/types.h>
 #  include <sys/types.h>
 #  include <sys/stat.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
 #  endif
-#  include <sys/types.h>
-#  include <sys/stat.h>
 #  ifndef UNDER_CE
 #  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_stat(const char *path, struct_stat *buffer);
      int curlx_win32_open(const char *filename, int oflag, ...);
      int curlx_win32_open(const char *filename, int oflag, ...);
      FILE *curlx_win32_fopen(const char *filename, const char *mode);
      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
 #  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
 #endif
 
 
 #ifndef struct_stat
 #ifndef struct_stat
@@ -582,7 +566,7 @@
 #    endif
 #    endif
 #  endif
 #  endif
 #  ifndef SIZEOF_OFF_T
 #  ifndef SIZEOF_OFF_T
-#    define SIZEOF_OFF_T 4
+#  define SIZEOF_OFF_T 4
 #  endif
 #  endif
 #endif
 #endif
 
 
@@ -590,9 +574,9 @@
 #error "too small curl_off_t"
 #error "too small curl_off_t"
 #else
 #else
    /* assume SIZEOF_CURL_OFF_T == 8 */
    /* assume SIZEOF_CURL_OFF_T == 8 */
-#  define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF)
+#  define CURL_OFF_T_MAX 0x7FFFFFFFFFFFFFFF
 #endif
 #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)
 #if (SIZEOF_CURL_OFF_T != 8)
 #  error "curl_off_t must be exactly 64 bits"
 #  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 select(n,r,w,x,t) select_s(n,r,w,x,t)
 #    define ioctl(x,y,z) ioctlsocket(x,y,(char *)(z))
 #    define ioctl(x,y,z) ioctlsocket(x,y,(char *)(z))
 #    include <tcp.h>
 #    include <tcp.h>
-#    ifdef word
-#      undef word
-#    endif
-#    ifdef byte
-#      undef byte
-#    endif
+#    undef word
+#    undef byte
 
 
 #  endif /* MSDOS */
 #  endif /* MSDOS */
 
 
@@ -739,8 +719,8 @@
 #endif
 #endif
 
 
 #if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \
 #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 */
 #define USE_SSL    /* SSL support has been enabled */
 #endif
 #endif
 
 
@@ -775,7 +755,7 @@
 /* Single point where USE_NTLM definition might be defined */
 /* Single point where USE_NTLM definition might be defined */
 #ifndef CURL_DISABLE_NTLM
 #ifndef CURL_DISABLE_NTLM
 #  if defined(USE_OPENSSL) || defined(USE_MBEDTLS) ||                   \
 #  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_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) ||              \
   (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT))
   (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT))
 #    define USE_CURL_NTLM_CORE
 #    define USE_CURL_NTLM_CORE

+ 9 - 9
lib/curl_setup_once.h

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

+ 0 - 2
lib/curl_sha512_256.c

@@ -34,9 +34,7 @@
  * * GnuTLS
  * * GnuTLS
  * * wolfSSL
  * * wolfSSL
  * * Schannel SSPI
  * * Schannel SSPI
- * * Secure Transport (Darwin)
  * * mbedTLS
  * * mbedTLS
- * * BearSSL
  * * Rustls
  * * Rustls
  * Skip the backend if it does not support the required algorithm */
  * 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/curl.h>
 #include "curl_sspi.h"
 #include "curl_sspi.h"
+#include "strdup.h"
 #include "curlx/multibyte.h"
 #include "curlx/multibyte.h"
 #include "system_win32.h"
 #include "system_win32.h"
 #include "curlx/version_win32.h"
 #include "curlx/version_win32.h"
@@ -37,23 +38,6 @@
 #include "curl_memory.h"
 #include "curl_memory.h"
 #include "memdebug.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 */
 /* Pointer to SSPI dispatch table */
 PSecurityFunctionTable Curl_pSecFn = NULL;
 PSecurityFunctionTable Curl_pSecFn = NULL;
 
 
@@ -76,31 +60,14 @@ PSecurityFunctionTable Curl_pSecFn = NULL;
  */
  */
 CURLcode Curl_sspi_global_init(void)
 CURLcode Curl_sspi_global_init(void)
 {
 {
-  INITSECURITYINTERFACE_FN pInitSecurityInterface;
-
   /* If security interface is not yet initialized try to do this */
   /* 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 */
     /* 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)
     if(!Curl_pSecFn)
       return CURLE_FAILED_INIT;
       return CURLE_FAILED_INIT;
   }
   }
@@ -119,9 +86,7 @@ CURLcode Curl_sspi_global_init(void)
  */
  */
 void Curl_sspi_global_cleanup(void)
 void Curl_sspi_global_cleanup(void)
 {
 {
-  if(Curl_hSecDll) {
-    FreeLibrary(Curl_hSecDll);
-    Curl_hSecDll = NULL;
+  if(Curl_pSecFn) {
     Curl_pSecFn = NULL;
     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);
 void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity);
 
 
 /* Forward-declaration of global variables defined in curl_sspi.c */
 /* Forward-declaration of global variables defined in curl_sspi.c */
-extern HMODULE Curl_hSecDll;
 extern PSecurityFunctionTable Curl_pSecFn;
 extern PSecurityFunctionTable Curl_pSecFn;
 
 
 /* Provide some definitions missing in old headers */
 /* Provide some definitions missing in old headers */

+ 1 - 2
lib/curl_trc.c

@@ -31,7 +31,6 @@
 #include "easyif.h"
 #include "easyif.h"
 #include "cfilters.h"
 #include "cfilters.h"
 #include "multiif.h"
 #include "multiif.h"
-#include "strcase.h"
 
 
 #include "cf-socket.h"
 #include "cf-socket.h"
 #include "connect.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,
 static void trc_infof(struct Curl_easy *data,
                       struct curl_trc_feat *feat,
                       struct curl_trc_feat *feat,
                       const char *opt_id, int opt_id_idx,
                       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,
 static void trc_infof(struct Curl_easy *data,
                       struct curl_trc_feat *feat,
                       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                     ___| | | |  _ \| |
  *  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
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * you should have received as part of this distribution. The terms
@@ -23,12 +23,17 @@
  * SPDX-License-Identifier: curl
  * SPDX-License-Identifier: curl
  *
  *
  ***************************************************************************/
  ***************************************************************************/
-
 #include "../curl_setup.h"
 #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.
  * be.
  */
  */
 
 
+#include "binmode.h"
+/* "binmode.h" provides macro CURLX_SET_BINMODE() */
+
 #include "nonblock.h"
 #include "nonblock.h"
 /* "nonblock.h" provides curlx_nonblock() */
 /* "nonblock.h" provides curlx_nonblock() */
 
 
@@ -65,10 +68,16 @@
 #include "timeval.h"
 #include "timeval.h"
 #include "timediff.h"
 #include "timediff.h"
 
 
+#include "wait.h"
+/* for curlx_wait_ms */
+
 #include "winapi.h"
 #include "winapi.h"
 /* for curlx_winapi_strerror */
 /* for curlx_winapi_strerror */
 
 
 #include "inet_pton.h"
 #include "inet_pton.h"
 /* for curlx_inet_pton */
 /* for curlx_inet_pton */
 
 
+#include "inet_ntop.h"
+/* for curlx_inet_ntop */
+
 #endif /* HEADER_CURL_CURLX_H */
 #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.
  * Original code by Paul Vixie. "curlified" by Gisle Vanem.
  */
  */
 
 
-#include "curl_setup.h"
+#include "../curl_setup.h"
 
 
 #ifndef HAVE_INET_NTOP
 #ifndef HAVE_INET_NTOP
 
 
@@ -35,7 +35,6 @@
 #endif
 #endif
 
 
 #include "inet_ntop.h"
 #include "inet_ntop.h"
-#include "curl_printf.h"
 
 
 #define IN6ADDRSZ       16
 #define IN6ADDRSZ       16
 /* #define INADDRSZ         4 */
 /* #define INADDRSZ         4 */
@@ -65,8 +64,9 @@ static char *inet_ntop4(const unsigned char *src, char *dst, size_t size)
 
 
   DEBUGASSERT(size >= 16);
   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[0])) & 0xff,
                   ((int)((unsigned char)src[1])) & 0xff,
                   ((int)((unsigned char)src[1])) & 0xff,
                   ((int)((unsigned char)src[2])) & 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);
       tp += strlen(tp);
       break;
       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?
   /* 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
  * code. This is to avoid losing the actual last Winsock error. When this
  * function returns NULL, check errno not SOCKERRNO.
  * 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) {
   switch(af) {
   case AF_INET:
   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_INET_NTOP
 #ifdef HAVE_NETINET_IN_H
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #include <netinet/in.h>
 #endif
 #endif
-#ifdef HAVE_SYS_SOCKET_H
+#ifndef _WIN32
 #include <sys/socket.h>
 #include <sys/socket.h>
 #endif
 #endif
 #ifdef HAVE_ARPA_INET_H
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #include <arpa/inet.h>
 #endif
 #endif
 #ifdef __AMIGA__
 #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,       \
   (char *)inet_ntop(af, CURL_UNCONST(addr), (unsigned char *)buf,       \
                     (curl_socklen_t)(size))
                     (curl_socklen_t)(size))
 #else
 #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))
   inet_ntop(af, addr, buf, (curl_socklen_t)(size))
 #endif
 #endif
 #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
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #include <netinet/in.h>
 #endif
 #endif
-#ifdef HAVE_SYS_SOCKET_H
+#ifndef _WIN32
 #include <sys/socket.h>
 #include <sys/socket.h>
 #endif
 #endif
 #ifdef HAVE_ARPA_INET_H
 #ifdef HAVE_ARPA_INET_H

+ 9 - 6
lib/curlx/strparse.c

@@ -23,7 +23,10 @@
  ***************************************************************************/
  ***************************************************************************/
 
 
 #include "strparse.h"
 #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)
 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.
 /* Get a word until the first DELIM or end of string. At least one byte long.
    return non-zero on error */
    return non-zero on error */
 int curlx_str_until(const char **linep, struct Curl_str *out,
 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;
   const char *s = *linep;
   size_t len = 0;
   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.
 /* Get a word until the first space or end of string. At least one byte long.
    return non-zero on error */
    return non-zero on error */
 int curlx_str_word(const char **linep, struct Curl_str *out,
 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, ' ');
   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.
 /* Get a word until a newline byte or end of string. At least one byte long.
    return non-zero on error */
    return non-zero on error */
 int curlx_str_untilnl(const char **linep, struct Curl_str *out,
 int curlx_str_untilnl(const char **linep, struct Curl_str *out,
-                     const size_t max)
+                      const size_t max)
 {
 {
   const char *s = *linep;
   const char *s = *linep;
   size_t len = 0;
   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.
 /* Get a "quoted" word. No escaping possible.
    return non-zero on error */
    return non-zero on error */
 int curlx_str_quotedword(const char **linep, struct Curl_str *out,
 int curlx_str_quotedword(const char **linep, struct Curl_str *out,
-                        const size_t max)
+                         const size_t max)
 {
 {
   const char *s = *linep;
   const char *s = *linep;
   size_t len = 0;
   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)
 int curlx_str_casecompare(struct Curl_str *str, const char *check)
 {
 {
   size_t clen = check ? strlen(check) : 0;
   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
 #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
 /* Get a word until the first DELIM or end of string
    return non-zero on error */
    return non-zero on error */
 int curlx_str_until(const char **linep, struct Curl_str *out, const size_t max,
 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.
 /* Get a word until a newline byte or end of string. At least one byte long.
    return non-zero on error */
    return non-zero on error */
 int curlx_str_untilnl(const char **linep, struct Curl_str *out,
 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.
 /* Get a "quoted" word. No escaping possible.
    return non-zero on error */
    return non-zero on error */
 int curlx_str_quotedword(const char **linep, struct Curl_str *out,
 int curlx_str_quotedword(const char **linep, struct Curl_str *out,
-                        const size_t max);
+                         const size_t max);
 
 
 /* Advance over a single character.
 /* Advance over a single character.
    return non-zero on error */
    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                     ___| | | |  _ \| |
  *  Project                     ___| | | |  _ \| |
@@ -7,7 +7,6 @@
  *                            | (__| |_| |  _ <| |___
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *                             \___|\___/|_| \_\_____|
  *
  *
- * Copyright (C) Nick Zitzmann, <[email protected]>.
  * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
  * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
  *
  *
  * This software is licensed as described in the file COPYING, which
  * This software is licensed as described in the file COPYING, which
@@ -24,11 +23,9 @@
  * SPDX-License-Identifier: curl
  * 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__ */
 #endif /* __INTEL_COMPILER && __unix__ */
 
 
-#ifdef _WIN32
-#undef read
-#undef write
-#endif
-
 #include <limits.h>
 #include <limits.h>
 
 
 #define CURL_MASK_UCHAR   ((unsigned char)~0)
 #define CURL_MASK_UCHAR   ((unsigned char)~0)
@@ -295,21 +290,3 @@ size_t curlx_sitouz(int sinum)
 #  pragma warning(pop)
 #  pragma warning(pop)
 #endif
 #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);
 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
 #ifdef _WIN32
 #undef  read
 #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
 #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
 
 
-#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 "headers.h"
 #include "multiif.h"
 #include "multiif.h"
 #include "sendf.h"
 #include "sendf.h"
+#include "transfer.h"
 #include "cw-out.h"
 #include "cw-out.h"
 #include "cw-pause.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");
         failf(data, "Write callback asked for PAUSE when not supported");
         return CURLE_WRITE_ERROR;
         return CURLE_WRITE_ERROR;
       }
       }
-      /* mark the connection as RECV paused */
-      data->req.keepon |= KEEP_RECV_PAUSE;
       ctx->paused = TRUE;
       ctx->paused = TRUE;
       CURL_TRC_WRITE(data, "[OUT] PAUSE requested by client");
       CURL_TRC_WRITE(data, "[OUT] PAUSE requested by client");
-      break;
+      return Curl_xfer_pause_recv(data, TRUE);
     }
     }
     else if(CURL_WRITEFUNC_ERROR == nwritten) {
     else if(CURL_WRITEFUNC_ERROR == nwritten) {
       failf(data, "client returned ERROR on write of %zu bytes", wlen);
       failf(data, "client returned ERROR on write of %zu bytes", wlen);

+ 6 - 7
lib/dict.c

@@ -60,7 +60,6 @@
 #include "progress.h"
 #include "progress.h"
 #include "dict.h"
 #include "dict.h"
 #include "curl_printf.h"
 #include "curl_printf.h"
-#include "strcase.h"
 #include "curl_memory.h"
 #include "curl_memory.h"
 /* The last #include file should be: */
 /* The last #include file should be: */
 #include "memdebug.h"
 #include "memdebug.h"
@@ -198,9 +197,9 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
   if(result)
   if(result)
     return 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, ':');
     word = strchr(path, ':');
     if(word) {
     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 */
     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, ':');
     word = strchr(path, ':');
     if(word) {
     if(word) {

+ 2 - 4
lib/doh.c

@@ -257,7 +257,7 @@ static void doh_probe_done(struct Curl_easy *data,
 
 
     if(!dohp->pending) {
     if(!dohp->pending) {
       /* DoH completed, run the transfer picking up the results */
       /* 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,
      options should be added to check doh proxy insecure separately,
      CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER.
      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]) {
   if(data->set.str[STRING_SSL_CAFILE]) {
     ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
     ERROR_CHECK_SETOPT(CURLOPT_CAINFO,
                        data->set.str[STRING_SSL_CAFILE]);
                        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,
 static void doh_store_aaaa(const unsigned char *doh, int index,
-                              struct dohentry *d)
+                           struct dohentry *d)
 {
 {
   /* silently ignore addresses over the limit */
   /* silently ignore addresses over the limit */
   if(d->numaddr < DOH_MAX_ADDR) {
   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;
   size_t i;
   for(i = 0; i < dynhds->hds_len; ++i) {
   for(i = 0; i < dynhds->hds_len; ++i) {
     if(dynhds->hds[i]->namelen == namelen &&
     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];
       return dynhds->hds[i];
     }
     }
   }
   }
@@ -297,7 +297,7 @@ size_t Curl_dynhds_count_name(struct dynhds *dynhds,
     size_t i;
     size_t i;
     for(i = 0; i < dynhds->hds_len; ++i) {
     for(i = 0; i < dynhds->hds_len; ++i) {
       if((namelen == dynhds->hds[i]->namelen) &&
       if((namelen == dynhds->hds[i]->namelen) &&
-         strncasecompare(name, dynhds->hds[i]->name, namelen))
+         curl_strnequal(name, dynhds->hds[i]->name, namelen))
         ++n;
         ++n;
     }
     }
   }
   }
@@ -325,7 +325,7 @@ size_t Curl_dynhds_remove(struct dynhds *dynhds,
     size_t i, len;
     size_t i, len;
     for(i = 0; i < dynhds->hds_len; ++i) {
     for(i = 0; i < dynhds->hds_len; ++i) {
       if((namelen == dynhds->hds[i]->namelen) &&
       if((namelen == dynhds->hds[i]->namelen) &&
-         strncasecompare(name, dynhds->hds[i]->name, namelen)) {
+         curl_strnequal(name, dynhds->hds[i]->name, namelen)) {
         ++n;
         ++n;
         --dynhds->hds_len;
         --dynhds->hds_len;
         dynhds->strs_len -= (dynhds->hds[i]->namelen +
         dynhds->strs_len -= (dynhds->hds[i]->namelen +

+ 27 - 65
lib/easy.c

@@ -67,6 +67,7 @@
 #include "amigaos.h"
 #include "amigaos.h"
 #include "macos.h"
 #include "macos.h"
 #include "curlx/warnless.h"
 #include "curlx/warnless.h"
+#include "curlx/wait.h"
 #include "sigpipe.h"
 #include "sigpipe.h"
 #include "vssh/ssh.h"
 #include "vssh/ssh.h"
 #include "setopt.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_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc;
 curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup;
 curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup;
 curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
 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)
 #if defined(_MSC_VER) && defined(_DLL)
 #  pragma warning(pop)
 #  pragma warning(pop)
@@ -156,9 +154,6 @@ static CURLcode global_init(long flags, bool memoryfuncs)
     Curl_crealloc = (curl_realloc_callback)realloc;
     Curl_crealloc = (curl_realloc_callback)realloc;
     Curl_cstrdup = (curl_strdup_callback)system_strdup;
     Curl_cstrdup = (curl_strdup_callback)system_strdup;
     Curl_ccalloc = (curl_calloc_callback)calloc;
     Curl_ccalloc = (curl_calloc_callback)calloc;
-#if defined(_WIN32) && defined(UNICODE)
-    Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup;
-#endif
   }
   }
 
 
   if(Curl_trc_init()) {
   if(Curl_trc_init()) {
@@ -611,7 +606,7 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
 #endif
 #endif
       pollrc = 0;
       pollrc = 0;
       if(ev->ms > 0)
       if(ev->ms > 0)
-        Curl_wait_ms(ev->ms);
+        curlx_wait_ms(ev->ms);
     }
     }
 
 
     ev->msbump = FALSE; /* reset here */
     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.
  * transfer as previously setup.
  *
  *
  * CONCEPT: This function creates a multi handle, adds the easy handle to it,
  * 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)
 CURLcode curl_easy_pause(CURL *d, int action)
 {
 {
-  struct SingleRequest *k;
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
-  int oldstate;
-  int newstate;
   bool recursive = FALSE;
   bool recursive = FALSE;
-  bool keep_changed, unpause_read, not_all_paused;
+  bool changed = FALSE;
   struct Curl_easy *data = d;
   struct Curl_easy *data = d;
+  bool recv_paused, recv_paused_new;
+  bool send_paused, send_paused_new;
 
 
   if(!GOOD_EASY_HANDLE(data) || !data->conn)
   if(!GOOD_EASY_HANDLE(data) || !data->conn)
     /* crazy input, do not continue */
     /* crazy input, do not continue */
@@ -1142,62 +1136,37 @@ CURLcode curl_easy_pause(CURL *d, int action)
 
 
   if(Curl_is_in_callback(data))
   if(Curl_is_in_callback(data))
     recursive = TRUE;
     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 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);
     Curl_expire(data, 0, EXPIRE_RUN_NOW);
     /* reset the too-slow time keeper */
     /* reset the too-slow time keeper */
     data->state.keeps_speed.tv_sec = 0;
     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. */
     /* 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;
         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 */
     /* 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;
       result = CURLE_ABORTED_BY_CALLBACK;
 
 
   if(recursive)
   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 curl_easy_recv(CURL *d, void *buffer, size_t buflen, size_t *n)
 {
 {
   CURLcode result;
   CURLcode result;
-  ssize_t n1;
   struct connectdata *c;
   struct connectdata *c;
   struct Curl_easy *data = d;
   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);
     Curl_attach_connection(data, c);
 
 
   *n = 0;
   *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
 #ifndef CURL_DISABLE_WEBSOCKETS

+ 3 - 4
lib/easygetopt.c

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

+ 7 - 9
lib/escape.c

@@ -85,7 +85,7 @@ char *curl_easy_escape(CURL *data, const char *string,
     else {
     else {
       /* encode it */
       /* encode it */
       unsigned char out[3]={'%'};
       unsigned char out[3]={'%'};
-      Curl_hexbyte(&out[1], in, FALSE);
+      Curl_hexbyte(&out[1], in);
       if(curlx_dyn_addn(&d, out, 3))
       if(curlx_dyn_addn(&d, out, 3))
         return NULL;
         return NULL;
     }
     }
@@ -212,7 +212,8 @@ void Curl_hexencode(const unsigned char *src, size_t len, /* input length */
   DEBUGASSERT(src && len && (olen >= 3));
   DEBUGASSERT(src && len && (olen >= 3));
   if(src && len && (olen >= 3)) {
   if(src && len && (olen >= 3)) {
     while(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;
       ++src;
       out += 2;
       out += 2;
       olen -= 2;
       olen -= 2;
@@ -225,14 +226,11 @@ void Curl_hexencode(const unsigned char *src, size_t len, /* input length */
 
 
 /* Curl_hexbyte
 /* 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 */
 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 */
                     unsigned char *out, size_t olen); /* output buffer size */
 
 
 void Curl_hexbyte(unsigned char *dest, /* must fit two bytes */
 void Curl_hexbyte(unsigned char *dest, /* must fit two bytes */
-                  unsigned char val,
-                  bool lowercase);
+                  unsigned char val);
 
 
 #endif /* HEADER_CURL_ESCAPE_H */
 #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 "urldata.h" /* for struct Curl_easy */
 #include "mime.h"
 #include "mime.h"
 #include "vtls/vtls.h"
 #include "vtls/vtls.h"
-#include "strcase.h"
 #include "sendf.h"
 #include "sendf.h"
 #include "strdup.h"
 #include "strdup.h"
 #include "rand.h"
 #include "rand.h"

+ 53 - 15
lib/ftp.c

@@ -60,7 +60,7 @@
 #include "cf-socket.h"
 #include "cf-socket.h"
 #include "connect.h"
 #include "connect.h"
 #include "strerror.h"
 #include "strerror.h"
-#include "inet_ntop.h"
+#include "curlx/inet_ntop.h"
 #include "curlx/inet_pton.h"
 #include "curlx/inet_pton.h"
 #include "select.h"
 #include "select.h"
 #include "parsedate.h" /* for the week day and month names */
 #include "parsedate.h" /* for the week day and month names */
@@ -114,12 +114,14 @@ static const char * const ftp_state_names[]={
   "QUOTE",
   "QUOTE",
   "RETR_PREQUOTE",
   "RETR_PREQUOTE",
   "STOR_PREQUOTE",
   "STOR_PREQUOTE",
+  "LIST_PREQUOTE",
   "POSTQUOTE",
   "POSTQUOTE",
   "CWD",
   "CWD",
   "MKD",
   "MKD",
   "MDTM",
   "MDTM",
   "TYPE",
   "TYPE",
   "LIST_TYPE",
   "LIST_TYPE",
+  "RETR_LIST_TYPE",
   "RETR_TYPE",
   "RETR_TYPE",
   "STOR_TYPE",
   "STOR_TYPE",
   "SIZE",
   "SIZE",
@@ -765,7 +767,12 @@ static CURLcode ftp_state_user(struct Curl_easy *data,
 static CURLcode ftp_state_pwd(struct Curl_easy *data,
 static CURLcode ftp_state_pwd(struct Curl_easy *data,
                               struct ftp_conn *ftpc)
                               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)
   if(!result)
     ftp_state(data, ftpc, FTP_PWD);
     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;
       port_min = port_max = 0;
 
 
     if(addrlen) {
     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);
       DEBUGASSERT(addr);
       if(addrlen >= sizeof(ipstr))
       if(addrlen >= sizeof(ipstr))
         goto out;
         goto out;
@@ -985,9 +998,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
       ipstr[addrlen] = 0;
       ipstr[addrlen] = 0;
 
 
       /* attempt to get the address of the given interface name */
       /* 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
 #ifdef USE_IPV6
-                        Curl_ipv6_scope(&conn->remote_addr->curl_sa_addr),
+                        Curl_ipv6_scope(&remote_addr->curl_sa_addr),
                         conn->scope_id,
                         conn->scope_id,
 #endif
 #endif
                         ipstr, hbuf, sizeof(hbuf))) {
                         ipstr, hbuf, sizeof(hbuf))) {
@@ -1020,11 +1033,11 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
     switch(sa->sa_family) {
     switch(sa->sa_family) {
 #ifdef USE_IPV6
 #ifdef USE_IPV6
     case AF_INET6:
     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;
       break;
 #endif
 #endif
     default:
     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;
       break;
     }
     }
     if(!r) {
     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 */
   /* step 2, create a socket for the requested address */
   error = 0;
   error = 0;
   for(ai = res; ai; ai = ai->ai_next) {
   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;
       error = SOCKERRNO;
       continue;
       continue;
     }
     }
@@ -1245,7 +1259,7 @@ out:
     }
     }
     data->conn->bits.do_more = FALSE;
     data->conn->bits.do_more = FALSE;
     Curl_pgrsTime(data, TIMER_STARTACCEPT);
     Curl_pgrsTime(data, TIMER_STARTACCEPT);
-    Curl_expire(data, data->set.accepttimeout ?
+    Curl_expire(data, (data->set.accepttimeout > 0) ?
                 data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT,
                 data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT,
                 EXPIRE_FTP_ACCEPT);
                 EXPIRE_FTP_ACCEPT);
   }
   }
@@ -1448,6 +1462,14 @@ static CURLcode ftp_state_list(struct Curl_easy *data,
   return result;
   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,
 static CURLcode ftp_state_retr_prequote(struct Curl_easy *data,
                                         struct ftp_conn *ftpc,
                                         struct ftp_conn *ftpc,
                                         struct FTP *ftp)
                                         struct FTP *ftp)
@@ -1636,6 +1658,7 @@ static CURLcode ftp_state_quote(struct Curl_easy *data,
     break;
     break;
   case FTP_RETR_PREQUOTE:
   case FTP_RETR_PREQUOTE:
   case FTP_STOR_PREQUOTE:
   case FTP_STOR_PREQUOTE:
+  case FTP_LIST_PREQUOTE:
     item = data->set.prequote;
     item = data->set.prequote;
     break;
     break;
   case FTP_POSTQUOTE:
   case FTP_POSTQUOTE:
@@ -1725,6 +1748,10 @@ static CURLcode ftp_state_quote(struct Curl_easy *data,
       break;
       break;
     case FTP_POSTQUOTE:
     case FTP_POSTQUOTE:
       break;
       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);
     result = ftp_state_retr_prequote(data, ftpc, ftp);
   else if(instate == FTP_STOR_TYPE)
   else if(instate == FTP_STOR_TYPE)
     result = ftp_state_stor_prequote(data, ftpc, ftp);
     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;
   return result;
 }
 }
@@ -2961,7 +2990,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
           return CURLE_OUT_OF_MEMORY;
           return CURLE_OUT_OF_MEMORY;
 
 
         /* Check for special servers here. */
         /* Check for special servers here. */
-        if(strcasecompare(os, "OS/400")) {
+        if(curl_strequal(os, "OS/400")) {
           /* Force OS400 name format 1. */
           /* Force OS400 name format 1. */
           result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1");
           result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1");
           if(result) {
           if(result) {
@@ -3002,6 +3031,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
     case FTP_POSTQUOTE:
     case FTP_POSTQUOTE:
     case FTP_RETR_PREQUOTE:
     case FTP_RETR_PREQUOTE:
     case FTP_STOR_PREQUOTE:
     case FTP_STOR_PREQUOTE:
+    case FTP_LIST_PREQUOTE:
       if((ftpcode >= 400) && !ftpc->count2) {
       if((ftpcode >= 400) && !ftpc->count2) {
         /* failure response code, and not allowed to fail */
         /* failure response code, and not allowed to fail */
         failf(data, "QUOT command failed with %03d", ftpcode);
         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_LIST_TYPE:
     case FTP_RETR_TYPE:
     case FTP_RETR_TYPE:
     case FTP_STOR_TYPE:
     case FTP_STOR_TYPE:
+    case FTP_RETR_LIST_TYPE:
       result = ftp_state_type_resp(data, ftpc, ftp, ftpcode, ftpc->state);
       result = ftp_state_type_resp(data, ftpc, ftp, ftpcode, ftpc->state);
       break;
       break;
 
 
@@ -3125,8 +3156,8 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
 
 
 /* called repeatedly until done from multi.c */
 /* called repeatedly until done from multi.c */
 static CURLcode ftp_statemach(struct Curl_easy *data,
 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);
   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)
       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
         /* The specified path ends with a slash, and therefore we think this
            is a directory that is requested, use LIST. But before that we
            is a directory that is requested, use LIST. But before that we
            need to set ASCII transfer mode. */
            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 */
         /* otherwise just fall through */
       }
       }
       else {
       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)
         if(result)
           return 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.
      will try to send the QUIT command, otherwise it will just return.
   */
   */
   ftpc->shutdown = TRUE;
   ftpc->shutdown = TRUE;
-  if(dead_connection)
+  if(dead_connection || Curl_pp_needs_flush(data, &ftpc->pp))
     ftpc->ctl_valid = FALSE;
     ftpc->ctl_valid = FALSE;
 
 
   /* The FTP session may or may not have been allocated/setup at this point! */
   /* 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_QUOTE, /* waiting for a response to a command sent in a quote list */
   FTP_RETR_PREQUOTE,
   FTP_RETR_PREQUOTE,
   FTP_STOR_PREQUOTE,
   FTP_STOR_PREQUOTE,
+  FTP_LIST_PREQUOTE,
   FTP_POSTQUOTE,
   FTP_POSTQUOTE,
   FTP_CWD,  /* change dir */
   FTP_CWD,  /* change dir */
   FTP_MKD,  /* if the dir did not exist */
   FTP_MKD,  /* if the dir did not exist */
   FTP_MDTM, /* to figure out the datestamp */
   FTP_MDTM, /* to figure out the datestamp */
   FTP_TYPE, /* to set type when doing a head-like request */
   FTP_TYPE, /* to set type when doing a head-like request */
   FTP_LIST_TYPE, /* set type when about to do a dir list */
   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_RETR_TYPE, /* set type when about to RETR a file */
   FTP_STOR_TYPE, /* set type when about to STOR 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 */
   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;
   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);
   size_t len = curlx_dyn_len(&infop->buf);
   char *mem = curlx_dyn_ptr(&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++;
       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;
           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;
     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;
       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;
     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;
     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;
     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_offset = len - 1;
         parser->item_length = 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;
     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;
         return CURLE_FTP_BAD_FILE_LIST;
-
-      break;
     }
     }
     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;
         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;
         return CURLE_FTP_BAD_FILE_LIST;
-      break;
     }
     }
     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;
     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;
       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;
     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,
 static CURLcode parse_winnt(struct Curl_easy *data,

+ 6 - 10
lib/getinfo.c

@@ -28,6 +28,7 @@
 
 
 #include "urldata.h"
 #include "urldata.h"
 #include "getinfo.h"
 #include "getinfo.h"
+#include "cfilters.h"
 #include "vtls/vtls.h"
 #include "vtls/vtls.h"
 #include "connect.h" /* Curl_getconnectinfo() */
 #include "connect.h" /* Curl_getconnectinfo() */
 #include "progress.h"
 #include "progress.h"
@@ -579,19 +580,14 @@ static CURLcode getinfo_slist(struct Curl_easy *data, CURLINFO info,
       struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **)
       struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **)
                                           param_slistp;
                                           param_slistp;
       struct curl_tlssessioninfo *tsi = &data->tsi;
       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;
       *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;
     break;
   default:
   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,
 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))
   if((key1_len == key2_len) && !memcmp(k1, k2, key1_len))
     return 1;
     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 *));
                                     int (*comp)(void *, void *));
 size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num);
 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 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,
 void Curl_hash_start_iterate(struct Curl_hash *hash,
                              struct Curl_hash_iterator *iter);
                              struct Curl_hash_iterator *iter);
 struct Curl_hash_element *
 struct Curl_hash_element *

+ 3 - 4
lib/headers.c

@@ -26,7 +26,6 @@
 
 
 #include "urldata.h"
 #include "urldata.h"
 #include "strdup.h"
 #include "strdup.h"
-#include "strcase.h"
 #include "sendf.h"
 #include "sendf.h"
 #include "headers.h"
 #include "headers.h"
 #include "curlx/strparse.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 */
   /* 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)) {
   for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
     hs = Curl_node_elem(e);
     hs = Curl_node_elem(e);
-    if(strcasecompare(hs->name, name) &&
+    if(curl_strequal(hs->name, name) &&
        (hs->type & type) &&
        (hs->type & type) &&
        (hs->request == request)) {
        (hs->request == request)) {
       amount++;
       amount++;
@@ -107,7 +106,7 @@ CURLHcode curl_easy_header(CURL *easy,
   else {
   else {
     for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
     for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
       hs = Curl_node_elem(e);
       hs = Curl_node_elem(e);
-      if(strcasecompare(hs->name, name) &&
+      if(curl_strequal(hs->name, name) &&
          (hs->type & type) &&
          (hs->type & type) &&
          (hs->request == request) &&
          (hs->request == request) &&
          (match++ == nameindex)) {
          (match++ == nameindex)) {
@@ -173,7 +172,7 @@ struct curl_header *curl_easy_nextheader(CURL *easy,
      the index for the currently selected entry */
      the index for the currently selected entry */
   for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
   for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) {
     struct Curl_header_store *check = Curl_node_elem(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->request == request) &&
        (check->type & type))
        (check->type & type))
       amount++;
       amount++;

+ 8 - 6
lib/hostip.c

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

+ 3 - 1
lib/hostip6.c

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

+ 9 - 6
lib/hsts.c

@@ -33,7 +33,6 @@
 #include "llist.h"
 #include "llist.h"
 #include "hsts.h"
 #include "hsts.h"
 #include "curl_get_line.h"
 #include "curl_get_line.h"
-#include "strcase.h"
 #include "sendf.h"
 #include "sendf.h"
 #include "parsedate.h"
 #include "parsedate.h"
 #include "fopen.h"
 #include "fopen.h"
@@ -155,7 +154,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
 
 
   do {
   do {
     curlx_str_passblanks(&p);
     curlx_str_passblanks(&p);
-    if(strncasecompare("max-age", p, 7)) {
+    if(curl_strnequal("max-age", p, 7)) {
       bool quoted = FALSE;
       bool quoted = FALSE;
       int rc;
       int rc;
 
 
@@ -185,7 +184,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
       }
       }
       gotma = TRUE;
       gotma = TRUE;
     }
     }
-    else if(strncasecompare("includesubdomains", p, 17)) {
+    else if(curl_strnequal("includesubdomains", p, 17)) {
       if(gotinc)
       if(gotinc)
         return CURLE_BAD_FUNCTION_ARGUMENT;
         return CURLE_BAD_FUNCTION_ARGUMENT;
       subdomains = TRUE;
       subdomains = TRUE;
@@ -272,15 +271,15 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
       if((subdomain && sts->includeSubDomains) && (ntail < hlen)) {
       if((subdomain && sts->includeSubDomains) && (ntail < hlen)) {
         size_t offs = hlen - ntail;
         size_t offs = hlen - ntail;
         if((hostname[offs-1] == '.') &&
         if((hostname[offs-1] == '.') &&
-           strncasecompare(&hostname[offs], sts->host, ntail) &&
+           curl_strnequal(&hostname[offs], sts->host, ntail) &&
            (ntail > blen)) {
            (ntail > blen)) {
           /* save the tail match with the longest tail */
           /* save the tail match with the longest tail */
           bestsub = sts;
           bestsub = sts;
           blen = ntail;
           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;
         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 */
 #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");
   connkeep(conn, "HTTP default");
   if(data->state.http_neg.wanted == CURL_HTTP_V3x) {
   if(data->state.http_neg.wanted == CURL_HTTP_V3x) {
     /* only HTTP/3, needs to work */
     /* 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)
     if(result)
       return result;
       return result;
   }
   }
@@ -259,7 +259,7 @@ char *Curl_checkProxyheaders(struct Curl_easy *data,
   for(head = (conn->bits.proxy && data->set.sep_headers) ?
   for(head = (conn->bits.proxy && data->set.sep_headers) ?
         data->set.proxyheaders : data->set.headers;
         data->set.proxyheaders : data->set.headers;
       head; head = head->next) {
       head; head = head->next) {
-    if(strncasecompare(head->data, thisheader, thislen) &&
+    if(curl_strnequal(head->data, thisheader, thislen) &&
        Curl_headersep(head->data[thislen]))
        Curl_headersep(head->data[thislen]))
       return head->data;
       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 */
   /* the auth string must not have an alnum following */
   size_t n = strlen(auth);
   size_t n = strlen(auth);
-  return strncasecompare(auth, line, n) && !ISALNUM(line[n]);
+  return curl_strnequal(auth, line, n) && !ISALNUM(line[n]);
 }
 }
 #endif
 #endif
 
 
@@ -1482,7 +1482,7 @@ Curl_compareheader(const char *headerline, /* line to check */
   DEBUGASSERT(header);
   DEBUGASSERT(header);
   DEBUGASSERT(content);
   DEBUGASSERT(content);
 
 
-  if(!strncasecompare(headerline, header, hlen))
+  if(!curl_strnequal(headerline, header, hlen))
     return FALSE; /* does not start with header */
     return FALSE; /* does not start with header */
 
 
   /* pass the header */
   /* pass the header */
@@ -1497,7 +1497,7 @@ Curl_compareheader(const char *headerline, /* line to check */
     size_t len;
     size_t len;
     p = curlx_str(&val);
     p = curlx_str(&val);
     for(len = curlx_strlen(&val); len >= curlx_strlen(&val); len--, p++) {
     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! */
         return TRUE; /* match! */
     }
     }
   }
   }
@@ -1893,7 +1893,7 @@ static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn)
 
 
   ptr = Curl_checkheaders(data, STRCONST("Host"));
   ptr = Curl_checkheaders(data, STRCONST("Host"));
   if(ptr && (!data->state.this_is_a_follow ||
   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 !defined(CURL_DISABLE_COOKIES)
     /* If we have a given custom Host: header, we extract the hostname in
     /* 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
        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
 #endif
 
 
-    if(!strcasecompare("Host:", ptr)) {
+    if(!curl_strequal("Host:", ptr)) {
       aptr->host = aprintf("Host:%s\r\n", &ptr[5]);
       aptr->host = aprintf("Host:%s\r\n", &ptr[5]);
       if(!aptr->host)
       if(!aptr->host)
         return CURLE_OUT_OF_MEMORY;
         return CURLE_OUT_OF_MEMORY;
@@ -2005,7 +2005,7 @@ static CURLcode http_target(struct Curl_easy *data,
       return CURLE_OUT_OF_MEMORY;
       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 */
       /* when getting HTTP, we do not want the userinfo the URL */
       uc = curl_url_set(h, CURLUPART_USER, NULL, 0);
       uc = curl_url_set(h, CURLUPART_USER, NULL, 0);
       if(uc) {
       if(uc) {
@@ -2034,7 +2034,7 @@ static CURLcode http_target(struct Curl_easy *data,
     if(result)
     if(result)
       return result;
       return result;
 
 
-    if(strcasecompare("ftp", data->state.up.scheme)) {
+    if(curl_strequal("ftp", data->state.up.scheme)) {
       if(data->set.proxy_transfer_mode) {
       if(data->set.proxy_transfer_mode) {
         /* when doing ftp, append ;type=<a|i> if not present */
         /* when doing ftp, append ;type=<a|i> if not present */
         char *type = strstr(path, ";type=");
         char *type = strstr(path, ";type=");
@@ -2443,7 +2443,7 @@ static CURLcode http_cookies(struct Curl_easy *data,
         data->state.aptr.cookiehost : conn->host.name;
         data->state.aptr.cookiehost : conn->host.name;
       const bool secure_context =
       const bool secure_context =
         conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) ||
         conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) ||
-        strcasecompare("localhost", host) ||
+        curl_strequal("localhost", host) ||
         !strcmp(host, "127.0.0.1") ||
         !strcmp(host, "127.0.0.1") ||
         !strcmp(host, "::1");
         !strcmp(host, "::1");
       Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
       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)))
    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
 #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
 #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
 #else
-          0
+        0
 #endif
 #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
 #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
 #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
 #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
 #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
 #endif
 #ifndef CURL_DISABLE_HSTS
 #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
 #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
 #else
-           0
+        0
 #endif
 #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
 #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
-    }
+  }
 #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;
     break;
   case 't':
   case 't':
   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;
     break;
   case 'w':
   case 'w':
   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;
     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:
 out:
   if(last_hd) {
   if(last_hd) {
     /* if not written yet, write it now */
     /* 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;
   return result;
 }
 }
@@ -4474,7 +4585,7 @@ static bool h2_permissible_field(struct dynhds_entry *e)
     if(e->namelen < H2_NON_FIELD[i].namelen)
     if(e->namelen < H2_NON_FIELD[i].namelen)
       return TRUE;
       return TRUE;
     if(e->namelen == H2_NON_FIELD[i].namelen &&
     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 FALSE;
   }
   }
   return TRUE;
   return TRUE;
@@ -4565,7 +4676,7 @@ CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers,
     e = Curl_dynhds_getn(&req->headers, i);
     e = Curl_dynhds_getn(&req->headers, i);
     /* "TE" is special in that it is only permissible when it
     /* "TE" is special in that it is only permissible when it
      * has only value "trailers". RFC 9113 ch. 8.2.2 */
      * 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"))
       if(http_TE_has_token(e->value, "trailers"))
         result = Curl_dynhds_add(h2_headers, e->name, e->namelen,
         result = Curl_dynhds_add(h2_headers, e->name, e->namelen,
                                  "trailers", sizeof("trailers") - 1);
                                  "trailers", sizeof("trailers") - 1);

+ 171 - 227
lib/http2.c

@@ -36,7 +36,6 @@
 #include "sendf.h"
 #include "sendf.h"
 #include "select.h"
 #include "select.h"
 #include "curlx/base64.h"
 #include "curlx/base64.h"
-#include "strcase.h"
 #include "multiif.h"
 #include "multiif.h"
 #include "url.h"
 #include "url.h"
 #include "urlapi-int.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,
 static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
                                    struct Curl_easy *data);
                                    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 */
 #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,
 static CURLcode http2_data_setup(struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
                                  struct Curl_easy *data,
@@ -449,8 +430,10 @@ static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
       flush_egress = TRUE;
       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);
   Curl_uint_hash_remove(&ctx->streams, data->mid);
@@ -480,33 +463,6 @@ static int h2_client_new(struct Curl_cfilter *cf,
   return rc;
   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,
 static ssize_t send_callback(nghttp2_session *h2,
                              const uint8_t *mem, size_t length, int flags,
                              const uint8_t *mem, size_t length, int flags,
                              void *userp);
                              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,
        not in use by any other transfer, there should not be any data here,
        only "protocol frames" */
        only "protocol frames" */
     CURLcode result;
     CURLcode result;
-    ssize_t nread = -1;
+    size_t nread;
 
 
     *input_pending = FALSE;
     *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);
                   "h2 connection", nread);
       if(h2_process_pending_input(cf, data, &result) < 0)
       if(h2_process_pending_input(cf, data, &result) < 0)
         /* immediate error, considered dead */
         /* immediate error, considered dead */
@@ -785,15 +741,16 @@ static CURLcode nw_out_flush(struct Curl_cfilter *cf,
                              struct Curl_easy *data)
                              struct Curl_easy *data)
 {
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   struct cf_h2_ctx *ctx = cf->ctx;
-  ssize_t nwritten;
+  size_t nwritten;
   CURLcode result;
   CURLcode result;
 
 
   (void)data;
   (void)data;
   if(Curl_bufq_is_empty(&ctx->outbufq))
   if(Curl_bufq_is_empty(&ctx->outbufq))
     return CURLE_OK;
     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) {
     if(result == CURLE_AGAIN) {
       CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN",
       CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN",
                   Curl_bufq_len(&ctx->outbufq));
                   Curl_bufq_len(&ctx->outbufq));
@@ -816,7 +773,7 @@ static ssize_t send_callback(nghttp2_session *h2,
   struct Curl_cfilter *cf = userp;
   struct Curl_cfilter *cf = userp;
   struct cf_h2_ctx *ctx = cf->ctx;
   struct cf_h2_ctx *ctx = cf->ctx;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
-  ssize_t nwritten;
+  size_t nwritten;
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
 
 
   (void)h2;
   (void)h2;
@@ -824,11 +781,12 @@ static ssize_t send_callback(nghttp2_session *h2,
   DEBUGASSERT(data);
   DEBUGASSERT(data);
 
 
   if(!cf->connected)
   if(!cf->connected)
-    nwritten = Curl_bufq_write(&ctx->outbufq, buf, blen, &result);
+    result = Curl_bufq_write(&ctx->outbufq, buf, blen, &nwritten);
   else
   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) {
     if(result == CURLE_AGAIN) {
       ctx->nw_out_blocked = 1;
       ctx->nw_out_blocked = 1;
       return NGHTTP2_ERR_WOULDBLOCK;
       return NGHTTP2_ERR_WOULDBLOCK;
@@ -841,7 +799,8 @@ static ssize_t send_callback(nghttp2_session *h2,
     ctx->nw_out_blocked = 1;
     ctx->nw_out_blocked = 1;
     return NGHTTP2_ERR_WOULDBLOCK;
     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) {
     if(stream->status_code / 100 != 1) {
       stream->resp_hds_complete = TRUE;
       stream->resp_hds_complete = TRUE;
     }
     }
-    drain_stream(cf, data, stream);
+    Curl_multi_mark_dirty(data);
     break;
     break;
   case NGHTTP2_PUSH_PROMISE:
   case NGHTTP2_PUSH_PROMISE:
     rv = push_promise(cf, data, &frame->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) {
     if(frame->rst_stream.error_code) {
       stream->reset = TRUE;
       stream->reset = TRUE;
     }
     }
-    drain_stream(cf, data, stream);
+    Curl_multi_mark_dirty(data);
     break;
     break;
   case NGHTTP2_WINDOW_UPDATE:
   case NGHTTP2_WINDOW_UPDATE:
     if(CURL_WANT_SEND(data) && Curl_bufq_is_empty(&stream->sendbuf)) {
     if(CURL_WANT_SEND(data) && Curl_bufq_is_empty(&stream->sendbuf)) {
       /* need more data, force processing of transfer */
       /* 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)) {
     else if(!Curl_bufq_is_empty(&stream->sendbuf)) {
       /* resume the potentially suspended stream */
       /* resume the potentially suspended stream */
@@ -1245,7 +1204,7 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
                                 stream->id, NGHTTP2_STREAM_CLOSED);
                                 stream->id, NGHTTP2_STREAM_CLOSED);
       stream->closed = TRUE;
       stream->closed = TRUE;
     }
     }
-    drain_stream(cf, data, stream);
+    Curl_multi_mark_dirty(data);
   }
   }
   return CURLE_OK;
   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
          * 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.
          * 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. */
          * 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;
       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);
               stream_id, nghttp2_http2_strerror(error_code), error_code);
   else
   else
     CURL_TRC_CF(data_s, cf, "[%d] CLOSED", stream_id);
     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 */
   /* remove `data_s` from the nghttp2 stream */
   rv = nghttp2_session_set_stream_user_data(session, stream_id, 0);
   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)
       if(!check)
         /* no memory */
         /* no memory */
         return NGHTTP2_ERR_CALLBACK_FAILURE;
         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) ||
          ((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
         /* 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
          * 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
          * 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 we receive data for another handle, wake that up */
     if(CF_DATA_CURRENT(cf) != data_s)
     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",
     CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d",
                 stream->id, stream->status_code);
                 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 we receive data for another handle, wake that up */
   if(CF_DATA_CURRENT(cf) != data_s)
   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",
   CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s",
               stream->id, (int)namelen, name, (int)valuelen, value);
               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;
   struct h2_stream_ctx *stream = NULL;
   CURLcode result;
   CURLcode result;
   ssize_t nread;
   ssize_t nread;
+  size_t n;
   (void)source;
   (void)source;
 
 
   (void)cf;
   (void)cf;
@@ -1803,12 +1760,14 @@ static ssize_t req_body_read_callback(nghttp2_session *session,
   if(!stream)
   if(!stream)
     return NGHTTP2_ERR_CALLBACK_FAILURE;
     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)
     if(result != CURLE_AGAIN)
       return NGHTTP2_ERR_CALLBACK_FAILURE;
       return NGHTTP2_ERR_CALLBACK_FAILURE;
     nread = 0;
     nread = 0;
   }
   }
+  else
+    nread = (ssize_t)n;
 
 
   CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) eos=%d -> %zd, %d",
   CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) eos=%d -> %zd, %d",
               stream_id, length, stream->body_eos, nread, result);
               stream_id, length, stream->body_eos, nread, result);
@@ -1874,20 +1833,20 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
   return result;
   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) {
   if(stream->error == NGHTTP2_REFUSED_STREAM) {
     CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new "
     CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new "
                 "connection", stream->id);
                 "connection", stream->id);
     connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */
     connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */
     data->state.refused_stream = TRUE;
     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) {
   else if(stream->error != NGHTTP2_NO_ERROR) {
     if(stream->resp_hds_complete && data->req.no_body) {
     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->id, nghttp2_http2_strerror(stream->error),
                   stream->error);
                   stream->error);
       stream->close_handled = TRUE;
       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)",
     failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)",
           stream->id, nghttp2_http2_strerror(stream->error),
           stream->id, nghttp2_http2_strerror(stream->error),
           stream->error);
           stream->error);
-    *err = CURLE_HTTP2_STREAM;
-    return -1;
+    return CURLE_HTTP2_STREAM;
   }
   }
   else if(stream->reset) {
   else if(stream->reset) {
     failf(data, "HTTP/2 stream %u was reset", stream->id);
     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) {
   if(!stream->bodystarted) {
     failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
     failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
           " all response header fields, treated as error",
           " all response header fields, treated as error",
           stream->id);
           stream->id);
-    *err = CURLE_HTTP2_STREAM;
-    return -1;
+    return CURLE_HTTP2_STREAM;
   }
   }
 
 
   if(Curl_dynhds_count(&stream->resp_trailers)) {
   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;
     struct dynbuf dbuf;
     size_t i;
     size_t i;
 
 
-    *err = CURLE_OK;
+    result = CURLE_OK;
     curlx_dyn_init(&dbuf, DYN_TRAILERS);
     curlx_dyn_init(&dbuf, DYN_TRAILERS);
     for(i = 0; i < Curl_dynhds_count(&stream->resp_trailers); ++i) {
     for(i = 0; i < Curl_dynhds_count(&stream->resp_trailers); ++i) {
       e = Curl_dynhds_getn(&stream->resp_trailers, i);
       e = Curl_dynhds_getn(&stream->resp_trailers, i);
       if(!e)
       if(!e)
         break;
         break;
       curlx_dyn_reset(&dbuf);
       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->namelen, e->name,
                             (int)e->valuelen, e->value);
                             (int)e->valuelen, e->value);
-      if(*err)
+      if(result)
         break;
         break;
       Curl_debug(data, CURLINFO_HEADER_IN, curlx_dyn_ptr(&dbuf),
       Curl_debug(data, CURLINFO_HEADER_IN, curlx_dyn_ptr(&dbuf),
                  curlx_dyn_len(&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;
         break;
     }
     }
     curlx_dyn_free(&dbuf);
     curlx_dyn_free(&dbuf);
-    if(*err)
+    if(result)
       goto out;
       goto out;
   }
   }
 
 
   stream->close_handled = TRUE;
   stream->close_handled = TRUE;
-  *err = CURLE_OK;
-  rv = 0;
+  result = CURLE_OK;
 
 
 out:
 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)
 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.
  * Flush any out data pending in the network buffer.
  */
  */
 static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
 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 cf_h2_ctx *ctx = cf->ctx;
   struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
   struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
@@ -2037,36 +1991,36 @@ out:
   return nw_out_flush(cf, data);
   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;
   struct cf_h2_ctx *ctx = cf->ctx;
-  ssize_t nread = -1;
+  CURLcode result = CURLE_AGAIN;
 
 
   (void)buf;
   (void)buf;
-  *err = CURLE_AGAIN;
+  (void)len;
+  *pnread = 0;
+
   if(stream->xfer_result) {
   if(stream->xfer_result) {
     CURL_TRC_CF(data, cf, "[%d] xfer write failed", stream->id);
     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) {
   else if(stream->closed) {
     CURL_TRC_CF(data, cf, "[%d] returning CLOSE", stream->id);
     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 ||
   else if(stream->reset ||
           (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) ||
           (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) ||
           (ctx->rcvd_goaway && ctx->remote_max_sid < stream->id)) {
           (ctx->rcvd_goaway && ctx->remote_max_sid < stream->id)) {
     CURL_TRC_CF(data, cf, "[%d] returning ERR", 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,
 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 cf_h2_ctx *ctx = cf->ctx;
   struct h2_stream_ctx *stream;
   struct h2_stream_ctx *stream;
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
-  ssize_t nread;
+  size_t nread;
 
 
   if(should_close_session(ctx)) {
   if(should_close_session(ctx)) {
     CURL_TRC_CF(data, cf, "progress ingress, session is closed");
     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
        * this may leave data in underlying buffers that will not
        * be consumed. */
        * be consumed. */
       if(!cf->next || !cf->next->cft->has_data_pending(cf->next, data))
       if(!cf->next || !cf->next->cft->has_data_pending(cf->next, data))
-        drain_stream(cf, data, stream);
+        Curl_multi_mark_dirty(data);
       break;
       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) {
       if(result != CURLE_AGAIN) {
         failf(data, "Failed receiving HTTP2 data: %d(%s)", result,
         failf(data, "Failed receiving HTTP2 data: %d(%s)", result,
               curl_easy_strerror(result));
               curl_easy_strerror(result));
@@ -2121,9 +2075,8 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
       break;
       break;
     }
     }
     else {
     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))
     if(h2_process_pending_input(cf, data, &result))
@@ -2140,15 +2093,15 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
   return CURLE_OK;
   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 cf_h2_ctx *ctx = cf->ctx;
   struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
   struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
-  ssize_t nread = -1;
-  CURLcode result;
+  CURLcode result, r2;
   struct cf_call_data save;
   struct cf_call_data save;
 
 
+  *pnread = 0;
   if(!stream) {
   if(!stream) {
     /* Abnormal call sequence: either this transfer has never opened a stream
     /* Abnormal call sequence: either this transfer has never opened a stream
      * (unlikely) or the transfer has been done, cleaned up its resources, but
      * (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. */
      * is for such a case. */
     failf(data, "http/2 recv on a transfer never opened "
     failf(data, "http/2 recv on a transfer never opened "
           "or already cleared, mid=%u", data->mid);
           "or already cleared, mid=%u", data->mid);
-    *err = CURLE_HTTP2;
-    return -1;
+    return CURLE_HTTP2;
   }
   }
 
 
   CF_DATA_SAVE(save, cf, data);
   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;
     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;
       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
     /* Now that we transferred this to the upper layer, we report
      * the actual amount of DATA consumed to the H2 session, so
      * the actual amount of DATA consumed to the H2 session, so
      * that it adjusts stream flow control */
      * 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) {
     if(stream->closed) {
       CURL_TRC_CF(data, cf, "[%d] DRAIN closed stream", stream->id);
       CURL_TRC_CF(data, cf, "[%d] DRAIN closed stream", stream->id);
-      drain_stream(cf, data, stream);
+      Curl_multi_mark_dirty(data);
     }
     }
   }
   }
 
 
 out:
 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
     /* pending data to send, need to be called again. Ideally, we
      * monitor the socket for POLLOUT, but when not SENDING
      * monitor the socket for POLLOUT, but when not SENDING
      * any more, we force processing of the transfer. */
      * any more, we force processing of the transfer. */
     if(!CURL_WANT_SEND(data))
     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",
               "window=%d/%d, connection %d/%d",
-              stream->id, len, nread, *err,
+              stream->id, len, result, *pnread,
               nghttp2_session_get_stream_effective_recv_data_length(
               nghttp2_session_get_stream_effective_recv_data_length(
                 ctx->h2, stream->id),
                 ctx->h2, stream->id),
               nghttp2_session_get_stream_effective_local_window_size(
               nghttp2_session_get_stream_effective_local_window_size(
@@ -2209,7 +2160,7 @@ out:
               HTTP2_HUGE_WINDOW_SIZE);
               HTTP2_HUGE_WINDOW_SIZE);
 
 
   CF_DATA_RESTORE(cf, save);
   CF_DATA_RESTORE(cf, save);
-  return nread;
+  return result;
 }
 }
 
 
 static ssize_t cf_h2_body_send(struct Curl_cfilter *cf,
 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)
                                CURLcode *err)
 {
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   struct cf_h2_ctx *ctx = cf->ctx;
-  ssize_t nwritten;
+  size_t nwritten;
 
 
   if(stream->closed) {
   if(stream->closed) {
     if(stream->resp_hds_complete) {
     if(stream->resp_hds_complete) {
@@ -2241,11 +2192,11 @@ static ssize_t cf_h2_body_send(struct Curl_cfilter *cf,
     return -1;
     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;
     return -1;
 
 
-  if(eos && (blen == (size_t)nwritten))
+  if(eos && (blen == nwritten))
     stream->body_eos = TRUE;
     stream->body_eos = TRUE;
 
 
   if(eos || !Curl_bufq_is_empty(&stream->sendbuf)) {
   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 -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 cf_h2_ctx *ctx = cf->ctx;
   struct h2_stream_ctx *stream = NULL;
   struct h2_stream_ctx *stream = NULL;
@@ -2274,36 +2225,34 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream,
   int32_t stream_id;
   int32_t stream_id;
   nghttp2_priority_spec pri_spec;
   nghttp2_priority_spec pri_spec;
   ssize_t nwritten;
   ssize_t nwritten;
+  CURLcode result = CURLE_OK;
 
 
+  *pnwritten = 0;
   Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
   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;
     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)
   if(nwritten < 0)
     goto out;
     goto out;
+  *pnwritten = (size_t)nwritten;
   if(!stream->h1.done) {
   if(!stream->h1.done) {
     /* need more data */
     /* need more data */
     goto out;
     goto out;
   }
   }
   DEBUGASSERT(stream->h1.req);
   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;
     goto out;
-  }
   /* no longer needed */
   /* no longer needed */
   Curl_h1_req_parse_free(&stream->h1);
   Curl_h1_req_parse_free(&stream->h1);
 
 
   nva = Curl_dynhds_to_nva(&h2_headers, &nheader);
   nva = Curl_dynhds_to_nva(&h2_headers, &nheader);
   if(!nva) {
   if(!nva) {
-    *err = CURLE_OUT_OF_MEMORY;
-    nwritten = -1;
+    result = CURLE_OUT_OF_MEMORY;
     goto out;
     goto out;
   }
   }
 
 
@@ -2329,8 +2278,7 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream,
   if(stream_id < 0) {
   if(stream_id < 0) {
     CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%u",
     CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%u",
                 nghttp2_strerror(stream_id), stream_id);
                 nghttp2_strerror(stream_id), stream_id);
-    *err = CURLE_SEND_ERROR;
-    nwritten = -1;
+    result = CURLE_SEND_ERROR;
     goto out;
     goto out;
   }
   }
 
 
@@ -2357,48 +2305,46 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream,
 
 
   stream->id = stream_id;
   stream->id = stream_id;
 
 
-  body = (const char *)buf + nwritten;
-  bodylen = len - nwritten;
+  body = (const char *)buf + *pnwritten;
+  bodylen = len - *pnwritten;
 
 
   if(bodylen || eos) {
   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)
     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:
 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);
   Curl_safefree(nva);
   *pstream = stream;
   *pstream = stream;
   Curl_dynhds_free(&h2_headers);
   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 cf_h2_ctx *ctx = cf->ctx;
   struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
   struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
   struct cf_call_data save;
   struct cf_call_data save;
   ssize_t nwritten;
   ssize_t nwritten;
-  CURLcode result;
+  CURLcode result = CURLE_OK, r2;
 
 
   CF_DATA_SAVE(save, cf, data);
   CF_DATA_SAVE(save, cf, data);
+  *pnwritten = 0;
 
 
   if(!stream || stream->id == -1) {
   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;
       goto out;
-    }
     DEBUGASSERT(stream);
     DEBUGASSERT(stream);
   }
   }
   else if(stream->body_eos) {
   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.
      * to trigger flushing again.
      * If this works, we report to have written `len` bytes. */
      * If this works, we report to have written `len` bytes. */
     DEBUGASSERT(eos);
     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",
     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) {
     if(nwritten < 0) {
       goto out;
       goto out;
     }
     }
-    nwritten = len;
+    *pnwritten = len;
   }
   }
   else {
   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",
     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,
   /* Call the nghttp2 send loop and flush to write ALL buffered data,
    * headers and/or request body completely out to the network */
    * 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
   /* if the stream has been closed in egress handling (nghttp2 does that
    * when it does not like the headers, for example */
    * when it does not like the headers, for example */
   if(stream && stream->closed) {
   if(stream && stream->closed) {
     infof(data, "stream %u closed", stream->id);
     infof(data, "stream %u closed", stream->id);
-    *err = CURLE_SEND_ERROR;
-    nwritten = -1;
+    result = CURLE_SEND_ERROR;
     goto out;
     goto out;
   }
   }
-  else if(result && (result != CURLE_AGAIN)) {
-    *err = result;
-    nwritten = -1;
+  else if(r2 && (r2 != CURLE_AGAIN)) {
+    result = r2;
     goto out;
     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
     /* nghttp2 thinks this session is done. If the stream has not been
      * closed, this is an error state for out transfer */
      * closed, this is an error state for out transfer */
     if(stream && stream->closed) {
     if(stream && stream->closed) {
-      nwritten = http2_handle_stream_close(cf, data, stream, err);
+      result = http2_handle_stream_close(cf, data, stream, pnwritten);
     }
     }
     else {
     else {
       CURL_TRC_CF(data, cf, "send: nothing to do in this session");
       CURL_TRC_CF(data, cf, "send: nothing to do in this session");
-      *err = CURLE_HTTP2;
-      nwritten = -1;
+      result = CURLE_HTTP2;
     }
     }
   }
   }
 
 
 out:
 out:
   if(stream) {
   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), "
                 "eos=%d, h2 windows %d-%d (stream-conn), "
                 "buffers %zu-%zu (stream-conn)",
                 "buffers %zu-%zu (stream-conn)",
-                stream->id, len, nwritten, *err,
+                stream->id, len, result, *pnwritten,
                 stream->body_eos,
                 stream->body_eos,
                 nghttp2_session_get_stream_remote_window_size(
                 nghttp2_session_get_stream_remote_window_size(
                   ctx->h2, stream->id),
                   ctx->h2, stream->id),
@@ -2466,14 +2411,14 @@ out:
                 Curl_bufq_len(&ctx->outbufq));
                 Curl_bufq_len(&ctx->outbufq));
   }
   }
   else {
   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)",
                 "connection-window=%d, nw_send_buffer(%zu)",
-                len, nwritten, *err,
+                len, result, *pnwritten,
                 nghttp2_session_get_remote_window_size(ctx->h2),
                 nghttp2_session_get_remote_window_size(ctx->h2),
                 Curl_bufq_len(&ctx->outbufq));
                 Curl_bufq_len(&ctx->outbufq));
   }
   }
   CF_DATA_RESTORE(cf, save);
   CF_DATA_RESTORE(cf, save);
-  return nwritten;
+  return result;
 }
 }
 
 
 static CURLcode cf_h2_flush(struct Curl_cfilter *cf,
 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
        * not. We may have already buffered and exhausted the new window
        * by operating on things in flight during the handling of other
        * by operating on things in flight during the handling of other
        * transfers. */
        * 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,
     CURL_TRC_CF(data, cf, "[%d] stream now %spaused", stream->id,
                 pause ? "" : "un");
                 pause ? "" : "un");
@@ -2769,15 +2713,16 @@ static bool cf_h2_is_alive(struct Curl_cfilter *cf,
                            bool *input_pending)
                            bool *input_pending)
 {
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   struct cf_h2_ctx *ctx = cf->ctx;
-  CURLcode result;
+  bool alive;
   struct cf_call_data save;
   struct cf_call_data save;
 
 
+  *input_pending = FALSE;
   CF_DATA_SAVE(save, cf, data);
   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",
   CURL_TRC_CF(data, cf, "conn alive -> %d, input_pending=%d",
-              result, *input_pending);
+              alive, *input_pending);
   CF_DATA_RESTORE(cf, save);
   CF_DATA_RESTORE(cf, save);
-  return result;
+  return alive;
 }
 }
 
 
 static CURLcode cf_h2_keep_alive(struct Curl_cfilter *cf,
 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);
     DEBUGASSERT(pres1);
 
 
     CF_DATA_SAVE(save, cf, data);
     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 */
       /* the limit is what we have in use right now */
       effective_max = CONN_ATTACHED(cf->conn);
       effective_max = CONN_ATTACHED(cf->conn);
     }
     }
@@ -2848,7 +2793,6 @@ struct Curl_cftype Curl_cft_nghttp2 = {
   cf_h2_connect,
   cf_h2_connect,
   cf_h2_close,
   cf_h2_close,
   cf_h2_shutdown,
   cf_h2_shutdown,
-  Curl_cf_def_get_host,
   cf_h2_adjust_pollset,
   cf_h2_adjust_pollset,
   cf_h2_data_pending,
   cf_h2_data_pending,
   cf_h2_send,
   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
     /* Remaining data from the protocol switch reply is already using
      * the switched protocol, ie. HTTP/2. We add that to the network
      * the switched protocol, ie. HTTP/2. We add that to the network
      * inbufq. */
      * 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);
       failf(data, "error on copying HTTP Upgrade response: %d", result);
       return CURLE_RECV_ERROR;
       return CURLE_RECV_ERROR;
     }
     }
-    if((size_t)copied < nread) {
+    if(copied < nread) {
       failf(data, "connection buffer size could not take all data "
       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);
             copied, nread);
       return CURLE_HTTP2;
       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,
 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;
   CURLcode result = CURLE_OK;
 
 

+ 26 - 18
lib/http_negotiate.c

@@ -39,6 +39,20 @@
 #include "curl_memory.h"
 #include "curl_memory.h"
 #include "memdebug.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,
 CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
                               bool proxy, const char *header)
                               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] ?
     service = data->set.str[STRING_PROXY_SERVICE_NAME] ?
               data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP";
               data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP";
     host = conn->http_proxy.host.name;
     host = conn->http_proxy.host.name;
-    neg_ctx = &conn->proxyneg;
     state = conn->proxy_negotiate_state;
     state = conn->proxy_negotiate_state;
 #else
 #else
     return CURLE_NOT_BUILT_IN;
     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] ?
     service = data->set.str[STRING_SERVICE_NAME] ?
               data->set.str[STRING_SERVICE_NAME] : "HTTP";
               data->set.str[STRING_SERVICE_NAME] : "HTTP";
     host = conn->host.name;
     host = conn->host.name;
-    neg_ctx = &conn->negotiate;
     state = conn->http_negotiate_state;
     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 */
   /* Not set means empty */
   if(!userp)
   if(!userp)
     userp = "";
     userp = "";
@@ -94,12 +110,12 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
   if(!len) {
   if(!len) {
     if(state == GSS_AUTHSUCC) {
     if(state == GSS_AUTHSUCC) {
       infof(data, "Negotiate auth restarted");
       infof(data, "Negotiate auth restarted");
-      Curl_http_auth_cleanup_negotiate(conn);
+      http_auth_nego_reset(conn, neg_ctx, proxy);
     }
     }
     else if(state != GSS_AUTHNONE) {
     else if(state != GSS_AUTHNONE) {
       /* The server rejected our authentication and has not supplied any more
       /* The server rejected our authentication and has not supplied any more
       negotiation mechanisms */
       negotiation mechanisms */
-      Curl_http_auth_cleanup_negotiate(conn);
+      http_auth_nego_reset(conn, neg_ctx, proxy);
       return CURLE_LOGIN_DENIED;
       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(
     result = Curl_ssl_get_channel_binding(
       data, FIRSTSOCKET, &neg_ctx->channel_binding_data);
       data, FIRSTSOCKET, &neg_ctx->channel_binding_data);
     if(result) {
     if(result) {
-      Curl_http_auth_cleanup_negotiate(conn);
+      http_auth_nego_reset(conn, neg_ctx, proxy);
       return result;
       return result;
     }
     }
   }
   }
@@ -134,7 +150,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
 #endif
 #endif
 
 
   if(result)
   if(result)
-    Curl_http_auth_cleanup_negotiate(conn);
+    http_auth_nego_reset(conn, neg_ctx, proxy);
 
 
   return result;
   return result;
 }
 }
@@ -152,7 +168,6 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data,
 
 
   if(proxy) {
   if(proxy) {
 #ifndef CURL_DISABLE_PROXY
 #ifndef CURL_DISABLE_PROXY
-    neg_ctx = &conn->proxyneg;
     authp = &data->state.authproxy;
     authp = &data->state.authproxy;
     state = &conn->proxy_negotiate_state;
     state = &conn->proxy_negotiate_state;
 #else
 #else
@@ -160,10 +175,12 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data,
 #endif
 #endif
   }
   }
   else {
   else {
-    neg_ctx = &conn->negotiate;
     authp = &data->state.authhost;
     authp = &data->state.authhost;
     state = &conn->http_negotiate_state;
     state = &conn->http_negotiate_state;
   }
   }
+  neg_ctx = Curl_auth_nego_get(conn, proxy);
+  if(!neg_ctx)
+    return CURLE_OUT_OF_MEMORY;
 
 
   authp->done = FALSE;
   authp->done = FALSE;
 
 
@@ -184,7 +201,7 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data,
     if(neg_ctx->noauthpersist && *state == GSS_AUTHSUCC) {
     if(neg_ctx->noauthpersist && *state == GSS_AUTHSUCC) {
       infof(data, "Curl_output_negotiate, "
       infof(data, "Curl_output_negotiate, "
             "no persistent authentication: cleanup existing context");
             "no persistent authentication: cleanup existing context");
-      Curl_http_auth_cleanup_negotiate(conn);
+      http_auth_nego_reset(conn, neg_ctx, proxy);
     }
     }
     if(!neg_ctx->context) {
     if(!neg_ctx->context) {
       result = Curl_input_negotiate(data, conn, proxy, "Negotiate");
       result = Curl_input_negotiate(data, conn, proxy, "Negotiate");
@@ -249,13 +266,4 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data,
   return CURLE_OK;
   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 */
 #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,
 CURLcode Curl_output_negotiate(struct Curl_easy *data,
                                struct connectdata *conn, bool proxy);
                                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
 
 
 #endif /* HEADER_CURL_HTTP_NEGOTIATE_H */
 #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 */
                                                 header */
 {
 {
   /* point to the correct struct with this */
   /* point to the correct struct with this */
-  struct ntlmdata *ntlm;
   curlntlm *state;
   curlntlm *state;
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
   struct connectdata *conn = data->conn;
 
 
-  ntlm = proxy ? &conn->proxyntlm : &conn->ntlm;
   state = proxy ? &conn->proxy_ntlm_state : &conn->http_ntlm_state;
   state = proxy ? &conn->proxy_ntlm_state : &conn->http_ntlm_state;
 
 
   if(checkprefix("NTLM", header)) {
   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);
     curlx_str_passblanks(&header);
     if(*header) {
     if(*header) {
       unsigned char *hdr;
       unsigned char *hdr;
@@ -93,11 +94,11 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data,
     else {
     else {
       if(*state == NTLMSTATE_LAST) {
       if(*state == NTLMSTATE_LAST) {
         infof(data, "NTLM auth restarted");
         infof(data, "NTLM auth restarted");
-        Curl_http_auth_cleanup_ntlm(conn);
+        Curl_auth_ntlm_remove(conn, proxy);
       }
       }
       else if(*state == NTLMSTATE_TYPE3) {
       else if(*state == NTLMSTATE_TYPE3) {
         infof(data, "NTLM handshake rejected");
         infof(data, "NTLM handshake rejected");
-        Curl_http_auth_cleanup_ntlm(conn);
+        Curl_auth_ntlm_remove(conn, proxy);
         *state = NTLMSTATE_NONE;
         *state = NTLMSTATE_NONE;
         return CURLE_REMOTE_ACCESS_DENIED;
         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] ?
     service = data->set.str[STRING_PROXY_SERVICE_NAME] ?
       data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP";
       data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP";
     hostname = conn->http_proxy.host.name;
     hostname = conn->http_proxy.host.name;
-    ntlm = &conn->proxyntlm;
     state = &conn->proxy_ntlm_state;
     state = &conn->proxy_ntlm_state;
     authp = &data->state.authproxy;
     authp = &data->state.authproxy;
 #else
 #else
@@ -164,10 +164,12 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
     service = data->set.str[STRING_SERVICE_NAME] ?
     service = data->set.str[STRING_SERVICE_NAME] ?
       data->set.str[STRING_SERVICE_NAME] : "HTTP";
       data->set.str[STRING_SERVICE_NAME] : "HTTP";
     hostname = conn->host.name;
     hostname = conn->host.name;
-    ntlm = &conn->ntlm;
     state = &conn->http_ntlm_state;
     state = &conn->http_ntlm_state;
     authp = &data->state.authhost;
     authp = &data->state.authhost;
   }
   }
+  ntlm = Curl_auth_ntlm_get(conn, proxy);
+  if(!ntlm)
+    return CURLE_OUT_OF_MEMORY;
   authp->done = FALSE;
   authp->done = FALSE;
 
 
   /* not set means empty */
   /* not set means empty */
@@ -178,10 +180,10 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
     passwdp = "";
     passwdp = "";
 
 
 #ifdef USE_WINDOWS_SSPI
 #ifdef USE_WINDOWS_SSPI
-  if(!Curl_hSecDll) {
+  if(!Curl_pSecFn) {
     /* not thread safe and leaks - use curl_global_init() to avoid */
     /* not thread safe and leaks - use curl_global_init() to avoid */
     CURLcode err = Curl_sspi_global_init();
     CURLcode err = Curl_sspi_global_init();
-    if(!Curl_hSecDll)
+    if(!Curl_pSecFn)
       return err;
       return err;
   }
   }
 #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
 #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
@@ -200,9 +202,8 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
   case NTLMSTATE_TYPE1:
   case NTLMSTATE_TYPE1:
   default: /* for the weird cases we (re)start here */
   default: /* for the weird cases we (re)start here */
     /* Create a type-1 message */
     /* 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) {
     if(!result) {
       DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0);
       DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0);
       result = curlx_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg),
       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;
   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 */
 #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 */
 /* this is for creating NTLM header output */
 CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy);
 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
 
 
 #endif /* HEADER_CURL_HTTP_NTLM_H */
 #endif /* HEADER_CURL_HTTP_NTLM_H */

+ 15 - 17
lib/http_proxy.c

@@ -38,7 +38,6 @@
 #include "cf-h1-proxy.h"
 #include "cf-h1-proxy.h"
 #include "cf-h2-proxy.h"
 #include "cf-h2-proxy.h"
 #include "connect.h"
 #include "connect.h"
-#include "strcase.h"
 #include "vtls/vtls.h"
 #include "vtls/vtls.h"
 #include "transfer.h"
 #include "transfer.h"
 #include "multiif.h"
 #include "multiif.h"
@@ -53,7 +52,7 @@
 static bool hd_name_eq(const char *n1, size_t n1len,
 static bool hd_name_eq(const char *n1, size_t n1len,
                        const char *n2, size_t n2len)
                        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,
 static CURLcode dynhds_add_custom(struct Curl_easy *data,
@@ -383,21 +382,21 @@ out:
   return result;
   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,
 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_connect,
   http_proxy_cf_close,
   http_proxy_cf_close,
   Curl_cf_def_shutdown,
   Curl_cf_def_shutdown,
-  Curl_cf_http_proxy_get_host,
   Curl_cf_def_adjust_pollset,
   Curl_cf_def_adjust_pollset,
   Curl_cf_def_data_pending,
   Curl_cf_def_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_send,
@@ -451,7 +449,7 @@ struct Curl_cftype Curl_cft_http_proxy = {
   Curl_cf_def_cntrl,
   Curl_cf_def_cntrl,
   Curl_cf_def_conn_is_alive,
   Curl_cf_def_conn_is_alive,
   Curl_cf_def_conn_keep_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,
 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 */
 /* Default proxy timeout in milliseconds */
 #define PROXY_TIMEOUT (3600*1000)
 #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,
 CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at,
                                          struct Curl_easy *data);
                                          struct Curl_easy *data);
 
 
 extern struct Curl_cftype Curl_cft_http_proxy;
 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) ||  \
 #define IS_HTTPS_PROXY(t) (((t) == CURLPROXY_HTTPS) ||  \
                            ((t) == CURLPROXY_HTTPS2))
                            ((t) == CURLPROXY_HTTPS2))

+ 5 - 6
lib/if2ip.c

@@ -52,8 +52,7 @@
 #  include <inet.h>
 #  include <inet.h>
 #endif
 #endif
 
 
-#include "inet_ntop.h"
-#include "strcase.h"
+#include "curlx/inet_ntop.h"
 #include "if2ip.h"
 #include "if2ip.h"
 /* The last 3 #include files should be in this order */
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_printf.h"
@@ -117,7 +116,7 @@ if2ip_result_t Curl_if2ip(int af,
     for(iface = head; iface != NULL; iface = iface->ifa_next) {
     for(iface = head; iface != NULL; iface = iface->ifa_next) {
       if(iface->ifa_addr) {
       if(iface->ifa_addr) {
         if(iface->ifa_addr->sa_family == af) {
         if(iface->ifa_addr->sa_family == af) {
-          if(strcasecompare(iface->ifa_name, interf)) {
+          if(curl_strequal(iface->ifa_name, interf)) {
             void *addr;
             void *addr;
             const char *ip;
             const char *ip;
             char scope[12] = "";
             char scope[12] = "";
@@ -162,13 +161,13 @@ if2ip_result_t Curl_if2ip(int af,
               addr =
               addr =
                 &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr;
                 &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr;
             res = IF2IP_FOUND;
             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);
             msnprintf(buf, buf_size, "%s%s", ip, scope);
             break;
             break;
           }
           }
         }
         }
         else if((res == IF2IP_NOT_FOUND) &&
         else if((res == IF2IP_NOT_FOUND) &&
-                strcasecompare(iface->ifa_name, interf)) {
+                curl_strequal(iface->ifa_name, interf)) {
           res = IF2IP_AF_NOT_SUPPORTED;
           res = IF2IP_AF_NOT_SUPPORTED;
         }
         }
       }
       }
@@ -235,7 +234,7 @@ if2ip_result_t Curl_if2ip(int af,
 
 
   s = (struct sockaddr_in *)(void *)&req.ifr_addr;
   s = (struct sockaddr_in *)(void *)&req.ifr_addr;
   memcpy(&in, &s->sin_addr, sizeof(in));
   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);
   sclose(dummy);
   if(!r)
   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
   /* Does the command name match and is it followed by a space character or at
      the end of line? */
      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))
      (line[cmd_len] == ' ' || line + cmd_len + 2 == end))
     return TRUE;
     return TRUE;
 
 
@@ -358,16 +358,16 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn,
       case IMAP_LIST:
       case IMAP_LIST:
         if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
         if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
            (imap->custom && !imap_matchresp(line, len, imap->custom) &&
            (imap->custom && !imap_matchresp(line, len, imap->custom) &&
-            (!strcasecompare(imap->custom, "STORE") ||
+            (!curl_strequal(imap->custom, "STORE") ||
              !imap_matchresp(line, len, "FETCH")) &&
              !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;
           return FALSE;
         break;
         break;
 
 
@@ -1239,7 +1239,7 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data,
   else if(imapcode == IMAP_RESP_OK) {
   else if(imapcode == IMAP_RESP_OK) {
     /* Check if the UIDVALIDITY has been specified and matches */
     /* Check if the UIDVALIDITY has been specified and matches */
     if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
     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");
       failf(data, "Mailbox UIDVALIDITY has changed");
       result = CURLE_REMOTE_FILE_NOT_FOUND;
       result = CURLE_REMOTE_FILE_NOT_FOUND;
     }
     }
@@ -1347,9 +1347,6 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
     else {
     else {
       /* IMAP download */
       /* IMAP download */
       data->req.maxdownload = size;
       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);
       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)
   /* Determine if the requested mailbox (with the same UIDVALIDITY if set)
      has already been selected on this connection */
      has already been selected on this connection */
   if(imap->mailbox && imapc->mailbox &&
   if(imap->mailbox && imapc->mailbox &&
-     strcasecompare(imap->mailbox, imapc->mailbox) &&
+     curl_strequal(imap->mailbox, imapc->mailbox) &&
      (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
      (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
-      strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)))
+      curl_strequal(imap->uidvalidity, imapc->mailbox_uidvalidity)))
     selected = TRUE;
     selected = TRUE;
 
 
   /* Start the first command in the DO phase */
   /* Start the first command in the DO phase */
@@ -1780,18 +1777,14 @@ static CURLcode imap_disconnect(struct Curl_easy *data,
   (void)data;
   (void)data;
   if(imapc) {
   if(imapc) {
     /* We cannot send quit unconditionally. If this connection is stale or
     /* 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. */
        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))
       if(!imap_perform_logout(data, imapc))
         (void)imap_block_statemach(data, imapc, TRUE); /* ignore errors */
         (void)imap_block_statemach(data, imapc, TRUE); /* ignore errors */
     }
     }
-
-    /* Cleanup the SASL module */
-    Curl_sasl_cleanup(conn, imapc->sasl.authused);
   }
   }
   return CURLE_OK;
   return CURLE_OK;
 }
 }
@@ -2079,12 +2072,12 @@ static CURLcode imap_parse_url_options(struct connectdata *conn,
     while(*ptr && *ptr != ';')
     while(*ptr && *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 */
       /* User prefers plaintext LOGIN over any SASL, including SASL LOGIN */
       prefer_login = TRUE;
       prefer_login = TRUE;
       imapc->sasl.prefmech = SASL_AUTH_NONE;
       imapc->sasl.prefmech = SASL_AUTH_NONE;
     }
     }
-    else if(strncasecompare(key, "AUTH=", 5)) {
+    else if(curl_strnequal(key, "AUTH=", 5)) {
       prefer_login = FALSE;
       prefer_login = FALSE;
       result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
       result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
                                                value, ptr - value);
                                                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.
        PARTIAL) stripping of the trailing slash character if it is present.
 
 
        Note: Unknown parameters trigger a URL_MALFORMAT error. */
        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] == '/')
       if(valuelen > 0 && value[valuelen - 1] == '/')
         value[valuelen - 1] = '\0';
         value[valuelen - 1] = '\0';
 
 
       imap->uidvalidity = value;
       imap->uidvalidity = value;
       value = NULL;
       value = NULL;
     }
     }
-    else if(strcasecompare(name, "UID") && !imap->uid) {
+    else if(curl_strequal(name, "UID") && !imap->uid) {
       if(valuelen > 0 && value[valuelen - 1] == '/')
       if(valuelen > 0 && value[valuelen - 1] == '/')
         value[valuelen - 1] = '\0';
         value[valuelen - 1] = '\0';
 
 
       imap->uid = value;
       imap->uid = value;
       value = NULL;
       value = NULL;
     }
     }
-    else if(strcasecompare(name, "MAILINDEX") && !imap->mindex) {
+    else if(curl_strequal(name, "MAILINDEX") && !imap->mindex) {
       if(valuelen > 0 && value[valuelen - 1] == '/')
       if(valuelen > 0 && value[valuelen - 1] == '/')
         value[valuelen - 1] = '\0';
         value[valuelen - 1] = '\0';
 
 
       imap->mindex = value;
       imap->mindex = value;
       value = NULL;
       value = NULL;
     }
     }
-    else if(strcasecompare(name, "SECTION") && !imap->section) {
+    else if(curl_strequal(name, "SECTION") && !imap->section) {
       if(valuelen > 0 && value[valuelen - 1] == '/')
       if(valuelen > 0 && value[valuelen - 1] == '/')
         value[valuelen - 1] = '\0';
         value[valuelen - 1] = '\0';
 
 
       imap->section = value;
       imap->section = value;
       value = NULL;
       value = NULL;
     }
     }
-    else if(strcasecompare(name, "PARTIAL") && !imap->partial) {
+    else if(curl_strequal(name, "PARTIAL") && !imap->partial) {
       if(valuelen > 0 && value[valuelen - 1] == '/')
       if(valuelen > 0 && value[valuelen - 1] == '/')
         value[valuelen - 1] = '\0';
         value[valuelen - 1] = '\0';
 
 

+ 54 - 54
lib/krb5.c

@@ -56,7 +56,6 @@
 #include "transfer.h"
 #include "transfer.h"
 #include "curl_krb5.h"
 #include "curl_krb5.h"
 #include "curlx/warnless.h"
 #include "curlx/warnless.h"
-#include "strcase.h"
 #include "strdup.h"
 #include "strdup.h"
 
 
 /* The last 3 #include files should be in this order */
 /* 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;
   gss_ctx_id_t *context = app_data;
   struct gss_channel_bindings_struct chan;
   struct gss_channel_bindings_struct chan;
   size_t base64_sz = 0;
   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;
   char *stringp;
   struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
   struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
 
 
-  if(!ftpc)
+  if(!ftpc || !remote_in_addr)
     return -2;
     return -2;
 
 
   if(getsockname(conn->sock[FIRSTSOCKET],
   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.initiator_address.value = &conn->local_addr.sin_addr.s_addr;
   chan.acceptor_addrtype = GSS_C_AF_INET;
   chan.acceptor_addrtype = GSS_C_AF_INET;
   chan.acceptor_address.length = l - 4;
   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.length = 0;
   chan.application_data.value = NULL;
   chan.application_data.value = NULL;
 
 
@@ -384,7 +385,8 @@ static void krb5_end(void *app_data)
   OM_uint32 min;
   OM_uint32 min;
   gss_ctx_id_t *context = app_data;
   gss_ctx_id_t *context = app_data;
   if(*context != GSS_C_NO_CONTEXT) {
   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;
     (void)maj;
     DEBUGASSERT(maj == GSS_S_COMPLETE);
     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;
   char *to_p = to;
   CURLcode result;
   CURLcode result;
-  ssize_t nread = 0;
+  size_t nread = 0;
 
 
   while(len > 0) {
   while(len > 0) {
     result = Curl_conn_recv(data, sockindex, to_p, len, &nread);
     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;
       return result;
-    }
+    if(nread > len)
+      return CURLE_RECV_ERROR;
+    len -= nread;
+    to_p += nread;
   }
   }
   return CURLE_OK;
   return CURLE_OK;
 }
 }
@@ -523,8 +524,8 @@ socket_write(struct Curl_easy *data, int sockindex, const void *to,
   return CURLE_OK;
   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;
   struct connectdata *conn = data->conn;
   int len;
   int len;
@@ -579,52 +580,49 @@ buffer_read(struct krb5buffer *buf, void *data, size_t len)
 }
 }
 
 
 /* Matches Curl_recv signature */
 /* 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;
   struct connectdata *conn = data->conn;
-
-  *err = CURLE_OK;
+  CURLcode result = CURLE_OK;
+  size_t bytes_read;
 
 
   /* Handle clear text response. */
   /* 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) {
   if(conn->in_buffer.eof_flag) {
     conn->in_buffer.eof_flag = 0;
     conn->in_buffer.eof_flag = 0;
-    return 0;
+    *pnread = 0;
+    return CURLE_OK;
   }
   }
 
 
   bytes_read = buffer_read(&conn->in_buffer, buffer, len);
   bytes_read = buffer_read(&conn->in_buffer, buffer, len);
-  len -= bytes_read;
-  total_read += bytes_read;
   buffer += bytes_read;
   buffer += bytes_read;
+  len -= bytes_read;
+  *pnread += bytes_read;
 
 
   while(len > 0) {
   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(curlx_dyn_len(&conn->in_buffer.buf) == 0) {
-      if(bytes_read > 0)
+      if(*pnread > 0)
         conn->in_buffer.eof_flag = 1;
         conn->in_buffer.eof_flag = 1;
-      return bytes_read;
+      return result;
     }
     }
     bytes_read = buffer_read(&conn->in_buffer, buffer, len);
     bytes_read = buffer_read(&conn->in_buffer, buffer, len);
-    len -= bytes_read;
-    total_read += bytes_read;
     buffer += 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
 /* Send |length| bytes from |from| to the |sockindex| socket taking care of
    encoding and negotiating with the server. |from| can be NULL. */
    encoding and negotiating with the server. |from| can be NULL. */
 static void do_sec_send(struct Curl_easy *data, struct connectdata *conn,
 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 */
   int bytes, htonl_bytes; /* 32-bit integers for htonl */
   char *buffer = NULL;
   char *buffer = NULL;
@@ -642,8 +640,8 @@ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn,
     else
     else
       prot_level = conn->command_prot;
       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)
   if(!buffer || bytes <= 0)
     return; /* error */
     return; /* error */
 
 
@@ -677,34 +675,36 @@ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn,
   free(buffer);
   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)
   if(len <= 0)
     len = length;
     len = length;
   while(length) {
   while(length) {
-    if(length < (size_t)len)
+    if(length < len)
       len = length;
       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;
     length -= len;
     buffer += len;
     buffer += len;
-    tx += len;
+    *pnwritten += len;
   }
   }
-  return tx;
+  return CURLE_OK;
 }
 }
 
 
 /* Matches Curl_send signature */
 /* 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 */
   (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,
 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 "escape.h"
 #include "progress.h"
 #include "progress.h"
 #include "transfer.h"
 #include "transfer.h"
-#include "strcase.h"
 #include "curlx/strparse.h"
 #include "curlx/strparse.h"
 #include "curl_ldap.h"
 #include "curl_ldap.h"
 #include "curlx/multibyte.h"
 #include "curlx/multibyte.h"
@@ -393,7 +392,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
     if(conn->ssl_config.verifypeer) {
     if(conn->ssl_config.verifypeer) {
       /* OpenLDAP SDK supports BASE64 files. */
       /* OpenLDAP SDK supports BASE64 files. */
       if((data->set.ssl.cert_type) &&
       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");
         failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type");
         result = CURLE_SSL_CERTPROBLEM;
         result = CURLE_SSL_CERTPROBLEM;
         goto quit;
         goto quit;
@@ -748,15 +747,15 @@ static void _ldap_trace(const char *fmt, ...)
  */
  */
 static int str2scope(const char *p)
 static int str2scope(const char *p)
 {
 {
-  if(strcasecompare(p, "one"))
+  if(curl_strequal(p, "one"))
     return LDAP_SCOPE_ONELEVEL;
     return LDAP_SCOPE_ONELEVEL;
-  if(strcasecompare(p, "onetree"))
+  if(curl_strequal(p, "onetree"))
     return LDAP_SCOPE_ONELEVEL;
     return LDAP_SCOPE_ONELEVEL;
-  if(strcasecompare(p, "base"))
+  if(curl_strequal(p, "base"))
     return LDAP_SCOPE_BASE;
     return LDAP_SCOPE_BASE;
-  if(strcasecompare(p, "sub"))
+  if(curl_strequal(p, "sub"))
     return LDAP_SCOPE_SUBTREE;
     return LDAP_SCOPE_SUBTREE;
-  if(strcasecompare(p, "subtree"))
+  if(curl_strequal(p, "subtree"))
     return LDAP_SCOPE_SUBTREE;
     return LDAP_SCOPE_SUBTREE;
   return -1;
   return -1;
 }
 }
@@ -801,7 +800,7 @@ static int _ldap_url_parse2(struct Curl_easy *data,
   if(!data ||
   if(!data ||
      !data->state.up.path ||
      !data->state.up.path ||
      data->state.up.path[0] != '/' ||
      data->state.up.path[0] != '/' ||
-     !strncasecompare("LDAP", data->state.up.scheme, 4))
+     !curl_strnequal("LDAP", data->state.up.scheme, 4))
     return LDAP_INVALID_SYNTAX;
     return LDAP_INVALID_SYNTAX;
 
 
   ludp->lud_scope = LDAP_SCOPE_BASE;
   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
  * @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)
 Curl_node_uremove(struct Curl_llist_node *e, void *user)
 {
 {
   struct Curl_llist *list;
   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);
                             const void *, struct Curl_llist_node *node);
 void Curl_llist_append(struct Curl_llist *,
 void Curl_llist_append(struct Curl_llist *,
                        const void *, struct Curl_llist_node *node);
                        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_node_remove(struct Curl_llist_node *);
 void Curl_llist_destroy(struct Curl_llist *, void *);
 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);
 void *Curl_node_elem(struct Curl_llist_node *n);
 
 
 /* Remove the node from the list and return the custom data
 /* 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 *);
 void *Curl_node_take_elem(struct Curl_llist_node *);
 
 
 /* Curl_node_next() returns the next element in a list from a given
 /* 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"
 #include "urldata.h"
 
 
-#define MEMDEBUG_NODEFINES /* do not redefine the standard functions */
-
 /* The last 3 #include files should be in this order */
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "curl_memory.h"
@@ -70,7 +68,7 @@ static void curl_dbg_cleanup(void)
   if(curl_dbg_logfile &&
   if(curl_dbg_logfile &&
      curl_dbg_logfile != stderr &&
      curl_dbg_logfile != stderr &&
      curl_dbg_logfile != stdout) {
      curl_dbg_logfile != stdout) {
-    fclose(curl_dbg_logfile);
+    (fclose)(curl_dbg_logfile);
   }
   }
   curl_dbg_logfile = NULL;
   curl_dbg_logfile = NULL;
 }
 }
@@ -80,7 +78,11 @@ void curl_dbg_memdebug(const char *logname)
 {
 {
   if(!curl_dbg_logfile) {
   if(!curl_dbg_logfile) {
     if(logname && *logname)
     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
     else
       curl_dbg_logfile = stderr;
       curl_dbg_logfile = stderr;
 #ifdef MEMDEBUG_LOG_SYNC
 #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,
 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;
   curl_socket_t sockfd;
 
 
   if(countcheck("socket", line, source))
   if(countcheck("socket", line, source))
     return CURL_SOCKET_BAD;
     return CURL_SOCKET_BAD;
 
 
-  sockfd = socket(domain, type, protocol);
+  sockfd = (socket)(domain, type, protocol);
 
 
   if(source && (sockfd != CURL_SOCKET_BAD))
   if(source && (sockfd != CURL_SOCKET_BAD))
     curl_dbg_log("FD %s:%d socket() = %" FMT_SOCKET_T "\n",
     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;
   SEND_TYPE_RETV rc;
   if(countcheck("send", line, source))
   if(countcheck("send", line, source))
     return -1;
     return -1;
-  rc = send(sockfd, buf, len, flags);
+  rc = (send)(sockfd, buf, len, flags);
   if(source)
   if(source)
     curl_dbg_log("SEND %s:%d send(%lu) = %ld\n",
     curl_dbg_log("SEND %s:%d send(%lu) = %ld\n",
                 source, line, (unsigned long)len, (long)rc);
                 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;
   RECV_TYPE_RETV rc;
   if(countcheck("recv", line, source))
   if(countcheck("recv", line, source))
     return -1;
     return -1;
-  rc = recv(sockfd, buf, len, flags);
+  rc = (recv)(sockfd, buf, len, flags);
   if(source)
   if(source)
     curl_dbg_log("RECV %s:%d recv(%lu) = %ld\n",
     curl_dbg_log("RECV %s:%d recv(%lu) = %ld\n",
                 source, line, (unsigned long)len, (long)rc);
                 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
 #ifdef HAVE_SOCKETPAIR
 int curl_dbg_socketpair(int domain, int type, int protocol,
 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))
   if(source && (0 == res))
     curl_dbg_log("FD %s:%d socketpair() = "
     curl_dbg_log("FD %s:%d socketpair() = "
@@ -364,12 +366,12 @@ int curl_dbg_socketpair(int domain, int type, int protocol,
 #endif
 #endif
 
 
 curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen,
 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;
   struct sockaddr *addr = (struct sockaddr *)saddr;
   curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen;
   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))
   if(source && (sockfd != CURL_SOCKET_BAD))
     curl_dbg_log("FD %s:%d accept() = %" FMT_SOCKET_T "\n",
     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;
   struct sockaddr *addr = (struct sockaddr *)saddr;
   curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen;
   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))
   if(source && (sockfd != CURL_SOCKET_BAD))
     curl_dbg_log("FD %s:%d accept() = %" FMT_SOCKET_T "\n",
     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 */
 /* 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 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);
   curl_dbg_mark_sclose(sockfd, line, source);
   return res;
   return res;
 }
 }
@@ -416,7 +418,12 @@ ALLOC_FUNC
 FILE *curl_dbg_fopen(const char *file, const char *mode,
 FILE *curl_dbg_fopen(const char *file, const char *mode,
                      int line, const char *source)
                      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)
   if(source)
     curl_dbg_log("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n",
     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,
 FILE *curl_dbg_fdopen(int filedes, const char *mode,
                       int line, const char *source)
                       int line, const char *source)
 {
 {
-  FILE *res = fdopen(filedes, mode);
+  FILE *res = (fdopen)(filedes, mode);
   if(source)
   if(source)
     curl_dbg_log("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n",
     curl_dbg_log("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n",
                  source, line, filedes, mode, (void *)res);
                  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",
     curl_dbg_log("FILE %s:%d fclose(%p)\n",
                  source, line, (void *)file);
                  source, line, (void *)file);
 
 
-  res = fclose(file);
+  res = (fclose)(file);
 
 
   return res;
   return res;
 }
 }
@@ -469,7 +476,7 @@ void curl_dbg_log(const char *format, ...)
     nchars = (int)sizeof(buf) - 1;
     nchars = (int)sizeof(buf) - 1;
 
 
   if(nchars > 0)
   if(nchars > 0)
-    fwrite(buf, 1, (size_t)nchars, curl_dbg_logfile);
+    (fwrite)(buf, 1, (size_t)nchars, curl_dbg_logfile);
 }
 }
 
 
 #endif /* CURLDEBUG */
 #endif /* CURLDEBUG */

部分文件因文件數量過多而無法顯示