Browse Source

curl 2024-05-22 (fd567d4f)

Code extracted from:

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

at commit fd567d4f06857f4fc8e2f64ea727b1318f76ad33 (curl-8_8_0).
Curl Upstream 1 year ago
parent
commit
9f46cdb65d
100 changed files with 2729 additions and 1931 deletions
  1. 1 1
      CMake/CurlTests.c
  2. 1 1
      CMake/FindNGHTTP2.cmake
  3. 5 1
      CMake/PickyWarnings.cmake
  4. 3 0
      CMake/Platforms/WindowsCache.cmake
  5. 106 25
      CMakeLists.txt
  6. 41 34
      include/curl/curl.h
  7. 3 3
      include/curl/curlver.h
  8. 14 0
      include/curl/multi.h
  9. 1 0
      include/curl/typecheck-gcc.h
  10. 3 0
      include/curl/urlapi.h
  11. 26 15
      lib/CMakeLists.txt
  12. 3 2
      lib/Makefile.inc
  13. 4 4
      lib/altsvc.c
  14. 3 3
      lib/asyn-ares.c
  15. 19 6
      lib/asyn-thread.c
  16. 1 1
      lib/base64.c
  17. 1 1
      lib/bufq.h
  18. 5 1
      lib/c-hyper.c
  19. 18 14
      lib/cf-h1-proxy.c
  20. 1 1
      lib/cf-h2-proxy.c
  21. 1 1
      lib/cf-haproxy.c
  22. 2 2
      lib/cf-https-connect.c
  23. 19 28
      lib/cf-socket.c
  24. 14 1
      lib/cfilters.c
  25. 10 1
      lib/cfilters.h
  26. 3 4
      lib/conncache.c
  27. 1 1
      lib/conncache.h
  28. 6 6
      lib/connect.c
  29. 34 7
      lib/content_encoding.c
  30. 2 1
      lib/cookie.c
  31. 9 9
      lib/curl_addrinfo.c
  32. 19 7
      lib/curl_config.h.cmake
  33. 1 1
      lib/curl_gethostname.c
  34. 0 17
      lib/curl_multibyte.c
  35. 0 500
      lib/curl_ntlm_wb.c
  36. 36 32
      lib/curl_path.c
  37. 1 1
      lib/curl_path.h
  38. 31 7
      lib/curl_rtmp.c
  39. 2 0
      lib/curl_rtmp.h
  40. 5 5
      lib/curl_sasl.c
  41. 48 13
      lib/curl_setup.h
  42. 2 6
      lib/curl_setup_once.h
  43. 29 23
      lib/curl_sha512_256.c
  44. 2 1
      lib/curl_threads.c
  45. 72 9
      lib/curl_trc.c
  46. 68 7
      lib/curl_trc.h
  47. 0 1
      lib/curlx.h
  48. 52 15
      lib/cw-out.c
  49. 1 1
      lib/cw-out.h
  50. 2 1
      lib/dict.c
  51. 81 0
      lib/dllmain.c
  52. 403 6
      lib/doh.c
  53. 38 3
      lib/doh.h
  54. 1 1
      lib/dynbuf.c
  55. 38 40
      lib/easy.c
  56. 2 1
      lib/easyoptions.c
  57. 76 34
      lib/file.c
  58. 121 102
      lib/ftp.c
  59. 1 1
      lib/ftplistparser.c
  60. 4 0
      lib/getinfo.c
  61. 4 2
      lib/gopher.c
  62. 30 27
      lib/hash.c
  63. 11 5
      lib/hash.h
  64. 5 5
      lib/headers.c
  65. 33 21
      lib/hostip.c
  66. 39 2
      lib/hostip.h
  67. 7 9
      lib/hsts.c
  68. 372 383
      lib/http.c
  69. 7 8
      lib/http.h
  70. 181 100
      lib/http2.c
  71. 1 4
      lib/http_aws_sigv4.c
  72. 16 1
      lib/http_chunks.c
  73. 20 5
      lib/http_negotiate.c
  74. 0 5
      lib/http_ntlm.c
  75. 1 1
      lib/http_proxy.c
  76. 68 18
      lib/idn.c
  77. 1 6
      lib/idn.h
  78. 11 11
      lib/if2ip.c
  79. 3 3
      lib/if2ip.h
  80. 4 2
      lib/imap.c
  81. 2 2
      lib/inet_ntop.c
  82. 2 2
      lib/inet_pton.c
  83. 31 25
      lib/krb5.c
  84. 8 3
      lib/ldap.c
  85. 16 0
      lib/llist.c
  86. 2 0
      lib/llist.h
  87. 2 1
      lib/md4.c
  88. 27 28
      lib/mime.c
  89. 20 20
      lib/mprintf.c
  90. 4 3
      lib/mqtt.c
  91. 248 174
      lib/multi.c
  92. 19 18
      lib/multihandle.h
  93. 4 3
      lib/multiif.h
  94. 1 1
      lib/noproxy.c
  95. 10 5
      lib/openldap.c
  96. 5 3
      lib/pop3.c
  97. 2 1
      lib/pop3.h
  98. 3 3
      lib/request.c
  99. 1 1
      lib/request.h
  100. 12 11
      lib/rtsp.c

+ 1 - 1
CMake/CurlTests.c

@@ -228,7 +228,7 @@ int main(void)
 #endif
 #endif
 int main(void)
 int main(void)
 {
 {
-  int flags = 0;
+  unsigned long flags = 0;
   if(0 != ioctlsocket(0, FIONBIO, &flags))
   if(0 != ioctlsocket(0, FIONBIO, &flags))
     return 1;
     return 1;
   ;
   ;

+ 1 - 1
CMake/FindNGHTTP2.cmake

@@ -25,7 +25,7 @@ include(FindPackageHandleStandardArgs)
 
 
 find_path(NGHTTP2_INCLUDE_DIR "nghttp2/nghttp2.h")
 find_path(NGHTTP2_INCLUDE_DIR "nghttp2/nghttp2.h")
 
 
-find_library(NGHTTP2_LIBRARY NAMES nghttp2)
+find_library(NGHTTP2_LIBRARY NAMES nghttp2 nghttp2_static)
 
 
 find_package_handle_standard_args(NGHTTP2
 find_package_handle_standard_args(NGHTTP2
     FOUND_VAR
     FOUND_VAR

+ 5 - 1
CMake/PickyWarnings.cmake

@@ -25,7 +25,11 @@ include(CheckCCompilerFlag)
 
 
 unset(WPICKY)
 unset(WPICKY)
 
 
-if(CURL_WERROR AND CMAKE_COMPILER_IS_GNUCC AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0)
+if(CURL_WERROR AND
+   ((CMAKE_COMPILER_IS_GNUCC AND
+     NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 5.0 AND
+     NOT CMAKE_VERSION VERSION_LESS 3.23.0) OR  # check_symbol_exists() incompatible with GCC -pedantic-errors in earlier CMake versions
+   CMAKE_C_COMPILER_ID MATCHES "Clang"))
   set(WPICKY "${WPICKY} -pedantic-errors")
   set(WPICKY "${WPICKY} -pedantic-errors")
 endif()
 endif()
 
 

+ 3 - 0
CMake/Platforms/WindowsCache.cmake

@@ -137,6 +137,9 @@ set(HAVE_TERMIOS_H 0)
 set(HAVE_TERMIO_H 0)
 set(HAVE_TERMIO_H 0)
 set(HAVE_UTIME_H 0)  # mingw-w64 has it (wrapper to sys/utime.h)
 set(HAVE_UTIME_H 0)  # mingw-w64 has it (wrapper to sys/utime.h)
 
 
+set(HAVE_DIRENT_H 0)
+set(HAVE_OPENDIR 0)
+
 set(HAVE_FSEEKO 0)
 set(HAVE_FSEEKO 0)
 set(HAVE__FSEEKI64 1)
 set(HAVE__FSEEKI64 1)
 set(HAVE_SOCKET 1)
 set(HAVE_SOCKET 1)

+ 106 - 25
CMakeLists.txt

@@ -37,6 +37,7 @@
 #   HAVE_GNUTLS_SRP: `gnutls_srp_verifier` present in GnuTLS
 #   HAVE_GNUTLS_SRP: `gnutls_srp_verifier` present in GnuTLS
 #   HAVE_SSL_CTX_SET_QUIC_METHOD: `SSL_CTX_set_quic_method` present in OpenSSL/wolfSSL
 #   HAVE_SSL_CTX_SET_QUIC_METHOD: `SSL_CTX_set_quic_method` present in OpenSSL/wolfSSL
 #   HAVE_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in QUICHE
 #   HAVE_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in QUICHE
+#   HAVE_ECH: ECH API checks for OpenSSL, BoringSSL or wolfSSL
 #
 #
 # For each of the above variables, if the variable is DEFINED (either
 # For each of the above variables, if the variable is DEFINED (either
 # to ON or OFF), the symbol detection will be skipped.  If the
 # to ON or OFF), the symbol detection will be skipped.  If the
@@ -303,10 +304,14 @@ if(ENABLE_IPV6 AND NOT WIN32)
     list(APPEND CURL_LIBS "-framework SystemConfiguration")
     list(APPEND CURL_LIBS "-framework SystemConfiguration")
   endif()
   endif()
 endif()
 endif()
+if(ENABLE_IPV6)
+  set(USE_IPV6 ON)
+endif()
 
 
 find_package(Perl)
 find_package(Perl)
 
 
 option(BUILD_LIBCURL_DOCS "to build libcurl man pages" ON)
 option(BUILD_LIBCURL_DOCS "to build libcurl man pages" ON)
+option(BUILD_MISC_DOCS "to build misc man pages (e.g. curl-config and mk-ca-bundle)" ON)
 option(ENABLE_CURL_MANUAL "to build the man page for curl and enable its -M/--manual option" ON)
 option(ENABLE_CURL_MANUAL "to build the man page for curl and enable its -M/--manual option" ON)
 
 
 if(ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS)
 if(ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS)
@@ -375,9 +380,6 @@ check_function_exists(gethostname HAVE_GETHOSTNAME)
 
 
 if(WIN32)
 if(WIN32)
   list(APPEND CURL_LIBS "ws2_32" "bcrypt")
   list(APPEND CURL_LIBS "ws2_32" "bcrypt")
-  if(USE_LIBRTMP)
-    list(APPEND CURL_LIBS "winmm")
-  endif()
 endif()
 endif()
 
 
 # check SSL libraries
 # check SSL libraries
@@ -436,7 +438,7 @@ if(CURL_USE_SECTRANSP)
 
 
   find_library(SECURITY_FRAMEWORK "Security")
   find_library(SECURITY_FRAMEWORK "Security")
   if(NOT SECURITY_FRAMEWORK)
   if(NOT SECURITY_FRAMEWORK)
-     message(FATAL_ERROR "Security framework not found")
+    message(FATAL_ERROR "Security framework not found")
   endif()
   endif()
 
 
   set(SSL_ENABLED ON)
   set(SSL_ENABLED ON)
@@ -453,10 +455,10 @@ if(use_core_foundation_and_core_services)
   find_library(CORESERVICES_FRAMEWORK "CoreServices")
   find_library(CORESERVICES_FRAMEWORK "CoreServices")
 
 
   if(NOT COREFOUNDATION_FRAMEWORK)
   if(NOT COREFOUNDATION_FRAMEWORK)
-      message(FATAL_ERROR "CoreFoundation framework not found")
+    message(FATAL_ERROR "CoreFoundation framework not found")
   endif()
   endif()
   if(NOT CORESERVICES_FRAMEWORK)
   if(NOT CORESERVICES_FRAMEWORK)
-      message(FATAL_ERROR "CoreServices framework not found")
+    message(FATAL_ERROR "CoreServices framework not found")
   endif()
   endif()
 
 
   list(APPEND CURL_LIBS "-framework CoreFoundation -framework CoreServices")
   list(APPEND CURL_LIBS "-framework CoreFoundation -framework CoreServices")
@@ -598,8 +600,8 @@ if(CURL_ZSTD)
   endif()
   endif()
 endif()
 endif()
 
 
-# Check symbol in OpenSSL-like TLS backends.
-macro(openssl_check_symbol_exists SYMBOL FILES VARIABLE)
+# Check symbol in an OpenSSL-like TLS backend, or in EXTRA_LIBS depending on it.
+macro(openssl_check_symbol_exists SYMBOL FILES VARIABLE EXTRA_LIBS)
   cmake_push_check_state()
   cmake_push_check_state()
   if(USE_OPENSSL)
   if(USE_OPENSSL)
     set(CMAKE_REQUIRED_INCLUDES   "${OPENSSL_INCLUDE_DIR}")
     set(CMAKE_REQUIRED_INCLUDES   "${OPENSSL_INCLUDE_DIR}")
@@ -623,6 +625,9 @@ macro(openssl_check_symbol_exists SYMBOL FILES VARIABLE)
     endif()
     endif()
     list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_UINTPTR_T)  # to pull in stdint.h (as of wolfSSL v5.5.4)
     list(APPEND CMAKE_REQUIRED_DEFINITIONS -DHAVE_UINTPTR_T)  # to pull in stdint.h (as of wolfSSL v5.5.4)
   endif()
   endif()
+  if(NOT "${EXTRA_LIBS}" STREQUAL "")
+    list(APPEND CMAKE_REQUIRED_LIBRARIES "${EXTRA_LIBS}")
+  endif()
   check_symbol_exists("${SYMBOL}" "${FILES}" "${VARIABLE}")
   check_symbol_exists("${SYMBOL}" "${FILES}" "${VARIABLE}")
   cmake_pop_check_state()
   cmake_pop_check_state()
 endmacro()
 endmacro()
@@ -631,9 +636,9 @@ endmacro()
 macro(openssl_check_quic)
 macro(openssl_check_quic)
   if(NOT DEFINED HAVE_SSL_CTX_SET_QUIC_METHOD)
   if(NOT DEFINED HAVE_SSL_CTX_SET_QUIC_METHOD)
     if(USE_OPENSSL)
     if(USE_OPENSSL)
-      openssl_check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD)
+      openssl_check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD "")
     elseif(USE_WOLFSSL)
     elseif(USE_WOLFSSL)
-      openssl_check_symbol_exists(wolfSSL_set_quic_method "wolfssl/options.h;wolfssl/openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD)
+      openssl_check_symbol_exists(wolfSSL_set_quic_method "wolfssl/options.h;wolfssl/openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD "")
     endif()
     endif()
   endif()
   endif()
   if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD)
   if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD)
@@ -643,10 +648,34 @@ endmacro()
 
 
 if(USE_OPENSSL OR USE_WOLFSSL)
 if(USE_OPENSSL OR USE_WOLFSSL)
   if(NOT DEFINED HAVE_SSL_SET0_WBIO)
   if(NOT DEFINED HAVE_SSL_SET0_WBIO)
-    openssl_check_symbol_exists(SSL_set0_wbio "openssl/ssl.h" HAVE_SSL_SET0_WBIO)
+    openssl_check_symbol_exists(SSL_set0_wbio "openssl/ssl.h" HAVE_SSL_SET0_WBIO "")
   endif()
   endif()
   if(NOT DEFINED HAVE_OPENSSL_SRP AND NOT CURL_DISABLE_SRP)
   if(NOT DEFINED HAVE_OPENSSL_SRP AND NOT CURL_DISABLE_SRP)
-    openssl_check_symbol_exists(SSL_CTX_set_srp_username "openssl/ssl.h" HAVE_OPENSSL_SRP)
+    openssl_check_symbol_exists(SSL_CTX_set_srp_username "openssl/ssl.h" HAVE_OPENSSL_SRP "")
+  endif()
+endif()
+
+option(USE_HTTPSRR "Enable HTTPS RR support for ECH (experimental)" OFF)
+option(USE_ECH "Enable ECH support" OFF)
+if(USE_ECH)
+  if(USE_OPENSSL OR USE_WOLFSSL)
+    # Be sure that the TLS library actually supports ECH.
+    if(NOT DEFINED HAVE_ECH)
+      if(USE_OPENSSL AND HAVE_BORINGSSL)
+        openssl_check_symbol_exists(SSL_set1_ech_config_list "openssl/ssl.h" HAVE_ECH "")
+      elseif(USE_OPENSSL)
+        openssl_check_symbol_exists(SSL_ech_set1_echconfig "openssl/ech.h" HAVE_ECH "")
+      elseif(USE_WOLFSSL)
+        openssl_check_symbol_exists(wolfSSL_CTX_GenerateEchConfig "wolfssl/options.h;wolfssl/ssl.h" HAVE_ECH "")
+      endif()
+    endif()
+    if(NOT HAVE_ECH)
+      message(FATAL_ERROR "ECH support missing in OpenSSL/BoringSSL/wolfSSL")
+    else()
+      message(STATUS "ECH enabled.")
+    endif()
+  else()
+    message(FATAL_ERROR "ECH requires ECH-enablded OpenSSL, BoringSSL or wolfSSL")
   endif()
   endif()
 endif()
 endif()
 
 
@@ -870,6 +899,21 @@ if(WIN32)
   endif()
   endif()
 endif()
 endif()
 
 
+if(APPLE)
+  option(USE_APPLE_IDN "Use Apple built-in IDN support" OFF)
+  if(USE_APPLE_IDN)
+    cmake_push_check_state()
+    set(CMAKE_REQUIRED_LIBRARIES "icucore")
+    check_symbol_exists("uidna_openUTS46" "unicode/uidna.h" HAVE_APPLE_IDN)
+    cmake_pop_check_state()
+    if(HAVE_APPLE_IDN)
+      list(APPEND CURL_LIBS "icucore")
+    else()
+      set(USE_APPLE_IDN OFF)
+    endif()
+  endif()
+endif()
+
 #libpsl
 #libpsl
 option(CURL_USE_LIBPSL "Use libPSL" ON)
 option(CURL_USE_LIBPSL "Use libPSL" ON)
 mark_as_advanced(CURL_USE_LIBPSL)
 mark_as_advanced(CURL_USE_LIBPSL)
@@ -966,7 +1010,6 @@ if(CURL_USE_GSSAPI)
     string(REPLACE ";" " " GSS_LINKER_FLAGS "${GSS_LINKER_FLAGS}")
     string(REPLACE ";" " " GSS_LINKER_FLAGS "${GSS_LINKER_FLAGS}")
     set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GSS_LINKER_FLAGS}")
     set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${GSS_LINKER_FLAGS}")
     set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GSS_LINKER_FLAGS}")
     set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GSS_LINKER_FLAGS}")
-    set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} ${GSS_LINKER_FLAGS}")
     list(APPEND CURL_LIBS ${GSS_LIBRARIES})
     list(APPEND CURL_LIBS ${GSS_LIBRARIES})
 
 
   else()
   else()
@@ -974,6 +1017,26 @@ if(CURL_USE_GSSAPI)
   endif()
   endif()
 endif()
 endif()
 
 
+option(USE_LIBRTMP "Enable librtmp from rtmpdump" OFF)
+if(USE_LIBRTMP)
+  cmake_push_check_state()
+  set(_extra_libs "rtmp")
+  if(WIN32)
+    list(APPEND _extra_libs "winmm")
+  endif()
+  openssl_check_symbol_exists("RTMP_Init" "librtmp/rtmp.h" HAVE_LIBRTMP "${_extra_libs}")
+  cmake_pop_check_state()
+  if(HAVE_LIBRTMP)
+    list(APPEND CURL_LIBS "rtmp")
+    if(WIN32)
+      list(APPEND CURL_LIBS "winmm")
+    endif()
+  else()
+    message(WARNING "librtmp requested, but not found or missing OpenSSL. Skipping.")
+    set(USE_LIBRTMP OFF)
+  endif()
+endif()
+
 option(ENABLE_UNIX_SOCKETS "Define if you want Unix domain sockets support" ON)
 option(ENABLE_UNIX_SOCKETS "Define if you want Unix domain sockets support" ON)
 if(ENABLE_UNIX_SOCKETS)
 if(ENABLE_UNIX_SOCKETS)
   include(CheckStructHasMember)
   include(CheckStructHasMember)
@@ -1125,6 +1188,7 @@ check_include_file_concat("sys/un.h"         HAVE_SYS_UN_H)
 check_include_file_concat("sys/utime.h"      HAVE_SYS_UTIME_H)
 check_include_file_concat("sys/utime.h"      HAVE_SYS_UTIME_H)
 check_include_file_concat("sys/xattr.h"      HAVE_SYS_XATTR_H)
 check_include_file_concat("sys/xattr.h"      HAVE_SYS_XATTR_H)
 check_include_file_concat("arpa/inet.h"      HAVE_ARPA_INET_H)
 check_include_file_concat("arpa/inet.h"      HAVE_ARPA_INET_H)
+check_include_file_concat("dirent.h"         HAVE_DIRENT_H)
 check_include_file_concat("fcntl.h"          HAVE_FCNTL_H)
 check_include_file_concat("fcntl.h"          HAVE_FCNTL_H)
 check_include_file_concat("ifaddrs.h"        HAVE_IFADDRS_H)
 check_include_file_concat("ifaddrs.h"        HAVE_IFADDRS_H)
 check_include_file_concat("io.h"             HAVE_IO_H)
 check_include_file_concat("io.h"             HAVE_IO_H)
@@ -1187,6 +1251,7 @@ endif()
 
 
 check_symbol_exists(fnmatch       "${CURL_INCLUDES};fnmatch.h" HAVE_FNMATCH)
 check_symbol_exists(fnmatch       "${CURL_INCLUDES};fnmatch.h" HAVE_FNMATCH)
 check_symbol_exists(basename      "${CURL_INCLUDES};string.h" HAVE_BASENAME)
 check_symbol_exists(basename      "${CURL_INCLUDES};string.h" HAVE_BASENAME)
+check_symbol_exists(opendir       "${CURL_INCLUDES};dirent.h" HAVE_OPENDIR)
 check_symbol_exists(socket        "${CURL_INCLUDES}" HAVE_SOCKET)
 check_symbol_exists(socket        "${CURL_INCLUDES}" HAVE_SOCKET)
 check_symbol_exists(sched_yield   "${CURL_INCLUDES};sched.h" HAVE_SCHED_YIELD)
 check_symbol_exists(sched_yield   "${CURL_INCLUDES};sched.h" HAVE_SCHED_YIELD)
 check_symbol_exists(socketpair    "${CURL_INCLUDES}" HAVE_SOCKETPAIR)
 check_symbol_exists(socketpair    "${CURL_INCLUDES}" HAVE_SOCKETPAIR)
@@ -1228,7 +1293,6 @@ check_symbol_exists(getifaddrs     "${CURL_INCLUDES};stdlib.h" HAVE_GETIFADDRS)
 check_symbol_exists(freeaddrinfo   "${CURL_INCLUDES}" HAVE_FREEADDRINFO)
 check_symbol_exists(freeaddrinfo   "${CURL_INCLUDES}" HAVE_FREEADDRINFO)
 check_symbol_exists(pipe           "${CURL_INCLUDES}" HAVE_PIPE)
 check_symbol_exists(pipe           "${CURL_INCLUDES}" HAVE_PIPE)
 check_symbol_exists(ftruncate      "${CURL_INCLUDES}" HAVE_FTRUNCATE)
 check_symbol_exists(ftruncate      "${CURL_INCLUDES}" HAVE_FTRUNCATE)
-check_symbol_exists(fseeko         "${CURL_INCLUDES};stdio.h" HAVE_FSEEKO)
 check_symbol_exists(_fseeki64      "${CURL_INCLUDES};stdio.h" HAVE__FSEEKI64)
 check_symbol_exists(_fseeki64      "${CURL_INCLUDES};stdio.h" HAVE__FSEEKI64)
 check_symbol_exists(getpeername    "${CURL_INCLUDES}" HAVE_GETPEERNAME)
 check_symbol_exists(getpeername    "${CURL_INCLUDES}" HAVE_GETPEERNAME)
 check_symbol_exists(getsockname    "${CURL_INCLUDES}" HAVE_GETSOCKNAME)
 check_symbol_exists(getsockname    "${CURL_INCLUDES}" HAVE_GETSOCKNAME)
@@ -1238,10 +1302,6 @@ check_symbol_exists(setlocale      "${CURL_INCLUDES}" HAVE_SETLOCALE)
 check_symbol_exists(setmode        "${CURL_INCLUDES}" HAVE_SETMODE)
 check_symbol_exists(setmode        "${CURL_INCLUDES}" HAVE_SETMODE)
 check_symbol_exists(setrlimit      "${CURL_INCLUDES}" HAVE_SETRLIMIT)
 check_symbol_exists(setrlimit      "${CURL_INCLUDES}" HAVE_SETRLIMIT)
 
 
-if(HAVE_FSEEKO)
-  set(HAVE_DECL_FSEEKO 1)
-endif()
-
 if(NOT MSVC OR (MSVC_VERSION GREATER_EQUAL 1900))
 if(NOT MSVC OR (MSVC_VERSION GREATER_EQUAL 1900))
   # earlier MSVC compilers had faulty snprintf implementations
   # earlier MSVC compilers had faulty snprintf implementations
   check_symbol_exists(snprintf       "stdio.h" HAVE_SNPRINTF)
   check_symbol_exists(snprintf       "stdio.h" HAVE_SNPRINTF)
@@ -1304,6 +1364,15 @@ if(HAVE_FILE_OFFSET_BITS)
 endif()
 endif()
 check_type_size("off_t"  SIZEOF_OFF_T)
 check_type_size("off_t"  SIZEOF_OFF_T)
 
 
+# fseeko may not exist with _FILE_OFFSET_BITS=64 but can exist with
+# _FILE_OFFSET_BITS unset or 32 (e.g. Android ARMv7 with NDK 26b and API level < 24)
+# so we need to test fseeko after testing for _FILE_OFFSET_BITS
+check_symbol_exists(fseeko         "${CURL_INCLUDES};stdio.h" HAVE_FSEEKO)
+
+if(HAVE_FSEEKO)
+  set(HAVE_DECL_FSEEKO 1)
+endif()
+
 # include this header to get the type
 # include this header to get the type
 set(CMAKE_REQUIRED_INCLUDES "${CURL_SOURCE_DIR}/include")
 set(CMAKE_REQUIRED_INCLUDES "${CURL_SOURCE_DIR}/include")
 set(CMAKE_EXTRA_INCLUDE_FILES "curl/system.h")
 set(CMAKE_EXTRA_INCLUDE_FILES "curl/system.h")
@@ -1495,6 +1564,10 @@ set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
 set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
 set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
 set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
 set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
 
 
+cmake_dependent_option(BUILD_TESTING "Build tests"
+  ON "PERL_FOUND;NOT CURL_DISABLE_TESTS"
+  OFF)
+
 if(HAVE_MANUAL_TOOLS)
 if(HAVE_MANUAL_TOOLS)
   add_subdirectory(docs)
   add_subdirectory(docs)
 endif()
 endif()
@@ -1505,15 +1578,24 @@ if(BUILD_CURL_EXE)
   add_subdirectory(src)
   add_subdirectory(src)
 endif()
 endif()
 
 
-cmake_dependent_option(BUILD_TESTING "Build tests"
-  ON "PERL_FOUND;NOT CURL_DISABLE_TESTS"
-  OFF)
+option(BUILD_EXAMPLES "Build libcurl examples" OFF)
+if(BUILD_EXAMPLES)
+  add_subdirectory(docs/examples)
+endif()
+
 if(BUILD_TESTING)
 if(BUILD_TESTING)
   add_subdirectory(tests)
   add_subdirectory(tests)
 endif()
 endif()
 
 
 if(NOT CURL_DISABLE_INSTALL)
 if(NOT CURL_DISABLE_INSTALL)
 
 
+  install(FILES "${PROJECT_SOURCE_DIR}/scripts/mk-ca-bundle.pl"
+          DESTINATION ${CMAKE_INSTALL_BINDIR}
+          PERMISSIONS
+            OWNER_READ OWNER_WRITE OWNER_EXECUTE
+            GROUP_READ GROUP_EXECUTE
+            WORLD_READ WORLD_EXECUTE)
+
   # Helper to populate a list (_items) with a label when conditions (the remaining
   # Helper to populate a list (_items) with a label when conditions (the remaining
   # args) are satisfied
   # args) are satisfied
   macro(_add_if label)
   macro(_add_if label)
@@ -1538,7 +1620,7 @@ if(NOT CURL_DISABLE_INSTALL)
   _add_if("brotli"        HAVE_BROTLI)
   _add_if("brotli"        HAVE_BROTLI)
   _add_if("zstd"          HAVE_ZSTD)
   _add_if("zstd"          HAVE_ZSTD)
   _add_if("AsynchDNS"     USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32)
   _add_if("AsynchDNS"     USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32)
-  _add_if("IDN"           HAVE_LIBIDN2 OR USE_WIN32_IDN)
+  _add_if("IDN"           HAVE_LIBIDN2 OR USE_WIN32_IDN OR USE_APPLE_IDN)
   _add_if("Largefile"     (SIZEOF_CURL_OFF_T GREATER 4) AND
   _add_if("Largefile"     (SIZEOF_CURL_OFF_T GREATER 4) AND
                           ((SIZEOF_OFF_T GREATER 4) OR USE_WIN32_LARGE_FILES))
                           ((SIZEOF_OFF_T GREATER 4) OR USE_WIN32_LARGE_FILES))
   _add_if("SSPI"          USE_WINDOWS_SSPI)
   _add_if("SSPI"          USE_WINDOWS_SSPI)
@@ -1551,9 +1633,6 @@ if(NOT CURL_DISABLE_INSTALL)
                           (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
                           (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
   _add_if("NTLM"          NOT (CURL_DISABLE_NTLM) AND
   _add_if("NTLM"          NOT (CURL_DISABLE_NTLM) AND
                           (use_curl_ntlm_core OR USE_WINDOWS_SSPI))
                           (use_curl_ntlm_core OR USE_WINDOWS_SSPI))
-  _add_if("NTLM_WB"       NOT (CURL_DISABLE_NTLM) AND
-                          (use_curl_ntlm_core OR USE_WINDOWS_SSPI) AND
-                          NOT CURL_DISABLE_HTTP AND NTLM_WB_ENABLED)
   _add_if("TLS-SRP"       USE_TLS_SRP)
   _add_if("TLS-SRP"       USE_TLS_SRP)
   _add_if("HTTP2"         USE_NGHTTP2)
   _add_if("HTTP2"         USE_NGHTTP2)
   _add_if("HTTP3"         USE_NGTCP2 OR USE_QUICHE OR USE_OPENSSL_QUIC)
   _add_if("HTTP3"         USE_NGTCP2 OR USE_QUICHE OR USE_OPENSSL_QUIC)
@@ -1576,6 +1655,8 @@ if(NOT CURL_DISABLE_INSTALL)
   _add_if("IPFS"          NOT CURL_DISABLE_HTTP)
   _add_if("IPFS"          NOT CURL_DISABLE_HTTP)
   _add_if("IPNS"          NOT CURL_DISABLE_HTTP)
   _add_if("IPNS"          NOT CURL_DISABLE_HTTP)
   _add_if("HTTPS"         NOT CURL_DISABLE_HTTP AND SSL_ENABLED)
   _add_if("HTTPS"         NOT CURL_DISABLE_HTTP AND SSL_ENABLED)
+  _add_if("ECH"           HAVE_ECH)
+  _add_if("HTTPSRR"       HAVE_ECH)
   _add_if("FTP"           NOT CURL_DISABLE_FTP)
   _add_if("FTP"           NOT CURL_DISABLE_FTP)
   _add_if("FTPS"          NOT CURL_DISABLE_FTP AND SSL_ENABLED)
   _add_if("FTPS"          NOT CURL_DISABLE_FTP AND SSL_ENABLED)
   _add_if("FILE"          NOT CURL_DISABLE_FILE)
   _add_if("FILE"          NOT CURL_DISABLE_FILE)

+ 41 - 34
include/curl/curl.h

@@ -632,6 +632,7 @@ typedef enum {
   CURLE_SSL_CLIENTCERT,          /* 98 - client-side certificate required */
   CURLE_SSL_CLIENTCERT,          /* 98 - client-side certificate required */
   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 */
   CURL_LAST /* never use! */
   CURL_LAST /* never use! */
 } CURLcode;
 } CURLcode;
 
 
@@ -811,7 +812,10 @@ typedef enum {
 #define CURLAUTH_GSSAPI CURLAUTH_NEGOTIATE
 #define CURLAUTH_GSSAPI CURLAUTH_NEGOTIATE
 #define CURLAUTH_NTLM         (((unsigned long)1)<<3)
 #define CURLAUTH_NTLM         (((unsigned long)1)<<3)
 #define CURLAUTH_DIGEST_IE    (((unsigned long)1)<<4)
 #define CURLAUTH_DIGEST_IE    (((unsigned long)1)<<4)
+#ifndef CURL_NO_OLDIES
+  /* functionality removed since 8.8.0 */
 #define CURLAUTH_NTLM_WB      (((unsigned long)1)<<5)
 #define CURLAUTH_NTLM_WB      (((unsigned long)1)<<5)
+#endif
 #define CURLAUTH_BEARER       (((unsigned long)1)<<6)
 #define CURLAUTH_BEARER       (((unsigned long)1)<<6)
 #define CURLAUTH_AWS_SIGV4    (((unsigned long)1)<<7)
 #define CURLAUTH_AWS_SIGV4    (((unsigned long)1)<<7)
 #define CURLAUTH_ONLY         (((unsigned long)1)<<31)
 #define CURLAUTH_ONLY         (((unsigned long)1)<<31)
@@ -2206,6 +2210,9 @@ typedef enum {
   /* millisecond version */
   /* millisecond version */
   CURLOPT(CURLOPT_SERVER_RESPONSE_TIMEOUT_MS, CURLOPTTYPE_LONG, 324),
   CURLOPT(CURLOPT_SERVER_RESPONSE_TIMEOUT_MS, CURLOPTTYPE_LONG, 324),
 
 
+  /* set ECH configuration  */
+  CURLOPT(CURLOPT_ECH, CURLOPTTYPE_STRINGPOINT, 325),
+
   CURLOPT_LASTENTRY /* the last unused */
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 } CURLoption;
 
 
@@ -2311,30 +2318,26 @@ enum CURL_NETRC_OPTION {
   CURL_NETRC_LAST
   CURL_NETRC_LAST
 };
 };
 
 
-enum {
-  CURL_SSLVERSION_DEFAULT,
-  CURL_SSLVERSION_TLSv1, /* TLS 1.x */
-  CURL_SSLVERSION_SSLv2,
-  CURL_SSLVERSION_SSLv3,
-  CURL_SSLVERSION_TLSv1_0,
-  CURL_SSLVERSION_TLSv1_1,
-  CURL_SSLVERSION_TLSv1_2,
-  CURL_SSLVERSION_TLSv1_3,
-
-  CURL_SSLVERSION_LAST /* never use, keep last */
-};
+#define CURL_SSLVERSION_DEFAULT 0
+#define CURL_SSLVERSION_TLSv1   1 /* TLS 1.x */
+#define CURL_SSLVERSION_SSLv2   2
+#define CURL_SSLVERSION_SSLv3   3
+#define CURL_SSLVERSION_TLSv1_0 4
+#define CURL_SSLVERSION_TLSv1_1 5
+#define CURL_SSLVERSION_TLSv1_2 6
+#define CURL_SSLVERSION_TLSv1_3 7
 
 
-enum {
-  CURL_SSLVERSION_MAX_NONE =     0,
-  CURL_SSLVERSION_MAX_DEFAULT =  (CURL_SSLVERSION_TLSv1   << 16),
-  CURL_SSLVERSION_MAX_TLSv1_0 =  (CURL_SSLVERSION_TLSv1_0 << 16),
-  CURL_SSLVERSION_MAX_TLSv1_1 =  (CURL_SSLVERSION_TLSv1_1 << 16),
-  CURL_SSLVERSION_MAX_TLSv1_2 =  (CURL_SSLVERSION_TLSv1_2 << 16),
-  CURL_SSLVERSION_MAX_TLSv1_3 =  (CURL_SSLVERSION_TLSv1_3 << 16),
+#define CURL_SSLVERSION_LAST 8 /* never use, keep last */
+
+#define CURL_SSLVERSION_MAX_NONE 0
+#define CURL_SSLVERSION_MAX_DEFAULT (CURL_SSLVERSION_TLSv1   << 16)
+#define CURL_SSLVERSION_MAX_TLSv1_0 (CURL_SSLVERSION_TLSv1_0 << 16)
+#define CURL_SSLVERSION_MAX_TLSv1_1 (CURL_SSLVERSION_TLSv1_1 << 16)
+#define CURL_SSLVERSION_MAX_TLSv1_2 (CURL_SSLVERSION_TLSv1_2 << 16)
+#define CURL_SSLVERSION_MAX_TLSv1_3 (CURL_SSLVERSION_TLSv1_3 << 16)
 
 
   /* never use, keep last */
   /* never use, keep last */
-  CURL_SSLVERSION_MAX_LAST =     (CURL_SSLVERSION_LAST    << 16)
-};
+#define CURL_SSLVERSION_MAX_LAST    (CURL_SSLVERSION_LAST    << 16)
 
 
 enum CURL_TLSAUTH {
 enum CURL_TLSAUTH {
   CURL_TLSAUTH_NONE,
   CURL_TLSAUTH_NONE,
@@ -3035,17 +3038,18 @@ CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *share);
  */
  */
 
 
 typedef enum {
 typedef enum {
-  CURLVERSION_FIRST,
-  CURLVERSION_SECOND,
-  CURLVERSION_THIRD,
-  CURLVERSION_FOURTH,
-  CURLVERSION_FIFTH,
-  CURLVERSION_SIXTH,
-  CURLVERSION_SEVENTH,
-  CURLVERSION_EIGHTH,
-  CURLVERSION_NINTH,
-  CURLVERSION_TENTH,
-  CURLVERSION_ELEVENTH,
+  CURLVERSION_FIRST,    /* 7.10 */
+  CURLVERSION_SECOND,   /* 7.11.1 */
+  CURLVERSION_THIRD,    /* 7.12.0 */
+  CURLVERSION_FOURTH,   /* 7.16.1 */
+  CURLVERSION_FIFTH,    /* 7.57.0 */
+  CURLVERSION_SIXTH,    /* 7.66.0 */
+  CURLVERSION_SEVENTH,  /* 7.70.0 */
+  CURLVERSION_EIGHTH,   /* 7.72.0 */
+  CURLVERSION_NINTH,    /* 7.75.0 */
+  CURLVERSION_TENTH,    /* 7.77.0 */
+  CURLVERSION_ELEVENTH, /* 7.87.0 */
+  CURLVERSION_TWELFTH,  /* 8.8.0 */
   CURLVERSION_LAST /* never actually use this */
   CURLVERSION_LAST /* never actually use this */
 } CURLversion;
 } CURLversion;
 
 
@@ -3054,7 +3058,7 @@ typedef enum {
    meant to be a built-in version number for what kind of struct the caller
    meant to be a built-in version number for what kind of struct the caller
    expects. If the struct ever changes, we redefine the NOW to another enum
    expects. If the struct ever changes, we redefine the NOW to another enum
    from above. */
    from above. */
-#define CURLVERSION_NOW CURLVERSION_ELEVENTH
+#define CURLVERSION_NOW CURLVERSION_TWELFTH
 
 
 struct curl_version_info_data {
 struct curl_version_info_data {
   CURLversion age;          /* age of the returned struct */
   CURLversion age;          /* age of the returned struct */
@@ -3114,6 +3118,9 @@ struct curl_version_info_data {
   /* These fields were added in CURLVERSION_ELEVENTH */
   /* These fields were added in CURLVERSION_ELEVENTH */
   /* feature_names is terminated by an entry with a NULL feature name */
   /* feature_names is terminated by an entry with a NULL feature name */
   const char * const *feature_names;
   const char * const *feature_names;
+
+  /* These fields were added in CURLVERSION_TWELFTH */
+  const char *rtmp_version; /* human readable string. */
 };
 };
 typedef struct curl_version_info_data curl_version_info_data;
 typedef struct curl_version_info_data curl_version_info_data;
 
 
@@ -3154,7 +3161,7 @@ typedef struct curl_version_info_data curl_version_info_data;
 #define CURL_VERSION_GSASL        (1<<29) /* libgsasl is supported */
 #define CURL_VERSION_GSASL        (1<<29) /* libgsasl is supported */
 #define CURL_VERSION_THREADSAFE   (1<<30) /* libcurl API is thread-safe */
 #define CURL_VERSION_THREADSAFE   (1<<30) /* libcurl API is thread-safe */
 
 
- /*
+/*
  * NAME curl_version_info()
  * NAME curl_version_info()
  *
  *
  * DESCRIPTION
  * DESCRIPTION

+ 3 - 3
include/curl/curlver.h

@@ -32,12 +32,12 @@
 
 
 /* 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.7.0-DEV"
+#define LIBCURL_VERSION "8.8.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 7
+#define LIBCURL_VERSION_MINOR 8
 #define LIBCURL_VERSION_PATCH 0
 #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
@@ -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 0x080700
+#define LIBCURL_VERSION_NUM 0x080800
 
 
 /*
 /*
  * 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

+ 14 - 0
include/curl/multi.h

@@ -464,6 +464,20 @@ typedef int (*curl_push_callback)(CURL *parent,
                                   struct curl_pushheaders *headers,
                                   struct curl_pushheaders *headers,
                                   void *userp);
                                   void *userp);
 
 
+/*
+ * Name:    curl_multi_waitfds()
+ *
+ * Desc:    Ask curl for fds for polling. The app can use these to poll on.
+ *          We want curl_multi_perform() called as soon as one of them are
+ *          ready. Passing zero size allows to get just a number of fds.
+ *
+ * Returns: CURLMcode type, general multi error code.
+ */
+CURL_EXTERN CURLMcode curl_multi_waitfds(CURLM *multi,
+                                         struct curl_waitfd *ufds,
+                                         unsigned int size,
+                                         unsigned int *fd_count);
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 } /* end of extern "C" */
 } /* end of extern "C" */
 #endif
 #endif

+ 1 - 0
include/curl/typecheck-gcc.h

@@ -275,6 +275,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
    (option) == CURLOPT_DNS_LOCAL_IP6 ||                                       \
    (option) == CURLOPT_DNS_LOCAL_IP6 ||                                       \
    (option) == CURLOPT_DNS_SERVERS ||                                         \
    (option) == CURLOPT_DNS_SERVERS ||                                         \
    (option) == CURLOPT_DOH_URL ||                                             \
    (option) == CURLOPT_DOH_URL ||                                             \
+   (option) == CURLOPT_ECH        ||                                          \
    (option) == CURLOPT_EGDSOCKET ||                                           \
    (option) == CURLOPT_EGDSOCKET ||                                           \
    (option) == CURLOPT_FTP_ACCOUNT ||                                         \
    (option) == CURLOPT_FTP_ACCOUNT ||                                         \
    (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER ||                             \
    (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER ||                             \

+ 3 - 0
include/curl/urlapi.h

@@ -99,6 +99,9 @@ typedef enum {
 #define CURLU_ALLOW_SPACE (1<<11)       /* Allow spaces in the URL */
 #define CURLU_ALLOW_SPACE (1<<11)       /* Allow spaces in the URL */
 #define CURLU_PUNYCODE (1<<12)          /* get the host name in punycode */
 #define CURLU_PUNYCODE (1<<12)          /* get the host name in punycode */
 #define CURLU_PUNY2IDN (1<<13)          /* punycode => IDN conversion */
 #define CURLU_PUNY2IDN (1<<13)          /* punycode => IDN conversion */
+#define CURLU_GET_EMPTY (1<<14)         /* allow empty queries and fragments
+                                           when extracting the URL or the
+                                           components */
 
 
 typedef struct Curl_URL CURLU;
 typedef struct Curl_URL CURLU;
 
 

+ 26 - 15
lib/CMakeLists.txt

@@ -31,9 +31,10 @@ configure_file(curl_config.h.cmake
 transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
 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)
 
 
-list(APPEND HHEADERS
-  ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h
-  )
+# DllMain is added later for DLL builds only.
+list(REMOVE_ITEM CSOURCES dllmain.c)
+
+list(APPEND HHEADERS ${CMAKE_CURRENT_BINARY_DIR}/curl_config.h)
 
 
 # The rest of the build
 # The rest of the build
 
 
@@ -55,6 +56,7 @@ if(BUILD_TESTING)
     ${HHEADERS} ${CSOURCES}
     ${HHEADERS} ${CSOURCES}
   )
   )
   target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB)
   target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB)
+  target_link_libraries(curlu PRIVATE ${CURL_LIBS})
 endif()
 endif()
 
 
 if(ENABLE_CURLDEBUG)
 if(ENABLE_CURLDEBUG)
@@ -63,10 +65,6 @@ if(ENABLE_CURLDEBUG)
   set_source_files_properties(memdebug.c curl_multibyte.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
   set_source_files_properties(memdebug.c curl_multibyte.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
 endif()
 endif()
 
 
-if(BUILD_TESTING)
-  target_link_libraries(curlu PRIVATE ${CURL_LIBS})
-endif()
-
 transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")
 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)
 
 
@@ -114,17 +112,17 @@ if(NOT DEFINED SHARE_LIB_OBJECT)
   endif()
   endif()
 endif()
 endif()
 
 
-if(WIN32)
-  # Define CURL_STATICLIB always, to disable __declspec(dllexport) for exported
-  # libcurl symbols. We handle exports via libcurl.def instead. Except with
-  # symbol hiding disabled or debug mode enabled, when we export _all_ symbols
-  # from libcurl DLL, without using libcurl.def.
-  add_definitions("-DCURL_STATICLIB")
-endif()
-
 if(SHARE_LIB_OBJECT)
 if(SHARE_LIB_OBJECT)
   set(LIB_OBJECT "libcurl_object")
   set(LIB_OBJECT "libcurl_object")
   add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES})
   add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES})
+  if(WIN32)
+    # Define CURL_STATICLIB always, to disable __declspec(dllexport) for
+    # exported libcurl symbols. We handle exports via libcurl.def instead.
+    # Except with symbol hiding disabled or debug mode enabled, when we export
+    # _all_ symbols from libcurl DLL, without using libcurl.def.
+    set_property(TARGET ${LIB_OBJECT} APPEND
+      PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB")
+  endif()
   target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS})
   target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS})
   set_target_properties(${LIB_OBJECT} PROPERTIES
   set_target_properties(${LIB_OBJECT} PROPERTIES
     POSITION_INDEPENDENT_CODE ON)
     POSITION_INDEPENDENT_CODE ON)
@@ -152,6 +150,10 @@ if(BUILD_STATIC_LIBS)
   list(APPEND libcurl_export ${LIB_STATIC})
   list(APPEND libcurl_export ${LIB_STATIC})
   add_library(${LIB_STATIC} STATIC ${LIB_SOURCE})
   add_library(${LIB_STATIC} STATIC ${LIB_SOURCE})
   add_library(${PROJECT_NAME}::${LIB_STATIC} ALIAS ${LIB_STATIC})
   add_library(${PROJECT_NAME}::${LIB_STATIC} ALIAS ${LIB_STATIC})
+  if(WIN32)
+    set_property(TARGET ${LIB_OBJECT} APPEND
+      PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB")
+  endif()
   target_link_libraries(${LIB_STATIC} PRIVATE ${CURL_LIBS})
   target_link_libraries(${LIB_STATIC} PRIVATE ${CURL_LIBS})
   # Remove the "lib" prefix since the library is already named "libcurl".
   # Remove the "lib" prefix since the library is already named "libcurl".
   set_target_properties(${LIB_STATIC} PROPERTIES
   set_target_properties(${LIB_STATIC} PROPERTIES
@@ -181,6 +183,15 @@ if(BUILD_SHARED_LIBS)
   list(APPEND libcurl_export ${LIB_SHARED})
   list(APPEND libcurl_export ${LIB_SHARED})
   add_library(${LIB_SHARED} SHARED ${LIB_SOURCE})
   add_library(${LIB_SHARED} SHARED ${LIB_SOURCE})
   add_library(${PROJECT_NAME}::${LIB_SHARED} ALIAS ${LIB_SHARED})
   add_library(${PROJECT_NAME}::${LIB_SHARED} ALIAS ${LIB_SHARED})
+  if(WIN32 OR CYGWIN)
+    if(CYGWIN)
+      # For cygwin always compile dllmain.c as a separate unit since it
+      # includes windows.h, which shouldn't be included in other units.
+      set_source_files_properties(dllmain.c PROPERTIES
+        SKIP_UNITY_BUILD_INCLUSION ON)
+    endif()
+    set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES dllmain.c)
+  endif()
   if(WIN32)
   if(WIN32)
     set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES libcurl.rc)
     set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES libcurl.rc)
     if(HIDES_CURL_PRIVATE_SYMBOLS)
     if(HIDES_CURL_PRIVATE_SYMBOLS)

+ 3 - 2
lib/Makefile.inc

@@ -44,6 +44,7 @@ LIB_VAUTH_HFILES =      \
 
 
 LIB_VTLS_CFILES =           \
 LIB_VTLS_CFILES =           \
   vtls/bearssl.c            \
   vtls/bearssl.c            \
+  vtls/cipher_suite.c       \
   vtls/gtls.c               \
   vtls/gtls.c               \
   vtls/hostcheck.c          \
   vtls/hostcheck.c          \
   vtls/keylog.c             \
   vtls/keylog.c             \
@@ -60,6 +61,7 @@ LIB_VTLS_CFILES =           \
 
 
 LIB_VTLS_HFILES =           \
 LIB_VTLS_HFILES =           \
   vtls/bearssl.h            \
   vtls/bearssl.h            \
+  vtls/cipher_suite.h       \
   vtls/gtls.h               \
   vtls/gtls.h               \
   vtls/hostcheck.h          \
   vtls/hostcheck.h          \
   vtls/keylog.h             \
   vtls/keylog.h             \
@@ -129,7 +131,6 @@ LIB_CFILES =         \
   curl_memrchr.c     \
   curl_memrchr.c     \
   curl_multibyte.c   \
   curl_multibyte.c   \
   curl_ntlm_core.c   \
   curl_ntlm_core.c   \
-  curl_ntlm_wb.c     \
   curl_path.c        \
   curl_path.c        \
   curl_range.c       \
   curl_range.c       \
   curl_rtmp.c        \
   curl_rtmp.c        \
@@ -140,6 +141,7 @@ LIB_CFILES =         \
   curl_trc.c         \
   curl_trc.c         \
   cw-out.c           \
   cw-out.c           \
   dict.c             \
   dict.c             \
+  dllmain.c          \
   doh.c              \
   doh.c              \
   dynbuf.c           \
   dynbuf.c           \
   dynhds.c           \
   dynhds.c           \
@@ -271,7 +273,6 @@ LIB_HFILES =         \
   curl_memrchr.h     \
   curl_memrchr.h     \
   curl_multibyte.h   \
   curl_multibyte.h   \
   curl_ntlm_core.h   \
   curl_ntlm_core.h   \
-  curl_ntlm_wb.h     \
   curl_path.h        \
   curl_path.h        \
   curl_printf.h      \
   curl_printf.h      \
   curl_range.h       \
   curl_range.h       \

+ 4 - 4
lib/altsvc.c

@@ -191,7 +191,7 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, char *line)
       as->expires = expires;
       as->expires = expires;
       as->prio = prio;
       as->prio = prio;
       as->persist = persist ? 1 : 0;
       as->persist = persist ? 1 : 0;
-      Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node);
+      Curl_llist_append(&asi->list, as, &as->node);
     }
     }
   }
   }
 
 
@@ -252,7 +252,7 @@ static CURLcode altsvc_out(struct altsvc *as, FILE *fp)
   CURLcode result = Curl_gmtime(as->expires, &stamp);
   CURLcode result = Curl_gmtime(as->expires, &stamp);
   if(result)
   if(result)
     return result;
     return result;
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   else {
   else {
     char ipv6_unused[16];
     char ipv6_unused[16];
     if(1 == Curl_inet_pton(AF_INET6, as->dst.host, ipv6_unused)) {
     if(1 == Curl_inet_pton(AF_INET6, as->dst.host, ipv6_unused)) {
@@ -303,7 +303,7 @@ struct altsvcinfo *Curl_altsvc_init(void)
 #ifdef USE_HTTP2
 #ifdef USE_HTTP2
     | CURLALTSVC_H2
     | CURLALTSVC_H2
 #endif
 #endif
-#ifdef ENABLE_QUIC
+#ifdef USE_HTTP3
     | CURLALTSVC_H3
     | CURLALTSVC_H3
 #endif
 #endif
     ;
     ;
@@ -643,7 +643,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
                account. [See RFC 7838 section 3.1] */
                account. [See RFC 7838 section 3.1] */
             as->expires = maxage + time(NULL);
             as->expires = maxage + time(NULL);
             as->persist = persist;
             as->persist = persist;
-            Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node);
+            Curl_llist_append(&asi->list, as, &as->node);
             infof(data, "Added alt-svc: %s:%d over %s", dsthost, dstport,
             infof(data, "Added alt-svc: %s:%d over %s", dsthost, dstport,
                   Curl_alpnid2str(dstalpnid));
                   Curl_alpnid2str(dstalpnid));
           }
           }

+ 3 - 3
lib/asyn-ares.c

@@ -539,7 +539,7 @@ static void compound_results(struct thread_data *res,
   if(!ai)
   if(!ai)
     return;
     return;
 
 
-#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
+#ifdef USE_IPV6 /* CURLRES_IPV6 */
   if(res->temp_ai && res->temp_ai->ai_family == PF_INET6) {
   if(res->temp_ai && res->temp_ai->ai_family == PF_INET6) {
     /* We have results already, put the new IPv6 entries at the head of the
     /* We have results already, put the new IPv6 entries at the head of the
        list. */
        list. */
@@ -684,7 +684,7 @@ static struct Curl_addrinfo *ares2addr(struct ares_addrinfo_node *node)
     /* settle family-specific sockaddr structure size.  */
     /* settle family-specific sockaddr structure size.  */
     if(ai->ai_family == AF_INET)
     if(ai->ai_family == AF_INET)
       ss_size = sizeof(struct sockaddr_in);
       ss_size = sizeof(struct sockaddr_in);
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
     else if(ai->ai_family == AF_INET6)
     else if(ai->ai_family == AF_INET6)
       ss_size = sizeof(struct sockaddr_in6);
       ss_size = sizeof(struct sockaddr_in6);
 #endif
 #endif
@@ -932,7 +932,7 @@ CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
                                 const char *local_ip6)
                                 const char *local_ip6)
 {
 {
-#if defined(HAVE_CARES_SET_LOCAL) && defined(ENABLE_IPV6)
+#if defined(HAVE_CARES_SET_LOCAL) && defined(USE_IPV6)
   unsigned char a6[INET6_ADDRSTRLEN];
   unsigned char a6[INET6_ADDRSTRLEN];
 
 
   if((!local_ip6) || (local_ip6[0] == 0)) {
   if((!local_ip6) || (local_ip6[0] == 0)) {

+ 19 - 6
lib/asyn-thread.c

@@ -325,7 +325,7 @@ query_complete(DWORD err, DWORD bytes, LPWSAOVERLAPPED overlapped)
       /* settle family-specific sockaddr structure size.  */
       /* settle family-specific sockaddr structure size.  */
       if(ai->ai_family == AF_INET)
       if(ai->ai_family == AF_INET)
         ss_size = sizeof(struct sockaddr_in);
         ss_size = sizeof(struct sockaddr_in);
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
       else if(ai->ai_family == AF_INET6)
       else if(ai->ai_family == AF_INET6)
         ss_size = sizeof(struct sockaddr_in6);
         ss_size = sizeof(struct sockaddr_in6);
 #endif
 #endif
@@ -444,7 +444,7 @@ query_complete(DWORD err, DWORD bytes, LPWSAOVERLAPPED overlapped)
 /*
 /*
  * getaddrinfo_thread() resolves a name and then exits.
  * getaddrinfo_thread() resolves a name and then exits.
  *
  *
- * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
+ * For builds without ARES, but with USE_IPV6, create a resolver thread
  * and wait on it.
  * and wait on it.
  */
  */
 static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
 static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
@@ -554,11 +554,15 @@ static void destroy_async_data(struct Curl_async *async)
 
 
     if(!done) {
     if(!done) {
 #ifdef _WIN32
 #ifdef _WIN32
-      if(td->complete_ev)
+      if(td->complete_ev) {
         CloseHandle(td->complete_ev);
         CloseHandle(td->complete_ev);
-      else
+        td->complete_ev = NULL;
+      }
 #endif
 #endif
-      Curl_thread_destroy(td->thread_hnd);
+      if(td->thread_hnd != curl_thread_t_null) {
+        Curl_thread_destroy(td->thread_hnd);
+        td->thread_hnd = curl_thread_t_null;
+      }
     }
     }
     else {
     else {
 #ifdef _WIN32
 #ifdef _WIN32
@@ -566,6 +570,7 @@ static void destroy_async_data(struct Curl_async *async)
         Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev);
         Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev);
         WaitForSingleObject(td->complete_ev, INFINITE);
         WaitForSingleObject(td->complete_ev, INFINITE);
         CloseHandle(td->complete_ev);
         CloseHandle(td->complete_ev);
+        td->complete_ev = NULL;
       }
       }
 #endif
 #endif
       if(td->thread_hnd != curl_thread_t_null)
       if(td->thread_hnd != curl_thread_t_null)
@@ -672,7 +677,7 @@ static bool init_resolve_thread(struct Curl_easy *data,
   td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
   td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
 #endif
 #endif
 
 
-  if(!td->thread_hnd) {
+  if(td->thread_hnd == curl_thread_t_null) {
     /* The thread never started, so mark it as done here for proper cleanup. */
     /* The thread never started, so mark it as done here for proper cleanup. */
     td->tsd.done = 1;
     td->tsd.done = 1;
     err = errno;
     err = errno;
@@ -713,6 +718,7 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data,
   if(td->complete_ev) {
   if(td->complete_ev) {
     WaitForSingleObject(td->complete_ev, INFINITE);
     WaitForSingleObject(td->complete_ev, INFINITE);
     CloseHandle(td->complete_ev);
     CloseHandle(td->complete_ev);
+    td->complete_ev = NULL;
     if(entry)
     if(entry)
       result = getaddrinfo_complete(data);
       result = getaddrinfo_complete(data);
   }
   }
@@ -754,6 +760,13 @@ void Curl_resolver_kill(struct Curl_easy *data)
   /* If we're still resolving, we must wait for the threads to fully clean up,
   /* If we're still resolving, we must wait for the threads to fully clean up,
      unfortunately.  Otherwise, we can simply cancel to clean up any resolver
      unfortunately.  Otherwise, we can simply cancel to clean up any resolver
      data. */
      data. */
+#ifdef _WIN32
+  if(td && td->complete_ev) {
+    Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev);
+    (void)thread_wait_resolv(data, NULL, FALSE);
+  }
+  else
+#endif
   if(td && td->thread_hnd != curl_thread_t_null
   if(td && td->thread_hnd != curl_thread_t_null
      && (data->set.quick_exit != 1L))
      && (data->set.quick_exit != 1L))
     (void)thread_wait_resolv(data, NULL, FALSE);
     (void)thread_wait_resolv(data, NULL, FALSE);

+ 1 - 1
lib/base64.c

@@ -243,7 +243,7 @@ static CURLcode base64_encode(const char *table64,
   *outptr = base64data;
   *outptr = base64data;
 
 
   /* Return the length of the new data */
   /* Return the length of the new data */
-  *outlen = output - base64data;
+  *outlen = (size_t)(output - base64data);
 
 
   return CURLE_OK;
   return CURLE_OK;
 }
 }

+ 1 - 1
lib/bufq.h

@@ -85,7 +85,7 @@ void Curl_bufcp_free(struct bufc_pool *pool);
  * preferably never fail (except for memory exhaustion).
  * preferably never fail (except for memory exhaustion).
  *
  *
  * By default and without a pool, a bufq will keep chunks that read
  * By default and without a pool, a bufq will keep chunks that read
- * read empty in its `spare` list. Option `BUFQ_OPT_NO_SPARES` will
+ * empty in its `spare` list. Option `BUFQ_OPT_NO_SPARES` will
  * disable that and free chunks once they become empty.
  * disable that and free chunks once they become empty.
  *
  *
  * When providing a pool to a bufq, all chunk creation and spare handling
  * When providing a pool to a bufq, all chunk creation and spare handling

+ 5 - 1
lib/c-hyper.c

@@ -171,7 +171,7 @@ static int hyper_each_header(void *userdata,
   len = Curl_dyn_len(&data->state.headerb);
   len = Curl_dyn_len(&data->state.headerb);
   headp = Curl_dyn_ptr(&data->state.headerb);
   headp = Curl_dyn_ptr(&data->state.headerb);
 
 
-  result = Curl_http_header(data, data->conn, headp, len);
+  result = Curl_http_header(data, headp, len);
   if(result) {
   if(result) {
     data->state.hresult = result;
     data->state.hresult = result;
     return HYPER_ITER_BREAK;
     return HYPER_ITER_BREAK;
@@ -980,11 +980,13 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
       goto error;
       goto error;
   }
   }
 
 
+#ifndef CURL_DISABLE_PROXY
   if(data->state.aptr.proxyuserpwd) {
   if(data->state.aptr.proxyuserpwd) {
     result = Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd);
     result = Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd);
     if(result)
     if(result)
       goto error;
       goto error;
   }
   }
+#endif
 
 
   if(data->state.aptr.userpwd) {
   if(data->state.aptr.userpwd) {
     result = Curl_hyper_header(data, headers, data->state.aptr.userpwd);
     result = Curl_hyper_header(data, headers, data->state.aptr.userpwd);
@@ -1137,7 +1139,9 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   /* clear userpwd and proxyuserpwd to avoid reusing old credentials
   /* clear userpwd and proxyuserpwd to avoid reusing old credentials
    * from reused connections */
    * from reused connections */
   Curl_safefree(data->state.aptr.userpwd);
   Curl_safefree(data->state.aptr.userpwd);
+#ifndef CURL_DISABLE_PROXY
   Curl_safefree(data->state.aptr.proxyuserpwd);
   Curl_safefree(data->state.aptr.proxyuserpwd);
+#endif
   return CURLE_OK;
   return CURLE_OK;
 error:
 error:
   DEBUGASSERT(result);
   DEBUGASSERT(result);

+ 18 - 14
lib/cf-h1-proxy.c

@@ -195,14 +195,16 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf,
 static void tunnel_free(struct Curl_cfilter *cf,
 static void tunnel_free(struct Curl_cfilter *cf,
                         struct Curl_easy *data)
                         struct Curl_easy *data)
 {
 {
-  struct h1_tunnel_state *ts = cf->ctx;
-  if(ts) {
-    h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
-    Curl_dyn_free(&ts->rcvbuf);
-    Curl_dyn_free(&ts->request_data);
-    Curl_httpchunk_free(data, &ts->ch);
-    free(ts);
-    cf->ctx = NULL;
+  if(cf) {
+    struct h1_tunnel_state *ts = cf->ctx;
+    if(ts) {
+      h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
+      Curl_dyn_free(&ts->rcvbuf);
+      Curl_dyn_free(&ts->request_data);
+      Curl_httpchunk_free(data, &ts->ch);
+      free(ts);
+      cf->ctx = NULL;
+    }
   }
   }
 }
 }
 
 
@@ -1057,18 +1059,20 @@ static void cf_h1_proxy_close(struct Curl_cfilter *cf,
                               struct Curl_easy *data)
                               struct Curl_easy *data)
 {
 {
   CURL_TRC_CF(data, cf, "close");
   CURL_TRC_CF(data, cf, "close");
-  cf->connected = FALSE;
-  if(cf->ctx) {
-    h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data);
+  if(cf) {
+    cf->connected = FALSE;
+    if(cf->ctx) {
+      h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data);
+    }
+    if(cf->next)
+      cf->next->cft->do_close(cf->next, data);
   }
   }
-  if(cf->next)
-    cf->next->cft->do_close(cf->next, data);
 }
 }
 
 
 
 
 struct Curl_cftype Curl_cft_h1_proxy = {
 struct Curl_cftype Curl_cft_h1_proxy = {
   "H1-PROXY",
   "H1-PROXY",
-  CF_TYPE_IP_CONNECT,
+  CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
   0,
   0,
   cf_h1_proxy_destroy,
   cf_h1_proxy_destroy,
   cf_h1_proxy_connect,
   cf_h1_proxy_connect,

+ 1 - 1
lib/cf-h2-proxy.c

@@ -1532,7 +1532,7 @@ static bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf,
 
 
 struct Curl_cftype Curl_cft_h2_proxy = {
 struct Curl_cftype Curl_cft_h2_proxy = {
   "H2-PROXY",
   "H2-PROXY",
-  CF_TYPE_IP_CONNECT,
+  CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
   CURL_LOG_LVL_NONE,
   CURL_LOG_LVL_NONE,
   cf_h2_proxy_destroy,
   cf_h2_proxy_destroy,
   cf_h2_proxy_connect,
   cf_h2_proxy_connect,

+ 1 - 1
lib/cf-haproxy.c

@@ -189,7 +189,7 @@ static void cf_haproxy_adjust_pollset(struct Curl_cfilter *cf,
 
 
 struct Curl_cftype Curl_cft_haproxy = {
 struct Curl_cftype Curl_cft_haproxy = {
   "HAPROXY",
   "HAPROXY",
-  0,
+  CF_TYPE_PROXY,
   0,
   0,
   cf_haproxy_destroy,
   cf_haproxy_destroy,
   cf_haproxy_connect,
   cf_haproxy_connect,

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

@@ -102,8 +102,8 @@ struct cf_hc_ctx {
   CURLcode result;          /* overall result */
   CURLcode result;          /* overall result */
   struct cf_hc_baller h3_baller;
   struct cf_hc_baller h3_baller;
   struct cf_hc_baller h21_baller;
   struct cf_hc_baller h21_baller;
-  int soft_eyeballs_timeout_ms;
-  int hard_eyeballs_timeout_ms;
+  unsigned int soft_eyeballs_timeout_ms;
+  unsigned int hard_eyeballs_timeout_ms;
 };
 };
 
 
 static void cf_hc_baller_init(struct cf_hc_baller *b,
 static void cf_hc_baller_init(struct cf_hc_baller *b,

+ 19 - 28
lib/cf-socket.c

@@ -81,7 +81,7 @@
 #include "memdebug.h"
 #include "memdebug.h"
 
 
 
 
-#if defined(ENABLE_IPV6) && defined(IPV6_V6ONLY) && defined(_WIN32)
+#if defined(USE_IPV6) && defined(IPV6_V6ONLY) && defined(_WIN32)
 /* It makes support for IPv4-mapped IPv6 addresses.
 /* It makes support for IPv4-mapped IPv6 addresses.
  * Linux kernel, NetBSD, FreeBSD and Darwin: default is off;
  * Linux kernel, NetBSD, FreeBSD and Darwin: default is off;
  * Windows Vista and later: default is on;
  * Windows Vista and later: default is on;
@@ -287,7 +287,7 @@ static CURLcode socket_open(struct Curl_easy *data,
     /* no socket, no connection */
     /* no socket, no connection */
     return CURLE_COULDNT_CONNECT;
     return CURLE_COULDNT_CONNECT;
 
 
-#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
+#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
   if(data->conn->scope_id && (addr->family == AF_INET6)) {
   if(data->conn->scope_id && (addr->family == AF_INET6)) {
     struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
     struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
     sa6->sin6_scope_id = data->conn->scope_id;
     sa6->sin6_scope_id = data->conn->scope_id;
@@ -405,7 +405,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
   struct sockaddr *sock = (struct sockaddr *)&sa;  /* bind to this address */
   struct sockaddr *sock = (struct sockaddr *)&sa;  /* bind to this address */
   curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
   curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
   struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
   struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
   struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
 #endif
 #endif
 
 
@@ -419,7 +419,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
 #ifdef IP_BIND_ADDRESS_NO_PORT
 #ifdef IP_BIND_ADDRESS_NO_PORT
   int on = 1;
   int on = 1;
 #endif
 #endif
-#ifndef ENABLE_IPV6
+#ifndef USE_IPV6
   (void)scope;
   (void)scope;
 #endif
 #endif
 
 
@@ -475,7 +475,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
 #endif
 #endif
 
 
       switch(Curl_if2ip(af,
       switch(Curl_if2ip(af,
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
                         scope, conn->scope_id,
                         scope, conn->scope_id,
 #endif
 #endif
                         dev, myhost, sizeof(myhost))) {
                         dev, myhost, sizeof(myhost))) {
@@ -514,7 +514,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
 
 
       if(af == AF_INET)
       if(af == AF_INET)
         conn->ip_version = CURL_IPRESOLVE_V4;
         conn->ip_version = CURL_IPRESOLVE_V4;
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
       else if(af == AF_INET6)
       else if(af == AF_INET6)
         conn->ip_version = CURL_IPRESOLVE_V6;
         conn->ip_version = CURL_IPRESOLVE_V6;
 #endif
 #endif
@@ -547,7 +547,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
     }
     }
 
 
     if(done > 0) {
     if(done > 0) {
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
       /* IPv6 address */
       /* IPv6 address */
       if(af == AF_INET6) {
       if(af == AF_INET6) {
 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
@@ -596,7 +596,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
   }
   }
   else {
   else {
     /* no device was given, prepare sa to match af's needs */
     /* no device was given, prepare sa to match af's needs */
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
     if(af == AF_INET6) {
     if(af == AF_INET6) {
       si6->sin6_family = AF_INET6;
       si6->sin6_family = AF_INET6;
       si6->sin6_port = htons(port);
       si6->sin6_port = htons(port);
@@ -616,16 +616,6 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
   for(;;) {
   for(;;) {
     if(bind(sockfd, sock, sizeof_sa) >= 0) {
     if(bind(sockfd, sock, sizeof_sa) >= 0) {
       /* we succeeded to bind */
       /* we succeeded to bind */
-      struct Curl_sockaddr_storage add;
-      curl_socklen_t size = sizeof(add);
-      memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
-      if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
-        char buffer[STRERROR_LEN];
-        data->state.os_errno = error = SOCKERRNO;
-        failf(data, "getsockname() failed with errno %d: %s",
-              error, Curl_strerror(error, buffer, sizeof(buffer)));
-        return CURLE_INTERFACE_FAILED;
-      }
       infof(data, "Local port: %hu", port);
       infof(data, "Local port: %hu", port);
       conn->bits.bound = TRUE;
       conn->bits.bound = TRUE;
       return CURLE_OK;
       return CURLE_OK;
@@ -639,7 +629,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
       /* We reuse/clobber the port variable here below */
       /* We reuse/clobber the port variable here below */
       if(sock->sa_family == AF_INET)
       if(sock->sa_family == AF_INET)
         si4->sin_port = ntohs(port);
         si4->sin_port = ntohs(port);
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
       else
       else
         si6->sin6_port = ntohs(port);
         si6->sin6_port = ntohs(port);
 #endif
 #endif
@@ -923,7 +913,8 @@ static CURLcode set_local_ip(struct Curl_cfilter *cf,
   struct cf_socket_ctx *ctx = cf->ctx;
   struct cf_socket_ctx *ctx = cf->ctx;
 
 
 #ifdef HAVE_GETSOCKNAME
 #ifdef HAVE_GETSOCKNAME
-  if(!(data->conn->handler->protocol & CURLPROTO_TFTP)) {
+  if((ctx->sock != CURL_SOCKET_BAD) &&
+     !(data->conn->handler->protocol & CURLPROTO_TFTP)) {
     /* TFTP does not connect, so it cannot get the IP like this */
     /* TFTP does not connect, so it cannot get the IP like this */
 
 
     char buffer[STRERROR_LEN];
     char buffer[STRERROR_LEN];
@@ -946,8 +937,8 @@ static CURLcode set_local_ip(struct Curl_cfilter *cf,
   }
   }
 #else
 #else
   (void)data;
   (void)data;
-  ctx->l_ip[0] = 0;
-  ctx->l_port = -1;
+  ctx->ip.local_ip[0] = 0;
+  ctx->ip.local_port = -1;
 #endif
 #endif
   return CURLE_OK;
   return CURLE_OK;
 }
 }
@@ -991,7 +982,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
   if(result)
   if(result)
     goto out;
     goto out;
 
 
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   if(ctx->addr.family == AF_INET6) {
   if(ctx->addr.family == AF_INET6) {
     set_ipv6_v6only(ctx->sock, 0);
     set_ipv6_v6only(ctx->sock, 0);
     infof(data, "  Trying [%s]:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
     infof(data, "  Trying [%s]:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
@@ -1000,7 +991,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
 #endif
 #endif
     infof(data, "  Trying %s:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
     infof(data, "  Trying %s:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
 
 
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   is_tcp = (ctx->addr.family == AF_INET
   is_tcp = (ctx->addr.family == AF_INET
             || ctx->addr.family == AF_INET6) &&
             || ctx->addr.family == AF_INET6) &&
            ctx->addr.socktype == SOCK_STREAM;
            ctx->addr.socktype == SOCK_STREAM;
@@ -1037,7 +1028,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
 #ifndef CURL_DISABLE_BINDLOCAL
 #ifndef CURL_DISABLE_BINDLOCAL
   /* possibly bind the local end to an IP, interface or port */
   /* possibly bind the local end to an IP, interface or port */
   if(ctx->addr.family == AF_INET
   if(ctx->addr.family == AF_INET
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
      || ctx->addr.family == AF_INET6
      || ctx->addr.family == AF_INET6
 #endif
 #endif
     ) {
     ) {
@@ -1288,7 +1279,7 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
 #ifdef DEBUGBUILD
 #ifdef DEBUGBUILD
   /* simulate network blocking/partial writes */
   /* simulate network blocking/partial writes */
   if(ctx->wblock_percent > 0) {
   if(ctx->wblock_percent > 0) {
-    unsigned char c;
+    unsigned char c = 0;
     Curl_rand(data, &c, 1);
     Curl_rand(data, &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);
@@ -1366,7 +1357,7 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
 #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) {
-    unsigned char c;
+    unsigned char c = 0;
     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);
@@ -1449,7 +1440,7 @@ static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
   /* the first socket info gets some specials */
   /* the first socket info gets some specials */
   if(cf->sockindex == FIRSTSOCKET) {
   if(cf->sockindex == FIRSTSOCKET) {
     cf->conn->remote_addr = &ctx->addr;
     cf->conn->remote_addr = &ctx->addr;
-  #ifdef ENABLE_IPV6
+  #ifdef USE_IPV6
     cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
     cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
   #endif
   #endif
     Curl_persistconninfo(data, cf->conn, &ctx->ip);
     Curl_persistconninfo(data, cf->conn, &ctx->ip);

+ 14 - 1
lib/cfilters.c

@@ -590,7 +590,7 @@ CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data)
 
 
 /**
 /**
  * Notify connection filters that the transfer represented by `data`
  * Notify connection filters that the transfer represented by `data`
- * is donw with sending data (e.g. has uploaded everything).
+ * is done with sending data (e.g. has uploaded everything).
  */
  */
 void Curl_conn_ev_data_done_send(struct Curl_easy *data)
 void Curl_conn_ev_data_done_send(struct Curl_easy *data)
 {
 {
@@ -670,6 +670,19 @@ size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
   return (result || n <= 0)? 1 : (size_t)n;
   return (result || n <= 0)? 1 : (size_t)n;
 }
 }
 
 
+int Curl_conn_get_stream_error(struct Curl_easy *data,
+                               struct connectdata *conn,
+                               int sockindex)
+{
+  CURLcode result;
+  int n = 0;
+
+  struct Curl_cfilter *cf = conn->cfilter[sockindex];
+  result = cf? cf->cft->query(cf, data, CF_QUERY_STREAM_ERROR,
+                              &n, NULL) : CURLE_UNKNOWN_OPTION;
+  return (result || n < 0)? 0 : n;
+}
+
 int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd)
 int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd)
 {
 {
   if(data && data->conn &&
   if(data && data->conn &&

+ 10 - 1
lib/cfilters.h

@@ -160,6 +160,7 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
 #define CF_QUERY_SOCKET             3  /* -          curl_socket_t */
 #define CF_QUERY_SOCKET             3  /* -          curl_socket_t */
 #define CF_QUERY_TIMER_CONNECT      4  /* -          struct curltime */
 #define CF_QUERY_TIMER_CONNECT      4  /* -          struct curltime */
 #define CF_QUERY_TIMER_APPCONNECT   5  /* -          struct curltime */
 #define CF_QUERY_TIMER_APPCONNECT   5  /* -          struct curltime */
+#define CF_QUERY_STREAM_ERROR       6  /* error code - */
 
 
 /**
 /**
  * Query the cfilter for properties. Filters ignorant of a query will
  * Query the cfilter for properties. Filters ignorant of a query will
@@ -178,10 +179,12 @@ typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf,
  *                     connection, etc.
  *                     connection, etc.
  * CF_TYPE_SSL:        provide SSL/TLS
  * CF_TYPE_SSL:        provide SSL/TLS
  * CF_TYPE_MULTIPLEX:  provides multiplexing of easy handles
  * CF_TYPE_MULTIPLEX:  provides multiplexing of easy handles
+ * CF_TYPE_PROXY       provides proxying
  */
  */
 #define CF_TYPE_IP_CONNECT  (1 << 0)
 #define CF_TYPE_IP_CONNECT  (1 << 0)
 #define CF_TYPE_SSL         (1 << 1)
 #define CF_TYPE_SSL         (1 << 1)
 #define CF_TYPE_MULTIPLEX   (1 << 2)
 #define CF_TYPE_MULTIPLEX   (1 << 2)
+#define CF_TYPE_PROXY       (1 << 3)
 
 
 /* A connection filter type, e.g. specific implementation. */
 /* A connection filter type, e.g. specific implementation. */
 struct Curl_cftype {
 struct Curl_cftype {
@@ -449,7 +452,7 @@ CURLcode Curl_conn_ev_data_idle(struct Curl_easy *data);
 
 
 /**
 /**
  * Notify connection filters that the transfer represented by `data`
  * Notify connection filters that the transfer represented by `data`
- * is donw with sending data (e.g. has uploaded everything).
+ * is done with sending data (e.g. has uploaded everything).
  */
  */
 void Curl_conn_ev_data_done_send(struct Curl_easy *data);
 void Curl_conn_ev_data_done_send(struct Curl_easy *data);
 
 
@@ -496,6 +499,12 @@ size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
                                     struct connectdata *conn,
                                     struct connectdata *conn,
                                     int sockindex);
                                     int sockindex);
 
 
+/**
+ * Get the underlying error code for a transfer stream or 0 if not known.
+ */
+int Curl_conn_get_stream_error(struct Curl_easy *data,
+                               struct connectdata *conn,
+                               int sockindex);
 
 
 /**
 /**
  * Get the index of the given socket in the connection's sockets.
  * Get the index of the given socket in the connection's sockets.

+ 3 - 4
lib/conncache.c

@@ -68,8 +68,7 @@ static void bundle_destroy(struct connectbundle *bundle)
 static void bundle_add_conn(struct connectbundle *bundle,
 static void bundle_add_conn(struct connectbundle *bundle,
                             struct connectdata *conn)
                             struct connectdata *conn)
 {
 {
-  Curl_llist_insert_next(&bundle->conn_list, bundle->conn_list.tail, conn,
-                         &conn->bundle_node);
+  Curl_llist_append(&bundle->conn_list, conn, &conn->bundle_node);
   conn->bundle = bundle;
   conn->bundle = bundle;
   bundle->num_connections++;
   bundle->num_connections++;
 }
 }
@@ -101,7 +100,7 @@ static void free_bundle_hash_entry(void *freethis)
   bundle_destroy(b);
   bundle_destroy(b);
 }
 }
 
 
-int Curl_conncache_init(struct conncache *connc, int size)
+int Curl_conncache_init(struct conncache *connc, size_t size)
 {
 {
   /* allocate a new easy handle to use when closing cached connections */
   /* allocate a new easy handle to use when closing cached connections */
   connc->closure_handle = curl_easy_init();
   connc->closure_handle = curl_easy_init();
@@ -141,7 +140,7 @@ static void hashkey(struct connectdata *conn, char *buf, size_t len)
     hostname = conn->host.name;
     hostname = conn->host.name;
 
 
   /* put the numbers first so that the hostname gets cut off if too long */
   /* put the numbers first so that the hostname gets cut off if too long */
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   msnprintf(buf, len, "%u/%ld/%s", conn->scope_id, port, hostname);
   msnprintf(buf, len, "%u/%ld/%s", conn->scope_id, port, hostname);
 #else
 #else
   msnprintf(buf, len, "%ld/%s", port, hostname);
   msnprintf(buf, len, "%ld/%s", port, hostname);

+ 1 - 1
lib/conncache.h

@@ -85,7 +85,7 @@ struct connectbundle {
 };
 };
 
 
 /* returns 1 on error, 0 is fine */
 /* returns 1 on error, 0 is fine */
-int Curl_conncache_init(struct conncache *, int size);
+int Curl_conncache_init(struct conncache *, size_t size);
 void Curl_conncache_destroy(struct conncache *connc);
 void Curl_conncache_destroy(struct conncache *connc);
 
 
 /* return the correct bundle, to a host or a proxy */
 /* return the correct bundle, to a host or a proxy */

+ 6 - 6
lib/connect.c

@@ -195,7 +195,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
                       char *addr, int *port)
                       char *addr, int *port)
 {
 {
   struct sockaddr_in *si = NULL;
   struct sockaddr_in *si = NULL;
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   struct sockaddr_in6 *si6 = NULL;
   struct sockaddr_in6 *si6 = NULL;
 #endif
 #endif
 #if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
 #if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
@@ -214,7 +214,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
         return TRUE;
         return TRUE;
       }
       }
       break;
       break;
-#ifdef ENABLE_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,
       if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr,
@@ -401,7 +401,7 @@ static CURLcode eyeballer_new(struct eyeballer **pballer,
     return CURLE_OUT_OF_MEMORY;
     return CURLE_OUT_OF_MEMORY;
 
 
   baller->name = ((ai_family == AF_INET)? "ipv4" : (
   baller->name = ((ai_family == AF_INET)? "ipv4" : (
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
                   (ai_family == AF_INET6)? "ipv6" :
                   (ai_family == AF_INET6)? "ipv6" :
 #endif
 #endif
                   "ip"));
                   "ip"));
@@ -779,7 +779,7 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
     /* any IP version is allowed */
     /* any IP version is allowed */
     ai_family0 = remotehost->addr?
     ai_family0 = remotehost->addr?
       remotehost->addr->ai_family : 0;
       remotehost->addr->ai_family : 0;
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
     ai_family1 = ai_family0 == AF_INET6 ?
     ai_family1 = ai_family0 == AF_INET6 ?
       AF_INET : AF_INET6;
       AF_INET : AF_INET6;
 #else
 #else
@@ -790,7 +790,7 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
     /* only one IP version is allowed */
     /* only one IP version is allowed */
     ai_family0 = (conn->ip_version == CURL_IPRESOLVE_V4) ?
     ai_family0 = (conn->ip_version == CURL_IPRESOLVE_V4) ?
       AF_INET :
       AF_INET :
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
       AF_INET6;
       AF_INET6;
 #else
 #else
       AF_UNSPEC;
       AF_UNSPEC;
@@ -1117,7 +1117,7 @@ const
 #endif
 #endif
 struct transport_provider transport_providers[] = {
 struct transport_provider transport_providers[] = {
   { TRNSPRT_TCP, Curl_cf_tcp_create },
   { TRNSPRT_TCP, Curl_cf_tcp_create },
-#ifdef ENABLE_QUIC
+#ifdef USE_HTTP3
   { TRNSPRT_QUIC, Curl_cf_quic_create },
   { TRNSPRT_QUIC, Curl_cf_quic_create },
 #endif
 #endif
 #ifndef CURL_DISABLE_TFTP
 #ifndef CURL_DISABLE_TFTP

+ 34 - 7
lib/content_encoding.c

@@ -300,7 +300,7 @@ static CURLcode deflate_do_write(struct Curl_easy *data,
   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 */
 
 
-  if(!(type & CLIENTWRITE_BODY))
+  if(!(type & CLIENTWRITE_BODY) || !nbytes)
     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
 
 
   /* Set the compressed input when this function is called */
   /* Set the compressed input when this function is called */
@@ -457,7 +457,7 @@ static CURLcode gzip_do_write(struct Curl_easy *data,
   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 */
 
 
-  if(!(type & CLIENTWRITE_BODY))
+  if(!(type & CLIENTWRITE_BODY) || !nbytes)
     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
 
 
   if(zp->zlib_init == ZLIB_INIT_GZIP) {
   if(zp->zlib_init == ZLIB_INIT_GZIP) {
@@ -669,7 +669,7 @@ static CURLcode brotli_do_write(struct Curl_easy *data,
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
   BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
   BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
 
 
-  if(!(type & CLIENTWRITE_BODY))
+  if(!(type & CLIENTWRITE_BODY) || !nbytes)
     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
 
 
   if(!bp->br)
   if(!bp->br)
@@ -762,7 +762,7 @@ static CURLcode zstd_do_write(struct Curl_easy *data,
   ZSTD_outBuffer out;
   ZSTD_outBuffer out;
   size_t errorCode;
   size_t errorCode;
 
 
-  if(!(type & CLIENTWRITE_BODY))
+  if(!(type & CLIENTWRITE_BODY) || !nbytes)
     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
 
 
   if(!zp->decomp) {
   if(!zp->decomp) {
@@ -916,7 +916,7 @@ static CURLcode error_do_write(struct Curl_easy *data,
   (void) buf;
   (void) buf;
   (void) nbytes;
   (void) nbytes;
 
 
-  if(!(type & CLIENTWRITE_BODY))
+  if(!(type & CLIENTWRITE_BODY) || !nbytes)
     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
     return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
 
 
   failf(data, "Unrecognized content encoding type. "
   failf(data, "Unrecognized content encoding type. "
@@ -978,6 +978,7 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
   do {
   do {
     const char *name;
     const char *name;
     size_t namelen;
     size_t namelen;
+    bool is_chunked = FALSE;
 
 
     /* Parse a single encoding name. */
     /* Parse a single encoding name. */
     while(ISBLANK(*enclist) || *enclist == ',')
     while(ISBLANK(*enclist) || *enclist == ',')
@@ -993,10 +994,11 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
       const struct Curl_cwtype *cwt;
       const struct Curl_cwtype *cwt;
       struct Curl_cwriter *writer;
       struct Curl_cwriter *writer;
 
 
+      is_chunked = (is_transfer && (namelen == 7) &&
+                    strncasecompare(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 &&
-          (namelen != 7 || !strncasecompare(name, "chunked", 7))) ||
+      if((is_transfer && !data->set.http_transfer_encoding && !is_chunked) ||
          (!is_transfer && data->set.http_ce_skip)) {
          (!is_transfer && data->set.http_ce_skip)) {
         /* not requested, ignore */
         /* not requested, ignore */
         return CURLE_OK;
         return CURLE_OK;
@@ -1009,6 +1011,31 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
       }
       }
 
 
       cwt = find_unencode_writer(name, namelen, phase);
       cwt = find_unencode_writer(name, namelen, phase);
+      if(cwt && is_chunked && Curl_cwriter_get_by_type(data, cwt)) {
+        /* A 'chunked' transfer encoding has already been added.
+         * Ignore duplicates. See #13451.
+         * Also RFC 9112, ch. 6.1:
+         * "A sender MUST NOT apply the chunked transfer coding more than
+         *  once to a message body."
+         */
+        return CURLE_OK;
+      }
+
+      if(is_transfer && !is_chunked &&
+         Curl_cwriter_get_by_name(data, "chunked")) {
+        /* RFC 9112, ch. 6.1:
+         * "If any transfer coding other than chunked is applied to a
+         *  response's content, the sender MUST either apply chunked as the
+         *  final transfer coding or terminate the message by closing the
+         *  connection."
+         * "chunked" must be the last added to be the first in its phase,
+         *  reject this.
+         */
+        failf(data, "Reject response due to 'chunked' not being the last "
+              "Transfer-Encoding");
+        return CURLE_BAD_CONTENT_ENCODING;
+      }
+
       if(!cwt)
       if(!cwt)
         cwt = &error_writer;  /* Defer error at use. */
         cwt = &error_writer;  /* Defer error at use. */
 
 

+ 2 - 1
lib/cookie.c

@@ -886,7 +886,8 @@ Curl_cookie_add(struct Curl_easy *data,
      * Now loop through the fields and init the struct we already have
      * Now loop through the fields and init the struct we already have
      * allocated
      * allocated
      */
      */
-    for(ptr = firstptr, fields = 0; ptr && !badcookie;
+    fields = 0;
+    for(ptr = firstptr; ptr && !badcookie;
         ptr = strtok_r(NULL, "\t", &tok_buf), fields++) {
         ptr = strtok_r(NULL, "\t", &tok_buf), fields++) {
       switch(fields) {
       switch(fields) {
       case 0:
       case 0:

+ 9 - 9
lib/curl_addrinfo.c

@@ -130,7 +130,7 @@ Curl_getaddrinfo_ex(const char *nodename,
     /* settle family-specific sockaddr structure size.  */
     /* settle family-specific sockaddr structure size.  */
     if(ai->ai_family == AF_INET)
     if(ai->ai_family == AF_INET)
       ss_size = sizeof(struct sockaddr_in);
       ss_size = sizeof(struct sockaddr_in);
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
     else if(ai->ai_family == AF_INET6)
     else if(ai->ai_family == AF_INET6)
       ss_size = sizeof(struct sockaddr_in6);
       ss_size = sizeof(struct sockaddr_in6);
 #endif
 #endif
@@ -259,7 +259,7 @@ Curl_he2ai(const struct hostent *he, int port)
   struct Curl_addrinfo *prevai = NULL;
   struct Curl_addrinfo *prevai = NULL;
   struct Curl_addrinfo *firstai = NULL;
   struct Curl_addrinfo *firstai = NULL;
   struct sockaddr_in *addr;
   struct sockaddr_in *addr;
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   struct sockaddr_in6 *addr6;
   struct sockaddr_in6 *addr6;
 #endif
 #endif
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
@@ -275,7 +275,7 @@ Curl_he2ai(const struct hostent *he, int port)
   for(i = 0; (curr = he->h_addr_list[i]) != NULL; i++) {
   for(i = 0; (curr = he->h_addr_list[i]) != NULL; i++) {
     size_t ss_size;
     size_t ss_size;
     size_t namelen = strlen(he->h_name) + 1; /* include null-terminator */
     size_t namelen = strlen(he->h_name) + 1; /* include null-terminator */
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
     if(he->h_addrtype == AF_INET6)
     if(he->h_addrtype == AF_INET6)
       ss_size = sizeof(struct sockaddr_in6);
       ss_size = sizeof(struct sockaddr_in6);
     else
     else
@@ -321,7 +321,7 @@ Curl_he2ai(const struct hostent *he, int port)
       addr->sin_port = htons((unsigned short)port);
       addr->sin_port = htons((unsigned short)port);
       break;
       break;
 
 
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
     case AF_INET6:
     case AF_INET6:
       addr6 = (void *)ai->ai_addr; /* storage area for this info */
       addr6 = (void *)ai->ai_addr; /* storage area for this info */
 
 
@@ -348,7 +348,7 @@ struct namebuff {
   struct hostent hostentry;
   struct hostent hostentry;
   union {
   union {
     struct in_addr  ina4;
     struct in_addr  ina4;
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
     struct in6_addr ina6;
     struct in6_addr ina6;
 #endif
 #endif
   } addrentry;
   } addrentry;
@@ -401,7 +401,7 @@ Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port)
     addrentry = (void *)&buf->addrentry.ina4;
     addrentry = (void *)&buf->addrentry.ina4;
     memcpy(addrentry, inaddr, sizeof(struct in_addr));
     memcpy(addrentry, inaddr, sizeof(struct in_addr));
     break;
     break;
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   case AF_INET6:
   case AF_INET6:
     addrsize = sizeof(struct in6_addr);
     addrsize = sizeof(struct in6_addr);
     addrentry = (void *)&buf->addrentry.ina6;
     addrentry = (void *)&buf->addrentry.ina6;
@@ -447,7 +447,7 @@ struct Curl_addrinfo *Curl_str2addr(char *address, int port)
   if(Curl_inet_pton(AF_INET, address, &in) > 0)
   if(Curl_inet_pton(AF_INET, address, &in) > 0)
     /* This is a dotted IP address 123.123.123.123-style */
     /* This is a dotted IP address 123.123.123.123-style */
     return Curl_ip2addr(AF_INET, &in, address, port);
     return Curl_ip2addr(AF_INET, &in, address, port);
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   {
   {
     struct in6_addr in6;
     struct in6_addr in6;
     if(Curl_inet_pton(AF_INET6, address, &in6) > 0)
     if(Curl_inet_pton(AF_INET6, address, &in6) > 0)
@@ -570,7 +570,7 @@ void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port)
 {
 {
   struct Curl_addrinfo *ca;
   struct Curl_addrinfo *ca;
   struct sockaddr_in *addr;
   struct sockaddr_in *addr;
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   struct sockaddr_in6 *addr6;
   struct sockaddr_in6 *addr6;
 #endif
 #endif
   for(ca = addrinfo; ca != NULL; ca = ca->ai_next) {
   for(ca = addrinfo; ca != NULL; ca = ca->ai_next) {
@@ -580,7 +580,7 @@ void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port)
       addr->sin_port = htons((unsigned short)port);
       addr->sin_port = htons((unsigned short)port);
       break;
       break;
 
 
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
     case AF_INET6:
     case AF_INET6:
       addr6 = (void *)ca->ai_addr; /* storage area for this info */
       addr6 = (void *)ca->ai_addr; /* storage area for this info */
       addr6->sin6_port = htons((unsigned short)port);
       addr6->sin6_port = htons((unsigned short)port);

+ 19 - 7
lib/curl_config.h.cmake

@@ -163,7 +163,7 @@
 #cmakedefine USE_WIN32_LDAP 1
 #cmakedefine USE_WIN32_LDAP 1
 
 
 /* Define if you want to enable IPv6 support */
 /* Define if you want to enable IPv6 support */
-#cmakedefine ENABLE_IPV6 1
+#cmakedefine USE_IPV6 1
 
 
 /* Define to 1 if you have the alarm function. */
 /* Define to 1 if you have the alarm function. */
 #cmakedefine HAVE_ALARM 1
 #cmakedefine HAVE_ALARM 1
@@ -199,6 +199,12 @@
 /* Define to 1 if you have the `closesocket' function. */
 /* Define to 1 if you have the `closesocket' function. */
 #cmakedefine HAVE_CLOSESOCKET 1
 #cmakedefine HAVE_CLOSESOCKET 1
 
 
+/* Define to 1 if you have the <dirent.h> header file. */
+#cmakedefine HAVE_DIRENT_H 1
+
+/* Define to 1 if you have the `opendir' function. */
+#cmakedefine HAVE_OPENDIR 1
+
 /* Define to 1 if you have the fcntl function. */
 /* Define to 1 if you have the fcntl function. */
 #cmakedefine HAVE_FCNTL 1
 #cmakedefine HAVE_FCNTL 1
 
 
@@ -704,6 +710,9 @@ ${SIZEOF_TIME_T_CODE}
 /* if OpenSSL is in use */
 /* if OpenSSL is in use */
 #cmakedefine USE_OPENSSL 1
 #cmakedefine USE_OPENSSL 1
 
 
+/* if librtmp/rtmpdump is in use */
+#cmakedefine USE_LIBRTMP 1
+
 /* Define to 1 if you don't want the OpenSSL configuration to be loaded
 /* Define to 1 if you don't want the OpenSSL configuration to be loaded
    automatically */
    automatically */
 #cmakedefine CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG 1
 #cmakedefine CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG 1
@@ -767,12 +776,6 @@ ${SIZEOF_TIME_T_CODE}
 /* Type to use in place of in_addr_t when system does not provide it. */
 /* Type to use in place of in_addr_t when system does not provide it. */
 #cmakedefine in_addr_t ${in_addr_t}
 #cmakedefine in_addr_t ${in_addr_t}
 
 
-/* Define to `__inline__' or `__inline' if that's what the C compiler
-   calls it, or to nothing if 'inline' is not supported under any name.  */
-#ifndef __cplusplus
-#undef inline
-#endif
-
 /* Define to `unsigned int' if <sys/types.h> does not define. */
 /* Define to `unsigned int' if <sys/types.h> does not define. */
 #cmakedefine size_t ${size_t}
 #cmakedefine size_t ${size_t}
 
 
@@ -785,6 +788,9 @@ ${SIZEOF_TIME_T_CODE}
 /* to enable Windows IDN */
 /* to enable Windows IDN */
 #cmakedefine USE_WIN32_IDN 1
 #cmakedefine USE_WIN32_IDN 1
 
 
+/* to enable Apple IDN */
+#cmakedefine USE_APPLE_IDN 1
+
 /* Define to 1 to enable websocket support. */
 /* Define to 1 to enable websocket support. */
 #cmakedefine USE_WEBSOCKETS 1
 #cmakedefine USE_WEBSOCKETS 1
 
 
@@ -796,3 +802,9 @@ ${SIZEOF_TIME_T_CODE}
 
 
 /* Define to 1 to enable TLS-SRP support. */
 /* Define to 1 to enable TLS-SRP support. */
 #cmakedefine USE_TLS_SRP 1
 #cmakedefine USE_TLS_SRP 1
+
+/* Define to 1 to query for HTTPSRR when using DoH */
+#cmakedefine USE_HTTPSRR 1
+
+/* if ECH support is available */
+#cmakedefine USE_ECH 1

+ 1 - 1
lib/curl_gethostname.c

@@ -68,7 +68,7 @@ int Curl_gethostname(char * const name, GETHOSTNAME_TYPE_ARG2 namelen)
   /* Override host name when environment variable CURL_GETHOSTNAME is set */
   /* Override host name when environment variable CURL_GETHOSTNAME is set */
   const char *force_hostname = getenv("CURL_GETHOSTNAME");
   const char *force_hostname = getenv("CURL_GETHOSTNAME");
   if(force_hostname) {
   if(force_hostname) {
-    strncpy(name, force_hostname, namelen);
+    strncpy(name, force_hostname, namelen - 1);
     err = 0;
     err = 0;
   }
   }
   else {
   else {

+ 0 - 17
lib/curl_multibyte.c

@@ -159,21 +159,4 @@ int curlx_win32_stat(const char *path, struct_stat *buffer)
 #endif
 #endif
 }
 }
 
 
-int curlx_win32_access(const char *path, int mode)
-{
-#if defined(_UNICODE)
-  int result = -1;
-  wchar_t *path_w = curlx_convert_UTF8_to_wchar(path);
-  if(path_w) {
-    result = _waccess(path_w, mode);
-    curlx_unicodefree(path_w);
-  }
-  else
-    errno = EINVAL;
-  return result;
-#else
-  return _access(path, mode);
-#endif
-}
-
 #endif /* USE_WIN32_LARGE_FILES || USE_WIN32_SMALL_FILES */
 #endif /* USE_WIN32_LARGE_FILES || USE_WIN32_SMALL_FILES */

+ 0 - 500
lib/curl_ntlm_wb.c

@@ -1,500 +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
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \
-    defined(NTLM_WB_ENABLED)
-
-/*
- * NTLM details:
- *
- * https://davenport.sourceforge.net/ntlm.html
- * https://www.innovation.ch/java/ntlm.html
- */
-
-#define DEBUG_ME 0
-
-#ifdef HAVE_SYS_WAIT_H
-#include <sys/wait.h>
-#endif
-#include <signal.h>
-#ifdef HAVE_PWD_H
-#include <pwd.h>
-#endif
-
-#include "urldata.h"
-#include "sendf.h"
-#include "select.h"
-#include "vauth/ntlm.h"
-#include "curl_ntlm_core.h"
-#include "curl_ntlm_wb.h"
-#include "url.h"
-#include "strerror.h"
-#include "strdup.h"
-#include "strcase.h"
-
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-#if DEBUG_ME
-# define DEBUG_OUT(x) x
-#else
-# define DEBUG_OUT(x) Curl_nop_stmt
-#endif
-
-/* Portable 'sclose_nolog' used only in child process instead of 'sclose'
-   to avoid fooling the socket leak detector */
-#ifdef HAVE_PIPE
-#  define sclose_nolog(x)  close((x))
-#elif defined(HAVE_CLOSESOCKET)
-#  define sclose_nolog(x)  closesocket((x))
-#elif defined(HAVE_CLOSESOCKET_CAMEL)
-#  define sclose_nolog(x)  CloseSocket((x))
-#else
-#  define sclose_nolog(x)  close((x))
-#endif
-
-static void ntlm_wb_cleanup(struct ntlmdata *ntlm)
-{
-  if(ntlm->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD) {
-    sclose(ntlm->ntlm_auth_hlpr_socket);
-    ntlm->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD;
-  }
-
-  if(ntlm->ntlm_auth_hlpr_pid) {
-    int i;
-    for(i = 0; i < 4; i++) {
-      pid_t ret = waitpid(ntlm->ntlm_auth_hlpr_pid, NULL, WNOHANG);
-      if(ret == ntlm->ntlm_auth_hlpr_pid || errno == ECHILD)
-        break;
-      switch(i) {
-      case 0:
-        kill(ntlm->ntlm_auth_hlpr_pid, SIGTERM);
-        break;
-      case 1:
-        /* Give the process another moment to shut down cleanly before
-           bringing down the axe */
-        Curl_wait_ms(1);
-        break;
-      case 2:
-        kill(ntlm->ntlm_auth_hlpr_pid, SIGKILL);
-        break;
-      case 3:
-        break;
-      }
-    }
-    ntlm->ntlm_auth_hlpr_pid = 0;
-  }
-
-  Curl_safefree(ntlm->challenge);
-  Curl_safefree(ntlm->response);
-}
-
-static CURLcode ntlm_wb_init(struct Curl_easy *data, struct ntlmdata *ntlm,
-                             const char *userp)
-{
-  curl_socket_t sockfds[2];
-  pid_t child_pid;
-  const char *username;
-  char *slash, *domain = NULL;
-  const char *ntlm_auth = NULL;
-  char *ntlm_auth_alloc = NULL;
-#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
-  struct passwd pw, *pw_res;
-  char pwbuf[1024];
-#endif
-  char buffer[STRERROR_LEN];
-
-#if defined(CURL_DISABLE_VERBOSE_STRINGS)
-  (void) data;
-#endif
-
-  /* Return if communication with ntlm_auth already set up */
-  if(ntlm->ntlm_auth_hlpr_socket != CURL_SOCKET_BAD ||
-     ntlm->ntlm_auth_hlpr_pid)
-    return CURLE_OK;
-
-  username = userp;
-  /* The real ntlm_auth really doesn't like being invoked with an
-     empty username. It won't make inferences for itself, and expects
-     the client to do so (mostly because it's really designed for
-     servers like squid to use for auth, and client support is an
-     afterthought for it). So try hard to provide a suitable username
-     if we don't already have one. But if we can't, provide the
-     empty one anyway. Perhaps they have an implementation of the
-     ntlm_auth helper which *doesn't* need it so we might as well try */
-  if(!username || !username[0]) {
-    username = getenv("NTLMUSER");
-    if(!username || !username[0])
-      username = getenv("LOGNAME");
-    if(!username || !username[0])
-      username = getenv("USER");
-#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
-    if((!username || !username[0]) &&
-       !getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res) &&
-       pw_res) {
-      username = pw.pw_name;
-    }
-#endif
-    if(!username || !username[0])
-      username = userp;
-  }
-  slash = strpbrk(username, "\\/");
-  if(slash) {
-    domain = strdup(username);
-    if(!domain)
-      return CURLE_OUT_OF_MEMORY;
-    slash = domain + (slash - username);
-    *slash = '\0';
-    username = username + (slash - domain) + 1;
-  }
-
-  /* For testing purposes, when DEBUGBUILD is defined and environment
-     variable CURL_NTLM_WB_FILE is set a fake_ntlm is used to perform
-     NTLM challenge/response which only accepts commands and output
-     strings pre-written in test case definitions */
-#ifdef DEBUGBUILD
-  ntlm_auth_alloc = curl_getenv("CURL_NTLM_WB_FILE");
-  if(ntlm_auth_alloc)
-    ntlm_auth = ntlm_auth_alloc;
-  else
-#endif
-    ntlm_auth = NTLM_WB_FILE;
-
-  if(access(ntlm_auth, X_OK) != 0) {
-    failf(data, "Could not access ntlm_auth: %s errno %d: %s",
-          ntlm_auth, errno, Curl_strerror(errno, buffer, sizeof(buffer)));
-    goto done;
-  }
-
-  if(wakeup_create(sockfds)) {
-    failf(data, "Could not open socket pair. errno %d: %s",
-          errno, Curl_strerror(errno, buffer, sizeof(buffer)));
-    goto done;
-  }
-
-  child_pid = fork();
-  if(child_pid == -1) {
-    wakeup_close(sockfds[0]);
-    wakeup_close(sockfds[1]);
-    failf(data, "Could not fork. errno %d: %s",
-          errno, Curl_strerror(errno, buffer, sizeof(buffer)));
-    goto done;
-  }
-  else if(!child_pid) {
-    /*
-     * child process
-     */
-
-    /* Don't use sclose in the child since it fools the socket leak detector */
-    sclose_nolog(sockfds[0]);
-    if(dup2(sockfds[1], STDIN_FILENO) == -1) {
-      failf(data, "Could not redirect child stdin. errno %d: %s",
-            errno, Curl_strerror(errno, buffer, sizeof(buffer)));
-      exit(1);
-    }
-
-    if(dup2(sockfds[1], STDOUT_FILENO) == -1) {
-      failf(data, "Could not redirect child stdout. errno %d: %s",
-            errno, Curl_strerror(errno, buffer, sizeof(buffer)));
-      exit(1);
-    }
-
-    if(domain)
-      execl(ntlm_auth, ntlm_auth,
-            "--helper-protocol", "ntlmssp-client-1",
-            "--use-cached-creds",
-            "--username", username,
-            "--domain", domain,
-            NULL);
-    else
-      execl(ntlm_auth, ntlm_auth,
-            "--helper-protocol", "ntlmssp-client-1",
-            "--use-cached-creds",
-            "--username", username,
-            NULL);
-
-    sclose_nolog(sockfds[1]);
-    failf(data, "Could not execl(). errno %d: %s",
-          errno, Curl_strerror(errno, buffer, sizeof(buffer)));
-    exit(1);
-  }
-
-  sclose(sockfds[1]);
-  ntlm->ntlm_auth_hlpr_socket = sockfds[0];
-  ntlm->ntlm_auth_hlpr_pid = child_pid;
-  free(domain);
-  free(ntlm_auth_alloc);
-  return CURLE_OK;
-
-done:
-  free(domain);
-  free(ntlm_auth_alloc);
-  return CURLE_REMOTE_ACCESS_DENIED;
-}
-
-/* if larger than this, something is seriously wrong */
-#define MAX_NTLM_WB_RESPONSE 100000
-
-static CURLcode ntlm_wb_response(struct Curl_easy *data, struct ntlmdata *ntlm,
-                                 const char *input, curlntlm state)
-{
-  size_t len_in = strlen(input), len_out = 0;
-  struct dynbuf b;
-  char *ptr = NULL;
-  unsigned char buf[1024];
-  Curl_dyn_init(&b, MAX_NTLM_WB_RESPONSE);
-
-  while(len_in > 0) {
-    ssize_t written = wakeup_write(ntlm->ntlm_auth_hlpr_socket, input, len_in);
-    if(written == -1) {
-      /* Interrupted by a signal, retry it */
-      if(errno == EINTR)
-        continue;
-      /* write failed if other errors happen */
-      goto done;
-    }
-    input += written;
-    len_in -= written;
-  }
-  /* Read one line */
-  while(1) {
-    ssize_t size =
-      wakeup_read(ntlm->ntlm_auth_hlpr_socket, buf, sizeof(buf));
-    if(size == -1) {
-      if(errno == EINTR)
-        continue;
-      goto done;
-    }
-    else if(size == 0)
-      goto done;
-
-    if(Curl_dyn_addn(&b, buf, size))
-      goto done;
-
-    len_out = Curl_dyn_len(&b);
-    ptr = Curl_dyn_ptr(&b);
-    if(len_out && ptr[len_out - 1] == '\n') {
-      ptr[len_out - 1] = '\0';
-      break; /* done! */
-    }
-    /* loop */
-  }
-
-  /* Samba/winbind installed but not configured */
-  if(state == NTLMSTATE_TYPE1 &&
-     len_out == 3 &&
-     ptr[0] == 'P' && ptr[1] == 'W')
-    goto done;
-  /* invalid response */
-  if(len_out < 4)
-    goto done;
-  if(state == NTLMSTATE_TYPE1 &&
-     (ptr[0]!='Y' || ptr[1]!='R' || ptr[2]!=' '))
-    goto done;
-  if(state == NTLMSTATE_TYPE2 &&
-     (ptr[0]!='K' || ptr[1]!='K' || ptr[2]!=' ') &&
-     (ptr[0]!='A' || ptr[1]!='F' || ptr[2]!=' '))
-    goto done;
-
-  ntlm->response = strdup(ptr + 3);
-  Curl_dyn_free(&b);
-  if(!ntlm->response)
-    return CURLE_OUT_OF_MEMORY;
-  return CURLE_OK;
-done:
-  Curl_dyn_free(&b);
-  return CURLE_REMOTE_ACCESS_DENIED;
-}
-
-CURLcode Curl_input_ntlm_wb(struct Curl_easy *data,
-                            struct connectdata *conn,
-                            bool proxy,
-                            const char *header)
-{
-  struct ntlmdata *ntlm = proxy ? &conn->proxyntlm : &conn->ntlm;
-  curlntlm *state = proxy ? &conn->proxy_ntlm_state : &conn->http_ntlm_state;
-
-  (void) data;  /* In case it gets unused by nop log macros. */
-
-  if(!checkprefix("NTLM", header))
-    return CURLE_BAD_CONTENT_ENCODING;
-
-  header += strlen("NTLM");
-  while(*header && ISSPACE(*header))
-    header++;
-
-  if(*header) {
-    ntlm->challenge = strdup(header);
-    if(!ntlm->challenge)
-      return CURLE_OUT_OF_MEMORY;
-
-    *state = NTLMSTATE_TYPE2; /* We got a type-2 message */
-  }
-  else {
-    if(*state == NTLMSTATE_LAST) {
-      infof(data, "NTLM auth restarted");
-      Curl_http_auth_cleanup_ntlm_wb(conn);
-    }
-    else if(*state == NTLMSTATE_TYPE3) {
-      infof(data, "NTLM handshake rejected");
-      Curl_http_auth_cleanup_ntlm_wb(conn);
-      *state = NTLMSTATE_NONE;
-      return CURLE_REMOTE_ACCESS_DENIED;
-    }
-    else if(*state >= NTLMSTATE_TYPE1) {
-      infof(data, "NTLM handshake failure (internal error)");
-      return CURLE_REMOTE_ACCESS_DENIED;
-    }
-
-    *state = NTLMSTATE_TYPE1; /* We should send away a type-1 */
-  }
-
-  return CURLE_OK;
-}
-
-/*
- * This is for creating ntlm header output by delegating challenge/response
- * to Samba's winbind daemon helper ntlm_auth.
- */
-CURLcode Curl_output_ntlm_wb(struct Curl_easy *data, struct connectdata *conn,
-                             bool proxy)
-{
-  /* point to the address of the pointer that holds the string to send to the
-     server, which is for a plain host or for an HTTP proxy */
-  char **allocuserpwd;
-  /* point to the name and password for this */
-  const char *userp;
-  struct ntlmdata *ntlm;
-  curlntlm *state;
-  struct auth *authp;
-
-  CURLcode res = CURLE_OK;
-
-  DEBUGASSERT(conn);
-  DEBUGASSERT(data);
-
-  if(proxy) {
-#ifndef CURL_DISABLE_PROXY
-    allocuserpwd = &data->state.aptr.proxyuserpwd;
-    userp = conn->http_proxy.user;
-    ntlm = &conn->proxyntlm;
-    state = &conn->proxy_ntlm_state;
-    authp = &data->state.authproxy;
-#else
-    return CURLE_NOT_BUILT_IN;
-#endif
-  }
-  else {
-    allocuserpwd = &data->state.aptr.userpwd;
-    userp = conn->user;
-    ntlm = &conn->ntlm;
-    state = &conn->http_ntlm_state;
-    authp = &data->state.authhost;
-  }
-  authp->done = FALSE;
-
-  /* not set means empty */
-  if(!userp)
-    userp = "";
-
-  switch(*state) {
-  case NTLMSTATE_TYPE1:
-  default:
-    /* Use Samba's 'winbind' daemon to support NTLM authentication,
-     * by delegating the NTLM challenge/response protocol to a helper
-     * in ntlm_auth.
-     * https://web.archive.org/web/20190925164737
-     * /devel.squid-cache.org/ntlm/squid_helper_protocol.html
-     * https://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
-     * https://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
-     * Preprocessor symbol 'NTLM_WB_ENABLED' is defined when this
-     * feature is enabled and 'NTLM_WB_FILE' symbol holds absolute
-     * filename of ntlm_auth helper.
-     * If NTLM authentication using winbind fails, go back to original
-     * request handling process.
-     */
-    /* Create communication with ntlm_auth */
-    res = ntlm_wb_init(data, ntlm, userp);
-    if(res)
-      return res;
-    res = ntlm_wb_response(data, ntlm, "YR\n", *state);
-    if(res)
-      return res;
-
-    free(*allocuserpwd);
-    *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
-                            proxy ? "Proxy-" : "",
-                            ntlm->response);
-    DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd));
-    Curl_safefree(ntlm->response);
-    if(!*allocuserpwd)
-      return CURLE_OUT_OF_MEMORY;
-    break;
-
-  case NTLMSTATE_TYPE2: {
-    char *input = aprintf("TT %s\n", ntlm->challenge);
-    if(!input)
-      return CURLE_OUT_OF_MEMORY;
-    res = ntlm_wb_response(data, ntlm, input, *state);
-    free(input);
-    if(res)
-      return res;
-
-    free(*allocuserpwd);
-    *allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
-                            proxy ? "Proxy-" : "",
-                            ntlm->response);
-    DEBUG_OUT(fprintf(stderr, "**** %s\n ", *allocuserpwd));
-    *state = NTLMSTATE_TYPE3; /* we sent a type-3 */
-    authp->done = TRUE;
-    Curl_http_auth_cleanup_ntlm_wb(conn);
-    if(!*allocuserpwd)
-      return CURLE_OUT_OF_MEMORY;
-    break;
-  }
-  case NTLMSTATE_TYPE3:
-    /* connection is already authenticated,
-     * don't send a header in future requests */
-    *state = NTLMSTATE_LAST;
-    FALLTHROUGH();
-  case NTLMSTATE_LAST:
-    Curl_safefree(*allocuserpwd);
-    authp->done = TRUE;
-    break;
-  }
-
-  return CURLE_OK;
-}
-
-void Curl_http_auth_cleanup_ntlm_wb(struct connectdata *conn)
-{
-  ntlm_wb_cleanup(&conn->ntlm);
-  ntlm_wb_cleanup(&conn->proxyntlm);
-}
-
-#endif /* !CURL_DISABLE_HTTP && USE_NTLM && NTLM_WB_ENABLED */

+ 36 - 32
lib/curl_path.c

@@ -98,8 +98,8 @@ CURLcode Curl_getworkingpath(struct Curl_easy *data,
   return CURLE_OK;
   return CURLE_OK;
 }
 }
 
 
-/* The get_pathname() function is being borrowed from OpenSSH sftp.c
-   version 4.6p1. */
+/* The original get_pathname() function came from OpenSSH sftp.c version
+   4.6p1. */
 /*
 /*
  * Copyright (c) 2001-2004 Damien Miller <[email protected]>
  * Copyright (c) 2001-2004 Damien Miller <[email protected]>
  *
  *
@@ -115,38 +115,37 @@ CURLcode Curl_getworkingpath(struct Curl_easy *data,
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
  */
-CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir)
+
+#define MAX_PATHLENGTH 65535 /* arbitrary long */
+
+CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir)
 {
 {
   const char *cp = *cpp, *end;
   const char *cp = *cpp, *end;
   char quot;
   char quot;
-  unsigned int i, j;
-  size_t fullPathLength, pathLength;
-  bool relativePath = false;
+  unsigned int i;
   static const char WHITESPACE[] = " \t\r\n";
   static const char WHITESPACE[] = " \t\r\n";
+  struct dynbuf out;
+  CURLcode result;
 
 
   DEBUGASSERT(homedir);
   DEBUGASSERT(homedir);
-  if(!*cp || !homedir) {
-    *cpp = NULL;
-    *path = NULL;
+  *path = NULL;
+  *cpp = NULL;
+  if(!*cp || !homedir)
     return CURLE_QUOTE_ERROR;
     return CURLE_QUOTE_ERROR;
-  }
+
+  Curl_dyn_init(&out, MAX_PATHLENGTH);
+
   /* Ignore leading whitespace */
   /* Ignore leading whitespace */
   cp += strspn(cp, WHITESPACE);
   cp += strspn(cp, WHITESPACE);
-  /* Allocate enough space for home directory and filename + separator */
-  fullPathLength = strlen(cp) + strlen(homedir) + 2;
-  *path = malloc(fullPathLength);
-  if(!*path)
-    return CURLE_OUT_OF_MEMORY;
 
 
   /* Check for quoted filenames */
   /* Check for quoted filenames */
   if(*cp == '\"' || *cp == '\'') {
   if(*cp == '\"' || *cp == '\'') {
     quot = *cp++;
     quot = *cp++;
 
 
     /* Search for terminating quote, unescape some chars */
     /* Search for terminating quote, unescape some chars */
-    for(i = j = 0; i <= strlen(cp); i++) {
+    for(i = 0; i <= strlen(cp); i++) {
       if(cp[i] == quot) {  /* Found quote */
       if(cp[i] == quot) {  /* Found quote */
         i++;
         i++;
-        (*path)[j] = '\0';
         break;
         break;
       }
       }
       if(cp[i] == '\0') {  /* End of string */
       if(cp[i] == '\0') {  /* End of string */
@@ -159,40 +158,45 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir)
           goto fail;
           goto fail;
         }
         }
       }
       }
-      (*path)[j++] = cp[i];
+      result = Curl_dyn_addn(&out, &cp[i], 1);
+      if(result)
+        return result;
     }
     }
 
 
-    if(j == 0) {
+    if(!Curl_dyn_len(&out))
       goto fail;
       goto fail;
-    }
-    *cpp = cp + i + strspn(cp + i, WHITESPACE);
+
+    /* return pointer to second parameter if it exists */
+    *cpp = &cp[i] + strspn(&cp[i], WHITESPACE);
   }
   }
   else {
   else {
     /* Read to end of filename - either to whitespace or terminator */
     /* Read to end of filename - either to whitespace or terminator */
     end = strpbrk(cp, WHITESPACE);
     end = strpbrk(cp, WHITESPACE);
     if(!end)
     if(!end)
       end = strchr(cp, '\0');
       end = strchr(cp, '\0');
+
     /* return pointer to second parameter if it exists */
     /* return pointer to second parameter if it exists */
     *cpp = end + strspn(end, WHITESPACE);
     *cpp = end + strspn(end, WHITESPACE);
-    pathLength = 0;
-    relativePath = (cp[0] == '/' && cp[1] == '~' && cp[2] == '/');
+
     /* Handling for relative path - prepend home directory */
     /* Handling for relative path - prepend home directory */
-    if(relativePath) {
-      strcpy(*path, homedir);
-      pathLength = strlen(homedir);
-      (*path)[pathLength++] = '/';
-      (*path)[pathLength] = '\0';
+    if(cp[0] == '/' && cp[1] == '~' && cp[2] == '/') {
+      result = Curl_dyn_add(&out, homedir);
+      if(!result)
+        result = Curl_dyn_addn(&out, "/", 1);
+      if(result)
+        return result;
       cp += 3;
       cp += 3;
     }
     }
     /* Copy path name up until first "whitespace" */
     /* Copy path name up until first "whitespace" */
-    memcpy(&(*path)[pathLength], cp, (int)(end - cp));
-    pathLength += (int)(end - cp);
-    (*path)[pathLength] = '\0';
+    result = Curl_dyn_addn(&out, cp, (end - cp));
+    if(result)
+      return result;
   }
   }
+  *path = Curl_dyn_ptr(&out);
   return CURLE_OK;
   return CURLE_OK;
 
 
 fail:
 fail:
-  Curl_safefree(*path);
+  Curl_dyn_free(&out);
   return CURLE_QUOTE_ERROR;
   return CURLE_QUOTE_ERROR;
 }
 }
 
 

+ 1 - 1
lib/curl_path.h

@@ -45,5 +45,5 @@ CURLcode Curl_getworkingpath(struct Curl_easy *data,
                              char *homedir,
                              char *homedir,
                              char **path);
                              char **path);
 
 
-CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir);
+CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir);
 #endif /* HEADER_CURL_PATH_H */
 #endif /* HEADER_CURL_PATH_H */

+ 31 - 7
lib/curl_rtmp.c

@@ -35,8 +35,10 @@
 #include "warnless.h"
 #include "warnless.h"
 #include <curl/curl.h>
 #include <curl/curl.h>
 #include <librtmp/rtmp.h>
 #include <librtmp/rtmp.h>
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
 #include "curl_memory.h"
 #include "curl_memory.h"
-/* The last #include file should be: */
 #include "memdebug.h"
 #include "memdebug.h"
 
 
 #if defined(_WIN32) && !defined(USE_LWIPSOCK)
 #if defined(_WIN32) && !defined(USE_LWIPSOCK)
@@ -66,7 +68,7 @@ static Curl_send rtmp_send;
  */
  */
 
 
 const struct Curl_handler Curl_handler_rtmp = {
 const struct Curl_handler Curl_handler_rtmp = {
-  "RTMP",                               /* scheme */
+  "rtmp",                               /* scheme */
   rtmp_setup_connection,                /* setup_connection */
   rtmp_setup_connection,                /* setup_connection */
   rtmp_do,                              /* do_it */
   rtmp_do,                              /* do_it */
   rtmp_done,                            /* done */
   rtmp_done,                            /* done */
@@ -80,6 +82,7 @@ const struct Curl_handler Curl_handler_rtmp = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMP,                            /* defport */
   PORT_RTMP,                            /* defport */
@@ -89,7 +92,7 @@ const struct Curl_handler Curl_handler_rtmp = {
 };
 };
 
 
 const struct Curl_handler Curl_handler_rtmpt = {
 const struct Curl_handler Curl_handler_rtmpt = {
-  "RTMPT",                              /* scheme */
+  "rtmpt",                              /* scheme */
   rtmp_setup_connection,                /* setup_connection */
   rtmp_setup_connection,                /* setup_connection */
   rtmp_do,                              /* do_it */
   rtmp_do,                              /* do_it */
   rtmp_done,                            /* done */
   rtmp_done,                            /* done */
@@ -103,6 +106,7 @@ const struct Curl_handler Curl_handler_rtmpt = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMPT,                           /* defport */
   PORT_RTMPT,                           /* defport */
@@ -112,7 +116,7 @@ const struct Curl_handler Curl_handler_rtmpt = {
 };
 };
 
 
 const struct Curl_handler Curl_handler_rtmpe = {
 const struct Curl_handler Curl_handler_rtmpe = {
-  "RTMPE",                              /* scheme */
+  "rtmpe",                              /* scheme */
   rtmp_setup_connection,                /* setup_connection */
   rtmp_setup_connection,                /* setup_connection */
   rtmp_do,                              /* do_it */
   rtmp_do,                              /* do_it */
   rtmp_done,                            /* done */
   rtmp_done,                            /* done */
@@ -126,6 +130,7 @@ const struct Curl_handler Curl_handler_rtmpe = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMP,                            /* defport */
   PORT_RTMP,                            /* defport */
@@ -135,7 +140,7 @@ const struct Curl_handler Curl_handler_rtmpe = {
 };
 };
 
 
 const struct Curl_handler Curl_handler_rtmpte = {
 const struct Curl_handler Curl_handler_rtmpte = {
-  "RTMPTE",                             /* scheme */
+  "rtmpte",                             /* scheme */
   rtmp_setup_connection,                /* setup_connection */
   rtmp_setup_connection,                /* setup_connection */
   rtmp_do,                              /* do_it */
   rtmp_do,                              /* do_it */
   rtmp_done,                            /* done */
   rtmp_done,                            /* done */
@@ -149,6 +154,7 @@ const struct Curl_handler Curl_handler_rtmpte = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMPT,                           /* defport */
   PORT_RTMPT,                           /* defport */
@@ -158,7 +164,7 @@ const struct Curl_handler Curl_handler_rtmpte = {
 };
 };
 
 
 const struct Curl_handler Curl_handler_rtmps = {
 const struct Curl_handler Curl_handler_rtmps = {
-  "RTMPS",                              /* scheme */
+  "rtmps",                              /* scheme */
   rtmp_setup_connection,                /* setup_connection */
   rtmp_setup_connection,                /* setup_connection */
   rtmp_do,                              /* do_it */
   rtmp_do,                              /* do_it */
   rtmp_done,                            /* done */
   rtmp_done,                            /* done */
@@ -172,6 +178,7 @@ const struct Curl_handler Curl_handler_rtmps = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMPS,                           /* defport */
   PORT_RTMPS,                           /* defport */
@@ -181,7 +188,7 @@ const struct Curl_handler Curl_handler_rtmps = {
 };
 };
 
 
 const struct Curl_handler Curl_handler_rtmpts = {
 const struct Curl_handler Curl_handler_rtmpts = {
-  "RTMPTS",                             /* scheme */
+  "rtmpts",                             /* scheme */
   rtmp_setup_connection,                /* setup_connection */
   rtmp_setup_connection,                /* setup_connection */
   rtmp_do,                              /* do_it */
   rtmp_do,                              /* do_it */
   rtmp_done,                            /* done */
   rtmp_done,                            /* done */
@@ -195,6 +202,7 @@ const struct Curl_handler Curl_handler_rtmpts = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* perform_getsock */
   rtmp_disconnect,                      /* disconnect */
   rtmp_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* attach connection */
   PORT_RTMPS,                           /* defport */
   PORT_RTMPS,                           /* defport */
@@ -335,4 +343,20 @@ static ssize_t rtmp_send(struct Curl_easy *data, int sockindex,
 
 
   return num;
   return num;
 }
 }
+
+void Curl_rtmp_version(char *version, size_t len)
+{
+  char suff[2];
+  if(RTMP_LIB_VERSION & 0xff) {
+    suff[0] = (RTMP_LIB_VERSION & 0xff) + 'a' - 1;
+    suff[1] = '\0';
+  }
+  else
+    suff[0] = '\0';
+
+  msnprintf(version, len, "librtmp/%d.%d%s",
+            RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff,
+            suff);
+}
+
 #endif  /* USE_LIBRTMP */
 #endif  /* USE_LIBRTMP */

+ 2 - 0
lib/curl_rtmp.h

@@ -30,6 +30,8 @@ extern const struct Curl_handler Curl_handler_rtmpe;
 extern const struct Curl_handler Curl_handler_rtmpte;
 extern const struct Curl_handler Curl_handler_rtmpte;
 extern const struct Curl_handler Curl_handler_rtmps;
 extern const struct Curl_handler Curl_handler_rtmps;
 extern const struct Curl_handler Curl_handler_rtmpts;
 extern const struct Curl_handler Curl_handler_rtmpts;
+
+void Curl_rtmp_version(char *version, size_t len);
 #endif
 #endif
 
 
 #endif /* HEADER_CURL_RTMP_H */
 #endif /* HEADER_CURL_RTMP_H */

+ 5 - 5
lib/curl_sasl.c

@@ -376,7 +376,7 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
     sasl->authused = SASL_MECH_EXTERNAL;
     sasl->authused = SASL_MECH_EXTERNAL;
 
 
     if(force_ir || data->set.sasl_ir)
     if(force_ir || data->set.sasl_ir)
-      result = Curl_auth_create_external_message(conn->user, &resp);
+      Curl_auth_create_external_message(conn->user, &resp);
   }
   }
   else if(data->state.aptr.user) {
   else if(data->state.aptr.user) {
 #if defined(USE_KERBEROS5)
 #if defined(USE_KERBEROS5)
@@ -498,7 +498,7 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
       sasl->authused = SASL_MECH_LOGIN;
       sasl->authused = SASL_MECH_LOGIN;
 
 
       if(force_ir || data->set.sasl_ir)
       if(force_ir || data->set.sasl_ir)
-        result = Curl_auth_create_login_message(conn->user, &resp);
+        Curl_auth_create_login_message(conn->user, &resp);
     }
     }
   }
   }
 
 
@@ -576,14 +576,14 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
                                             conn->user, conn->passwd, &resp);
                                             conn->user, conn->passwd, &resp);
     break;
     break;
   case SASL_LOGIN:
   case SASL_LOGIN:
-    result = Curl_auth_create_login_message(conn->user, &resp);
+    Curl_auth_create_login_message(conn->user, &resp);
     newstate = SASL_LOGIN_PASSWD;
     newstate = SASL_LOGIN_PASSWD;
     break;
     break;
   case SASL_LOGIN_PASSWD:
   case SASL_LOGIN_PASSWD:
-    result = Curl_auth_create_login_message(conn->passwd, &resp);
+    Curl_auth_create_login_message(conn->passwd, &resp);
     break;
     break;
   case SASL_EXTERNAL:
   case SASL_EXTERNAL:
-    result = Curl_auth_create_external_message(conn->user, &resp);
+    Curl_auth_create_external_message(conn->user, &resp);
     break;
     break;
 #ifdef USE_GSASL
 #ifdef USE_GSASL
   case SASL_GSASL:
   case SASL_GSASL:

+ 48 - 13
lib/curl_setup.h

@@ -72,6 +72,11 @@
 # endif
 # endif
 #endif
 #endif
 
 
+/* Compatibility */
+#if defined(ENABLE_IPV6)
+#  define USE_IPV6 1
+#endif
+
 /*
 /*
  * Include configuration script results or hand-crafted
  * Include configuration script results or hand-crafted
  * configuration file for platforms which lack config tool.
  * configuration file for platforms which lack config tool.
@@ -286,7 +291,8 @@
 
 
 /* based on logic in "curl/mprintf.h" */
 /* based on logic in "curl/mprintf.h" */
 
 
-#if (defined(__GNUC__) || defined(__clang__)) &&                        \
+#if (defined(__GNUC__) || defined(__clang__) ||                         \
+  defined(__IAR_SYSTEMS_ICC__)) &&                                      \
   defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) &&         \
   defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) &&         \
   !defined(CURL_NO_FMT_CHECKS)
   !defined(CURL_NO_FMT_CHECKS)
 #if defined(__MINGW32__) && !defined(__clang__)
 #if defined(__MINGW32__) && !defined(__clang__)
@@ -309,7 +315,7 @@
 #include <TargetConditionals.h>
 #include <TargetConditionals.h>
 #define USE_RESOLVE_ON_IPS 1
 #define USE_RESOLVE_ON_IPS 1
 #  if TARGET_OS_MAC && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && \
 #  if TARGET_OS_MAC && !(defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) && \
-     defined(ENABLE_IPV6)
+     defined(USE_IPV6)
 #    define CURL_MACOS_CALL_COPYPROXIES 1
 #    define CURL_MACOS_CALL_COPYPROXIES 1
 #  endif
 #  endif
 #endif
 #endif
@@ -404,11 +410,9 @@
 #  define LSEEK_ERROR                (__int64)-1
 #  define LSEEK_ERROR                (__int64)-1
 #  define open                       curlx_win32_open
 #  define open                       curlx_win32_open
 #  define fopen(fname,mode)          curlx_win32_fopen(fname, mode)
 #  define fopen(fname,mode)          curlx_win32_fopen(fname, mode)
-#  define access(fname,mode)         curlx_win32_access(fname, mode)
    int curlx_win32_open(const char *filename, int oflag, ...);
    int curlx_win32_open(const char *filename, int oflag, ...);
    int curlx_win32_stat(const char *path, struct_stat *buffer);
    int curlx_win32_stat(const char *path, struct_stat *buffer);
    FILE *curlx_win32_fopen(const char *filename, const char *mode);
    FILE *curlx_win32_fopen(const char *filename, const char *mode);
-   int curlx_win32_access(const char *path, int mode);
 #endif
 #endif
 
 
 /*
 /*
@@ -427,11 +431,9 @@
 #    define struct_stat                struct _stat
 #    define struct_stat                struct _stat
 #    define open                       curlx_win32_open
 #    define open                       curlx_win32_open
 #    define fopen(fname,mode)          curlx_win32_fopen(fname, mode)
 #    define fopen(fname,mode)          curlx_win32_fopen(fname, mode)
-#    define access(fname,mode)         curlx_win32_access(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);
-     int curlx_win32_access(const char *path, int mode);
 #  endif
 #  endif
 #  define LSEEK_ERROR                (long)-1
 #  define LSEEK_ERROR                (long)-1
 #endif
 #endif
@@ -506,11 +508,14 @@
 #  error "curl_off_t must be exactly 64 bits"
 #  error "curl_off_t must be exactly 64 bits"
 #else
 #else
   typedef unsigned CURL_TYPEOF_CURL_OFF_T curl_uint64_t;
   typedef unsigned CURL_TYPEOF_CURL_OFF_T curl_uint64_t;
+  typedef CURL_TYPEOF_CURL_OFF_T  curl_int64_t;
 #  ifndef CURL_SUFFIX_CURL_OFF_TU
 #  ifndef CURL_SUFFIX_CURL_OFF_TU
 #    error "CURL_SUFFIX_CURL_OFF_TU must be defined"
 #    error "CURL_SUFFIX_CURL_OFF_TU must be defined"
 #  endif
 #  endif
 #  define CURL_UINT64_SUFFIX  CURL_SUFFIX_CURL_OFF_TU
 #  define CURL_UINT64_SUFFIX  CURL_SUFFIX_CURL_OFF_TU
 #  define CURL_UINT64_C(val)  CURL_CONC_MACROS(val,CURL_UINT64_SUFFIX)
 #  define CURL_UINT64_C(val)  CURL_CONC_MACROS(val,CURL_UINT64_SUFFIX)
+# define CURL_PRId64  CURL_FORMAT_CURL_OFF_T
+# define CURL_PRIu64  CURL_FORMAT_CURL_OFF_TU
 #endif
 #endif
 
 
 #if (SIZEOF_TIME_T == 4)
 #if (SIZEOF_TIME_T == 4)
@@ -617,9 +622,9 @@
  * Mutually exclusive CURLRES_* definitions.
  * Mutually exclusive CURLRES_* definitions.
  */
  */
 
 
-#if defined(ENABLE_IPV6) && defined(HAVE_GETADDRINFO)
+#if defined(USE_IPV6) && defined(HAVE_GETADDRINFO)
 #  define CURLRES_IPV6
 #  define CURLRES_IPV6
-#elif defined(ENABLE_IPV6) && (defined(_WIN32) || defined(__CYGWIN__))
+#elif defined(USE_IPV6) && (defined(_WIN32) || defined(__CYGWIN__))
 /* assume on Windows that IPv6 without getaddrinfo is a broken build */
 /* assume on Windows that IPv6 without getaddrinfo is a broken build */
 #  error "Unexpected build: IPv6 is enabled but getaddrinfo was not found."
 #  error "Unexpected build: IPv6 is enabled but getaddrinfo was not found."
 #else
 #else
@@ -641,13 +646,14 @@
 
 
 /* ---------------------------------------------------------------- */
 /* ---------------------------------------------------------------- */
 
 
-#if defined(HAVE_LIBIDN2) && defined(HAVE_IDN2_H) && !defined(USE_WIN32_IDN)
+#if defined(HAVE_LIBIDN2) && defined(HAVE_IDN2_H) && \
+  !defined(USE_WIN32_IDN) && !defined(USE_APPLE_IDN)
 /* The lib and header are present */
 /* The lib and header are present */
 #define USE_LIBIDN2
 #define USE_LIBIDN2
 #endif
 #endif
 
 
-#if defined(USE_LIBIDN2) && defined(USE_WIN32_IDN)
-#error "Both libidn2 and WinIDN are enabled, choose one."
+#if defined(USE_LIBIDN2) && (defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN))
+#error "libidn2 cannot be enabled with WinIDN or AppleIDN, choose one."
 #endif
 #endif
 
 
 #define LIBIDN_REQUIRED_VERSION "0.4.1"
 #define LIBIDN_REQUIRED_VERSION "0.4.1"
@@ -701,6 +707,13 @@
   ((__GNUC__ == 2) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 7)))
   ((__GNUC__ == 2) && defined(__GNUC_MINOR__) && (__GNUC_MINOR__ >= 7)))
 #  define UNUSED_PARAM __attribute__((__unused__))
 #  define UNUSED_PARAM __attribute__((__unused__))
 #  define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
 #  define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#elif defined(__IAR_SYSTEMS_ICC__)
+#  define UNUSED_PARAM __attribute__((__unused__))
+#  if (__VER__ >= 9040001)
+#    define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#  else
+#    define WARN_UNUSED_RESULT
+#  endif
 #else
 #else
 #  define UNUSED_PARAM /* NOTHING */
 #  define UNUSED_PARAM /* NOTHING */
 #  define WARN_UNUSED_RESULT
 #  define WARN_UNUSED_RESULT
@@ -709,7 +722,8 @@
 /* noreturn attribute */
 /* noreturn attribute */
 
 
 #if !defined(CURL_NORETURN)
 #if !defined(CURL_NORETURN)
-#if (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__)
+#if (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__) || \
+  defined(__IAR_SYSTEMS_ICC__)
 #  define CURL_NORETURN  __attribute__((__noreturn__))
 #  define CURL_NORETURN  __attribute__((__noreturn__))
 #elif defined(_MSC_VER) && (_MSC_VER >= 1200)
 #elif defined(_MSC_VER) && (_MSC_VER >= 1200)
 #  define CURL_NORETURN  __declspec(noreturn)
 #  define CURL_NORETURN  __declspec(noreturn)
@@ -852,7 +866,6 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
 #error "Multi-SSL combined with QUIC is not supported"
 #error "Multi-SSL combined with QUIC is not supported"
 #endif
 #endif
 
 
-#define ENABLE_QUIC
 #define USE_HTTP3
 #define USE_HTTP3
 #endif
 #endif
 
 
@@ -883,4 +896,26 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
 #define OPENSSL_SUPPRESS_DEPRECATED
 #define OPENSSL_SUPPRESS_DEPRECATED
 #endif
 #endif
 
 
+#if defined(inline)
+  /* 'inline' is defined as macro and assumed to be correct */
+  /* No need for 'inline' replacement */
+#elif defined(__cplusplus)
+  /* The code is compiled with C++ compiler.
+     C++ always supports 'inline'. */
+  /* No need for 'inline' replacement */
+#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901
+  /* C99 (and later) supports 'inline' keyword */
+  /* No need for 'inline' replacement */
+#elif defined(__GNUC__) && __GNUC__ >= 3
+  /* GCC supports '__inline__' as an extension */
+#  define inline __inline__
+#elif defined(_MSC_VER) && _MSC_VER >= 1400
+  /* MSC supports '__inline' from VS 2005 (or even earlier) */
+#  define inline __inline
+#else
+  /* Probably 'inline' is not supported by compiler.
+     Define to the empty string to be on the safe side. */
+#  define inline /* empty */
+#endif
+
 #endif /* HEADER_CURL_SETUP_H */
 #endif /* HEADER_CURL_SETUP_H */

+ 2 - 6
lib/curl_setup_once.h

@@ -164,9 +164,7 @@ struct timeval {
                                    (RECV_TYPE_ARG4)(0))
                                    (RECV_TYPE_ARG4)(0))
 #else /* HAVE_RECV */
 #else /* HAVE_RECV */
 #ifndef sread
 #ifndef sread
-  /* */
-  Error Missing_definition_of_macro_sread
-  /* */
+#error "Missing definition of macro sread!"
 #endif
 #endif
 #endif /* HAVE_RECV */
 #endif /* HAVE_RECV */
 
 
@@ -184,9 +182,7 @@ struct timeval {
                                     (SEND_TYPE_ARG4)(SEND_4TH_ARG))
                                     (SEND_TYPE_ARG4)(SEND_4TH_ARG))
 #else /* HAVE_SEND */
 #else /* HAVE_SEND */
 #ifndef swrite
 #ifndef swrite
-  /* */
-  Error Missing_definition_of_macro_swrite
-  /* */
+#error "Missing definition of macro swrite!"
 #endif
 #endif
 #endif /* HAVE_SEND */
 #endif /* HAVE_SEND */
 
 

+ 29 - 23
lib/curl_sha512_256.c

@@ -44,7 +44,7 @@
 #  include <openssl/opensslv.h>
 #  include <openssl/opensslv.h>
 #  if (!defined(LIBRESSL_VERSION_NUMBER) && \
 #  if (!defined(LIBRESSL_VERSION_NUMBER) && \
         defined(OPENSSL_VERSION_NUMBER) && \
         defined(OPENSSL_VERSION_NUMBER) && \
-        (OPENSSL_VERSION_NUMBER >= 0x10100010L)) || \
+        (OPENSSL_VERSION_NUMBER >= 0x10101000L)) || \
       (defined(LIBRESSL_VERSION_NUMBER) && \
       (defined(LIBRESSL_VERSION_NUMBER) && \
         (LIBRESSL_VERSION_NUMBER >= 0x3080000fL))
         (LIBRESSL_VERSION_NUMBER >= 0x3080000fL))
 #    include <openssl/opensslconf.h>
 #    include <openssl/opensslconf.h>
@@ -52,6 +52,27 @@
 #      include <openssl/evp.h>
 #      include <openssl/evp.h>
 #      define USE_OPENSSL_SHA512_256          1
 #      define USE_OPENSSL_SHA512_256          1
 #      define HAS_SHA512_256_IMPLEMENTATION   1
 #      define HAS_SHA512_256_IMPLEMENTATION   1
+#      ifdef __NetBSD__
+/* Some NetBSD versions has a bug in SHA-512/256.
+ * See https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039
+ * The problematic versions:
+ * - NetBSD before 9.4
+ * - NetBSD 9 all development versions (9.99.x)
+ * - NetBSD 10 development versions (10.99.x) before 10.99.11
+ * The bug was fixed in NetBSD 9.4 release, NetBSD 10.0 release,
+ * NetBSD 10.99.11 development.
+ * It is safe to apply the workaround even if the bug is not present, as
+ * the workaround just reduces performance slightly. */
+#        include <sys/param.h>
+#        if  __NetBSD_Version__ <   904000000 ||  \
+            (__NetBSD_Version__ >=  999000000 &&  \
+             __NetBSD_Version__ <  1000000000) || \
+            (__NetBSD_Version__ >= 1099000000 &&  \
+             __NetBSD_Version__ <  1099001100)
+#          define NEED_NETBSD_SHA512_256_WORKAROUND 1
+#          include <string.h>
+#        endif
+#      endif
 #    endif
 #    endif
 #  endif
 #  endif
 #endif /* USE_OPENSSL */
 #endif /* USE_OPENSSL */
@@ -153,7 +174,7 @@ Curl_sha512_256_finish(unsigned char *digest,
   CURLcode ret;
   CURLcode ret;
   Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
   Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
 
 
-#ifdef __NetBSD__
+#ifdef NEED_NETBSD_SHA512_256_WORKAROUND
   /* Use a larger buffer to work around a bug in NetBSD:
   /* Use a larger buffer to work around a bug in NetBSD:
      https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 */
      https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 */
   unsigned char tmp_digest[SHA512_256_DIGEST_SIZE * 2];
   unsigned char tmp_digest[SHA512_256_DIGEST_SIZE * 2];
@@ -161,9 +182,10 @@ Curl_sha512_256_finish(unsigned char *digest,
                            tmp_digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER;
                            tmp_digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER;
   if(ret == CURLE_OK)
   if(ret == CURLE_OK)
     memcpy(digest, tmp_digest, SHA512_256_DIGEST_SIZE);
     memcpy(digest, tmp_digest, SHA512_256_DIGEST_SIZE);
-#else  /* ! __NetBSD__ */
+  explicit_memset(tmp_digest, 0, sizeof(tmp_digest));
+#else  /* ! NEED_NETBSD_SHA512_256_WORKAROUND */
   ret = EVP_DigestFinal_ex(*ctx, digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER;
   ret = EVP_DigestFinal_ex(*ctx, digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER;
-#endif /* ! __NetBSD__ */
+#endif /* ! NEED_NETBSD_SHA512_256_WORKAROUND */
 
 
   EVP_MD_CTX_destroy(*ctx);
   EVP_MD_CTX_destroy(*ctx);
   *ctx = NULL;
   *ctx = NULL;
@@ -264,29 +286,13 @@ Curl_sha512_256_finish(unsigned char *digest,
   defined(_MSC_VER) && !defined(__GNUC__) && !defined(__clang__)
   defined(_MSC_VER) && !defined(__GNUC__) && !defined(__clang__)
 #  if _MSC_VER >= 1400
 #  if _MSC_VER >= 1400
 #    define MHDX_INLINE __forceinline
 #    define MHDX_INLINE __forceinline
-#  else
-#    define MHDX_INLINE /* empty */
 #  endif
 #  endif
 #endif
 #endif
 
 
 #if !defined(MHDX_INLINE)
 #if !defined(MHDX_INLINE)
-#  if defined(inline)
-     /* Assume that 'inline' macro was already defined correctly by
-      * the build system. */
-#    define MHDX_INLINE inline
-#  elif defined(__cplusplus)
-     /* The code is compiled with C++ compiler.
-      * C++ always supports 'inline'. */
-#    define MHDX_INLINE inline
-#  elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901
-     /* C99 (and later) supports 'inline' keyword */
-#    define MHDX_INLINE inline
-#  elif defined(__GNUC__) && __GNUC__ >= 3
-     /* GCC supports '__inline__' as an extension */
-#    define MHDX_INLINE __inline__
-#  else
-#    define MHDX_INLINE /* empty */
-#  endif
+   /* Assume that 'inline' keyword works or the
+    * macro was already defined correctly. */
+#  define MHDX_INLINE inline
 #endif
 #endif
 
 
 /* Bits manipulation macros and functions.
 /* Bits manipulation macros and functions.

+ 2 - 1
lib/curl_threads.c

@@ -131,7 +131,8 @@ curl_thread_t Curl_thread_create(unsigned int (CURL_STDCALL *func) (void *),
 
 
 void Curl_thread_destroy(curl_thread_t hnd)
 void Curl_thread_destroy(curl_thread_t hnd)
 {
 {
-  CloseHandle(hnd);
+  if(hnd != curl_thread_t_null)
+    CloseHandle(hnd);
 }
 }
 
 
 int Curl_thread_join(curl_thread_t *hnd)
 int Curl_thread_join(curl_thread_t *hnd)

+ 72 - 9
lib/curl_trc.c

@@ -111,21 +111,30 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
 /* Curl_infof() is for info message along the way */
 /* Curl_infof() is for info message along the way */
 #define MAXINFO 2048
 #define MAXINFO 2048
 
 
+static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat,
+                      const char * const fmt, va_list ap)  CURL_PRINTF(3, 0);
+
+static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat,
+                      const char * const fmt, va_list ap)
+{
+  int len = 0;
+  char buffer[MAXINFO + 2];
+  if(feat)
+    len = msnprintf(buffer, MAXINFO, "[%s] ", feat->name);
+  len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
+  buffer[len++] = '\n';
+  buffer[len] = '\0';
+  Curl_debug(data, CURLINFO_TEXT, buffer, len);
+}
+
 void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
 void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
 {
 {
   DEBUGASSERT(!strchr(fmt, '\n'));
   DEBUGASSERT(!strchr(fmt, '\n'));
   if(Curl_trc_is_verbose(data)) {
   if(Curl_trc_is_verbose(data)) {
     va_list ap;
     va_list ap;
-    int len = 0;
-    char buffer[MAXINFO + 2];
-    if(data->state.feat)
-      len = msnprintf(buffer, MAXINFO, "[%s] ", data->state.feat->name);
     va_start(ap, fmt);
     va_start(ap, fmt);
-    len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
+    trc_infof(data, data->state.feat, fmt, ap);
     va_end(ap);
     va_end(ap);
-    buffer[len++] = '\n';
-    buffer[len] = '\0';
-    Curl_debug(data, CURLINFO_TEXT, buffer, len);
   }
   }
 }
 }
 
 
@@ -154,7 +163,61 @@ void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
   }
   }
 }
 }
 
 
+struct curl_trc_feat Curl_trc_feat_read = {
+  "READ",
+  CURL_LOG_LVL_NONE,
+};
+struct curl_trc_feat Curl_trc_feat_write = {
+  "WRITE",
+  CURL_LOG_LVL_NONE,
+};
+
+void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...)
+{
+  DEBUGASSERT(!strchr(fmt, '\n'));
+  if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_read)) {
+    va_list ap;
+    va_start(ap, fmt);
+    trc_infof(data, &Curl_trc_feat_read, fmt, ap);
+    va_end(ap);
+  }
+}
+
+void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...)
+{
+  DEBUGASSERT(!strchr(fmt, '\n'));
+  if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_write)) {
+    va_list ap;
+    va_start(ap, fmt);
+    trc_infof(data, &Curl_trc_feat_write, fmt, ap);
+    va_end(ap);
+  }
+}
+
+#ifndef CURL_DISABLE_FTP
+struct curl_trc_feat Curl_trc_feat_ftp = {
+  "FTP",
+  CURL_LOG_LVL_NONE,
+};
+
+void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...)
+{
+  DEBUGASSERT(!strchr(fmt, '\n'));
+  if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ftp)) {
+    va_list ap;
+    va_start(ap, fmt);
+    trc_infof(data, &Curl_trc_feat_ftp, fmt, ap);
+    va_end(ap);
+  }
+}
+#endif /* !CURL_DISABLE_FTP */
+
 static struct curl_trc_feat *trc_feats[] = {
 static struct curl_trc_feat *trc_feats[] = {
+  &Curl_trc_feat_read,
+  &Curl_trc_feat_write,
+#ifndef CURL_DISABLE_FTP
+  &Curl_trc_feat_ftp,
+#endif
 #ifndef CURL_DISABLE_DOH
 #ifndef CURL_DISABLE_DOH
   &Curl_doh_trc,
   &Curl_doh_trc,
 #endif
 #endif
@@ -188,7 +251,7 @@ static struct Curl_cftype *cf_types[] = {
   &Curl_cft_haproxy,
   &Curl_cft_haproxy,
   &Curl_cft_socks_proxy,
   &Curl_cft_socks_proxy,
 #endif /* !CURL_DISABLE_PROXY */
 #endif /* !CURL_DISABLE_PROXY */
-#ifdef ENABLE_QUIC
+#ifdef USE_HTTP3
   &Curl_cft_http3,
   &Curl_cft_http3,
 #endif
 #endif
 #if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
 #if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)

+ 68 - 7
lib/curl_trc.h

@@ -77,12 +77,32 @@ void Curl_failf(struct Curl_easy *data,
 #define CURL_TRC_CF(data, cf, ...) \
 #define CURL_TRC_CF(data, cf, ...) \
   do { if(Curl_trc_cf_is_verbose(cf, data)) \
   do { if(Curl_trc_cf_is_verbose(cf, data)) \
          Curl_trc_cf_infof(data, cf, __VA_ARGS__); } while(0)
          Curl_trc_cf_infof(data, cf, __VA_ARGS__); } while(0)
+#define CURL_TRC_WRITE(data, ...) \
+  do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_write)) \
+         Curl_trc_write(data, __VA_ARGS__); } while(0)
+#define CURL_TRC_READ(data, ...) \
+  do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_read)) \
+         Curl_trc_read(data, __VA_ARGS__); } while(0)
+
+#ifndef CURL_DISABLE_FTP
+#define CURL_TRC_FTP(data, ...) \
+  do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ftp)) \
+         Curl_trc_ftp(data, __VA_ARGS__); } while(0)
+#endif /* !CURL_DISABLE_FTP */
+
+#else /* CURL_HAVE_C99 */
 
 
-#else
 #define infof Curl_infof
 #define infof Curl_infof
 #define CURL_TRC_CF Curl_trc_cf_infof
 #define CURL_TRC_CF Curl_trc_cf_infof
+#define CURL_TRC_WRITE Curl_trc_write
+#define CURL_TRC_READ  Curl_trc_read
+
+#ifndef CURL_DISABLE_FTP
+#define CURL_TRC_FTP   Curl_trc_ftp
 #endif
 #endif
 
 
+#endif /* !CURL_HAVE_C99 */
+
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 /* informational messages enabled */
 /* informational messages enabled */
 
 
@@ -90,6 +110,8 @@ struct curl_trc_feat {
   const char *name;
   const char *name;
   int log_level;
   int log_level;
 };
 };
+extern struct curl_trc_feat Curl_trc_feat_read;
+extern struct curl_trc_feat Curl_trc_feat_write;
 
 
 #define Curl_trc_is_verbose(data) \
 #define Curl_trc_is_verbose(data) \
             ((data) && (data)->set.verbose && \
             ((data) && (data)->set.verbose && \
@@ -97,10 +119,10 @@ struct curl_trc_feat {
              ((data)->state.feat->log_level >= CURL_LOG_LVL_INFO)))
              ((data)->state.feat->log_level >= CURL_LOG_LVL_INFO)))
 #define Curl_trc_cf_is_verbose(cf, data) \
 #define Curl_trc_cf_is_verbose(cf, data) \
             (Curl_trc_is_verbose(data) && \
             (Curl_trc_is_verbose(data) && \
-            (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO)
+             (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO)
 #define Curl_trc_ft_is_verbose(data, ft) \
 #define Curl_trc_ft_is_verbose(data, ft) \
-                            (Curl_trc_is_verbose(data) && \
-                            (ft)->log_level >= CURL_LOG_LVL_INFO)
+            (Curl_trc_is_verbose(data) && \
+             (ft)->log_level >= CURL_LOG_LVL_INFO)
 
 
 /**
 /**
  * Output an informational message when transfer's verbose logging is enabled.
  * Output an informational message when transfer's verbose logging is enabled.
@@ -114,13 +136,26 @@ void Curl_infof(struct Curl_easy *data,
  */
  */
 void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
 void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
                        const char *fmt, ...) CURL_PRINTF(3, 4);
                        const char *fmt, ...) CURL_PRINTF(3, 4);
+void Curl_trc_ft_infof(struct Curl_easy *data, struct curl_trc_feat *ft,
+                       const char *fmt, ...) CURL_PRINTF(3, 4);
+void Curl_trc_write(struct Curl_easy *data,
+                    const char *fmt, ...) CURL_PRINTF(2, 3);
+void Curl_trc_read(struct Curl_easy *data,
+                   const char *fmt, ...) CURL_PRINTF(2, 3);
+
+#ifndef CURL_DISABLE_FTP
+extern struct curl_trc_feat Curl_trc_feat_ftp;
+void Curl_trc_ftp(struct Curl_easy *data,
+                  const char *fmt, ...) CURL_PRINTF(2, 3);
+#endif
+
 
 
 #else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */
 #else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */
 /* All informational messages are not compiled in for size savings */
 /* All informational messages are not compiled in for size savings */
 
 
-#define Curl_trc_is_verbose(d)        ((void)(d), FALSE)
-#define Curl_trc_cf_is_verbose(x,y)   ((void)(x), (void)(y), FALSE)
-#define Curl_trc_ft_is_verbose(x,y)   ((void)(x), (void)(y), FALSE)
+#define Curl_trc_is_verbose(d)        (FALSE)
+#define Curl_trc_cf_is_verbose(x,y)   (FALSE)
+#define Curl_trc_ft_is_verbose(x,y)   (FALSE)
 
 
 static void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
 static void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
 {
 {
@@ -134,6 +169,32 @@ static void Curl_trc_cf_infof(struct Curl_easy *data,
   (void)data; (void)cf; (void)fmt;
   (void)data; (void)cf; (void)fmt;
 }
 }
 
 
+struct curl_trc_feat;
+
+static void Curl_trc_ft_infof(struct Curl_easy *data,
+                              struct curl_trc_feat *ft,
+                              const char *fmt, ...)
+{
+  (void)data; (void)ft; (void)fmt;
+}
+
+static void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...)
+{
+  (void)data; (void)fmt;
+}
+
+static void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...)
+{
+  (void)data; (void)fmt;
+}
+
+#ifndef CURL_DISABLE_FTP
+static void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...)
+{
+  (void)data; (void)fmt;
+}
+#endif
+
 #endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */
 #endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */
 
 
 #endif /* HEADER_CURL_TRC_H */
 #endif /* HEADER_CURL_TRC_H */

+ 0 - 1
lib/curlx.h

@@ -77,7 +77,6 @@
 
 
 */
 */
 
 
-#define curlx_getenv curl_getenv
 #define curlx_mvsnprintf curl_mvsnprintf
 #define curlx_mvsnprintf curl_mvsnprintf
 #define curlx_msnprintf curl_msnprintf
 #define curlx_msnprintf curl_msnprintf
 #define curlx_maprintf curl_maprintf
 #define curlx_maprintf curl_maprintf

+ 52 - 15
lib/cw-out.c

@@ -102,6 +102,8 @@ static void cw_out_buf_free(struct cw_out_buf *cwbuf)
 struct cw_out_ctx {
 struct cw_out_ctx {
   struct Curl_cwriter super;
   struct Curl_cwriter super;
   struct cw_out_buf *buf;
   struct cw_out_buf *buf;
+  BIT(paused);
+  BIT(errored);
 };
 };
 
 
 static CURLcode cw_out_write(struct Curl_easy *data,
 static CURLcode cw_out_write(struct Curl_easy *data,
@@ -201,7 +203,10 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
   size_t max_write, min_write;
   size_t max_write, min_write;
   size_t wlen, nwritten;
   size_t wlen, nwritten;
 
 
-  (void)ctx;
+  /* If we errored once, we do not invoke the client callback  again */
+  if(ctx->errored)
+    return CURLE_WRITE_ERROR;
+
   /* write callbacks may get NULLed by the client between calls. */
   /* write callbacks may get NULLed by the client between calls. */
   cw_get_writefunc(data, otype, &wcb, &wcb_data, &max_write, &min_write);
   cw_get_writefunc(data, otype, &wcb, &wcb_data, &max_write, &min_write);
   if(!wcb) {
   if(!wcb) {
@@ -210,13 +215,16 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
   }
   }
 
 
   *pconsumed = 0;
   *pconsumed = 0;
-  while(blen && !(data->req.keepon & KEEP_RECV_PAUSE)) {
+  while(blen && !ctx->paused) {
     if(!flush_all && blen < min_write)
     if(!flush_all && blen < min_write)
       break;
       break;
     wlen = max_write? CURLMIN(blen, max_write) : blen;
     wlen = max_write? CURLMIN(blen, max_write) : blen;
     Curl_set_in_callback(data, TRUE);
     Curl_set_in_callback(data, TRUE);
     nwritten = wcb((char *)buf, 1, wlen, wcb_data);
     nwritten = wcb((char *)buf, 1, wlen, wcb_data);
     Curl_set_in_callback(data, FALSE);
     Curl_set_in_callback(data, FALSE);
+    CURL_TRC_WRITE(data, "cw_out, wrote %zu %s bytes -> %zu",
+                   wlen, (otype == CW_OUT_BODY)? "body" : "header",
+                   nwritten);
     if(CURL_WRITEFUNC_PAUSE == nwritten) {
     if(CURL_WRITEFUNC_PAUSE == nwritten) {
       if(data->conn && data->conn->handler->flags & PROTOPT_NONETWORK) {
       if(data->conn && data->conn->handler->flags & PROTOPT_NONETWORK) {
         /* Protocols that work without network cannot be paused. This is
         /* Protocols that work without network cannot be paused. This is
@@ -227,9 +235,15 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
       }
       }
       /* mark the connection as RECV paused */
       /* mark the connection as RECV paused */
       data->req.keepon |= KEEP_RECV_PAUSE;
       data->req.keepon |= KEEP_RECV_PAUSE;
+      ctx->paused = TRUE;
+      CURL_TRC_WRITE(data, "cw_out, PAUSE requested by client");
       break;
       break;
     }
     }
-    if(nwritten != wlen) {
+    else if(CURL_WRITEFUNC_ERROR == nwritten) {
+      failf(data, "client returned ERROR on write of %zu bytes", wlen);
+      return CURLE_WRITE_ERROR;
+    }
+    else if(nwritten != wlen) {
       failf(data, "Failure writing output to destination, "
       failf(data, "Failure writing output to destination, "
             "passed %zu returned %zd", wlen, nwritten);
             "passed %zu returned %zd", wlen, nwritten);
       return CURLE_WRITE_ERROR;
       return CURLE_WRITE_ERROR;
@@ -283,7 +297,7 @@ static CURLcode cw_out_flush_chain(struct cw_out_ctx *ctx,
 
 
   if(!cwbuf)
   if(!cwbuf)
     return CURLE_OK;
     return CURLE_OK;
-  if(data->req.keepon & KEEP_RECV_PAUSE)
+  if(ctx->paused)
     return CURLE_OK;
     return CURLE_OK;
 
 
   /* write the end of the chain until it blocks or gets empty */
   /* write the end of the chain until it blocks or gets empty */
@@ -296,7 +310,7 @@ static CURLcode cw_out_flush_chain(struct cw_out_ctx *ctx,
       return result;
       return result;
     if(*plast) {
     if(*plast) {
       /* could not write last, paused again? */
       /* could not write last, paused again? */
-      DEBUGASSERT(data->req.keepon & KEEP_RECV_PAUSE);
+      DEBUGASSERT(ctx->paused);
       return CURLE_OK;
       return CURLE_OK;
     }
     }
   }
   }
@@ -338,14 +352,14 @@ static CURLcode cw_out_do_write(struct cw_out_ctx *ctx,
                                 bool flush_all,
                                 bool flush_all,
                                 const char *buf, size_t blen)
                                 const char *buf, size_t blen)
 {
 {
-  CURLcode result;
+  CURLcode result = CURLE_OK;
 
 
   /* if we have buffered data and it is a different type than what
   /* if we have buffered data and it is a different type than what
    * we are writing now, try to flush all */
    * we are writing now, try to flush all */
   if(ctx->buf && ctx->buf->type != otype) {
   if(ctx->buf && ctx->buf->type != otype) {
     result = cw_out_flush_chain(ctx, data, &ctx->buf, TRUE);
     result = cw_out_flush_chain(ctx, data, &ctx->buf, TRUE);
     if(result)
     if(result)
-      return result;
+      goto out;
   }
   }
 
 
   if(ctx->buf) {
   if(ctx->buf) {
@@ -355,7 +369,7 @@ static CURLcode cw_out_do_write(struct cw_out_ctx *ctx,
       return result;
       return result;
     result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
     result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
     if(result)
     if(result)
-      return result;
+      goto out;
   }
   }
   else {
   else {
     /* nothing buffered, try direct write */
     /* nothing buffered, try direct write */
@@ -368,10 +382,18 @@ static CURLcode cw_out_do_write(struct cw_out_ctx *ctx,
       /* did not write all, append the rest */
       /* did not write all, append the rest */
       result = cw_out_append(ctx, otype, buf + consumed, blen - consumed);
       result = cw_out_append(ctx, otype, buf + consumed, blen - consumed);
       if(result)
       if(result)
-        return result;
+        goto out;
     }
     }
   }
   }
-  return CURLE_OK;
+
+out:
+  if(result) {
+    /* We do not want to invoked client callbacks a second time after
+     * encountering an error. See issue #13337 */
+    ctx->errored = TRUE;
+    cw_out_bufs_free(ctx);
+  }
+  return result;
 }
 }
 
 
 static CURLcode cw_out_write(struct Curl_easy *data,
 static CURLcode cw_out_write(struct Curl_easy *data,
@@ -409,10 +431,12 @@ bool Curl_cw_out_is_paused(struct Curl_easy *data)
     return FALSE;
     return FALSE;
 
 
   ctx = (struct cw_out_ctx *)cw_out;
   ctx = (struct cw_out_ctx *)cw_out;
-  return cw_out_bufs_len(ctx) > 0;
+  CURL_TRC_WRITE(data, "cw-out is%spaused", ctx->paused? "" : " not");
+  return ctx->paused;
 }
 }
 
 
-static CURLcode cw_out_flush(struct Curl_easy *data, bool flush_all)
+static CURLcode cw_out_flush(struct Curl_easy *data,
+                             bool unpause, bool flush_all)
 {
 {
   struct Curl_cwriter *cw_out;
   struct Curl_cwriter *cw_out;
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
@@ -420,18 +444,31 @@ static CURLcode cw_out_flush(struct Curl_easy *data, bool flush_all)
   cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out);
   cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out);
   if(cw_out) {
   if(cw_out) {
     struct cw_out_ctx *ctx = (struct cw_out_ctx *)cw_out;
     struct cw_out_ctx *ctx = (struct cw_out_ctx *)cw_out;
+    if(ctx->errored)
+      return CURLE_WRITE_ERROR;
+    if(unpause && ctx->paused)
+      ctx->paused = FALSE;
+    if(ctx->paused)
+      return CURLE_OK;  /* not doing it */
 
 
     result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
     result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
+    if(result) {
+      ctx->errored = TRUE;
+      cw_out_bufs_free(ctx);
+      return result;
+    }
   }
   }
   return result;
   return result;
 }
 }
 
 
-CURLcode Curl_cw_out_flush(struct Curl_easy *data)
+CURLcode Curl_cw_out_unpause(struct Curl_easy *data)
 {
 {
-  return cw_out_flush(data, FALSE);
+  CURL_TRC_WRITE(data, "cw-out unpause");
+  return cw_out_flush(data, TRUE, FALSE);
 }
 }
 
 
 CURLcode Curl_cw_out_done(struct Curl_easy *data)
 CURLcode Curl_cw_out_done(struct Curl_easy *data)
 {
 {
-  return cw_out_flush(data, TRUE);
+  CURL_TRC_WRITE(data, "cw-out done");
+  return cw_out_flush(data, FALSE, TRUE);
 }
 }

+ 1 - 1
lib/cw-out.h

@@ -43,7 +43,7 @@ bool Curl_cw_out_is_paused(struct Curl_easy *data);
 /**
 /**
  * Flush any buffered date to the client, chunk collation still applies.
  * Flush any buffered date to the client, chunk collation still applies.
  */
  */
-CURLcode Curl_cw_out_flush(struct Curl_easy *data);
+CURLcode Curl_cw_out_unpause(struct Curl_easy *data);
 
 
 /**
 /**
  * Mark EndOfStream reached and flush ALL data to the client.
  * Mark EndOfStream reached and flush ALL data to the client.

+ 2 - 1
lib/dict.c

@@ -76,7 +76,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done);
  */
  */
 
 
 const struct Curl_handler Curl_handler_dict = {
 const struct Curl_handler Curl_handler_dict = {
-  "DICT",                               /* scheme */
+  "dict",                               /* scheme */
   ZERO_NULL,                            /* setup_connection */
   ZERO_NULL,                            /* setup_connection */
   dict_do,                              /* do_it */
   dict_do,                              /* do_it */
   ZERO_NULL,                            /* done */
   ZERO_NULL,                            /* done */
@@ -90,6 +90,7 @@ const struct Curl_handler Curl_handler_dict = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* attach connection */
   PORT_DICT,                            /* defport */
   PORT_DICT,                            /* defport */

+ 81 - 0
lib/dllmain.c

@@ -0,0 +1,81 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_OPENSSL
+#include <openssl/crypto.h>
+#endif
+
+/* The fourth-to-last include */
+#ifdef __CYGWIN__
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#ifdef _WIN32
+#undef _WIN32
+#endif
+#endif
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+/* DllMain() must only be defined for Windows and Cygwin DLL builds. */
+#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(CURL_STATICLIB)
+
+#if defined(USE_OPENSSL) && \
+    !defined(OPENSSL_IS_AWSLC) && \
+    !defined(OPENSSL_IS_BORINGSSL) && \
+    !defined(LIBRESSL_VERSION_NUMBER) && \
+    (OPENSSL_VERSION_NUMBER >= 0x10100000L)
+#define PREVENT_OPENSSL_MEMLEAK
+#endif
+
+#ifdef PREVENT_OPENSSL_MEMLEAK
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved);
+BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
+{
+  (void)hinstDLL;
+  (void)lpvReserved;
+
+  switch(fdwReason) {
+  case DLL_PROCESS_ATTACH:
+    break;
+  case DLL_PROCESS_DETACH:
+    break;
+  case DLL_THREAD_ATTACH:
+    break;
+  case DLL_THREAD_DETACH:
+    /* Call OPENSSL_thread_stop to prevent a memory leak in case OpenSSL is
+       linked statically.
+       https://github.com/curl/curl/issues/12327#issuecomment-1826405944 */
+    OPENSSL_thread_stop();
+    break;
+  }
+  return TRUE;
+}
+#endif /* OpenSSL */
+
+#endif /* DLL build */

+ 403 - 6
lib/doh.c

@@ -42,9 +42,13 @@
 #include "curl_printf.h"
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 #include "memdebug.h"
+#include "escape.h"
 
 
 #define DNS_CLASS_IN 0x01
 #define DNS_CLASS_IN 0x01
 
 
+/* local_print_buf truncates if the hex string will be more than this */
+#define LOCAL_PB_HEXMAX 400
+
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 static const char * const errors[]={
 static const char * const errors[]={
   "",
   "",
@@ -187,6 +191,26 @@ doh_write_cb(const void *contents, size_t size, size_t nmemb, void *userp)
   return realsize;
   return realsize;
 }
 }
 
 
+#if defined(USE_HTTPSRR) && defined(CURLDEBUG)
+static void local_print_buf(struct Curl_easy *data,
+                            const char *prefix,
+                            unsigned char *buf, size_t len)
+{
+  unsigned char hexstr[LOCAL_PB_HEXMAX];
+  size_t hlen = LOCAL_PB_HEXMAX;
+  bool truncated = false;
+
+  if(len > (LOCAL_PB_HEXMAX / 2))
+    truncated = true;
+  Curl_hexencode(buf, len, hexstr, hlen);
+  if(!truncated)
+    infof(data, "%s: len=%d, val=%s", prefix, (int)len, hexstr);
+  else
+    infof(data, "%s: len=%d (truncated)val=%s", prefix, (int)len, hexstr);
+  return;
+}
+#endif
+
 /* called from multi.c when this DoH transfer is complete */
 /* called from multi.c when this DoH transfer is complete */
 static int doh_done(struct Curl_easy *doh, CURLcode result)
 static int doh_done(struct Curl_easy *doh, CURLcode result)
 {
 {
@@ -379,6 +403,12 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
   int slot;
   int slot;
   struct dohdata *dohp;
   struct dohdata *dohp;
   struct connectdata *conn = data->conn;
   struct connectdata *conn = data->conn;
+#ifdef USE_HTTPSRR
+  /* for now, this is only used when ECH is enabled */
+# ifdef USE_ECH
+  char *qname = NULL;
+# endif
+#endif
   *waitp = FALSE;
   *waitp = FALSE;
   (void)hostname;
   (void)hostname;
   (void)port;
   (void)port;
@@ -408,7 +438,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
     goto error;
     goto error;
   dohp->pending++;
   dohp->pending++;
 
 
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
   if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
     /* create IPv6 DoH request */
     /* create IPv6 DoH request */
     result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6],
     result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6],
@@ -418,6 +448,37 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
       goto error;
       goto error;
     dohp->pending++;
     dohp->pending++;
   }
   }
+#endif
+
+#ifdef USE_HTTPSRR
+  /*
+   * TODO: Figure out the conditions under which we want to make
+   * a request for an HTTPS RR when we are not doing ECH. For now,
+   * making this request breaks a bunch of DoH tests, e.g. test2100,
+   * where the additional request doesn't match the pre-cooked data
+   * files, so there's a bit of work attached to making the request
+   * in a non-ECH use-case. For the present, we'll only make the
+   * request when ECH is enabled in the build and is being used for
+   * the curl operation.
+   */
+# ifdef USE_ECH
+  if(data->set.tls_ech & CURLECH_ENABLE
+     || data->set.tls_ech & CURLECH_HARD) {
+    if(port == 443)
+      qname = strdup(hostname);
+    else
+      qname = aprintf("_%d._https.%s", port, hostname);
+    if(!qname)
+      goto error;
+    result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_HTTPS],
+                      DNS_TYPE_HTTPS, qname, data->set.str[STRING_DOH],
+                      data->multi, dohp->headers);
+    free(qname);
+    if(result)
+      goto error;
+    dohp->pending++;
+  }
+# endif
 #endif
 #endif
   *waitp = TRUE; /* this never returns synchronously */
   *waitp = TRUE; /* this never returns synchronously */
   return NULL;
   return NULL;
@@ -501,6 +562,25 @@ static DOHcode store_aaaa(const unsigned char *doh,
   return DOH_OK;
   return DOH_OK;
 }
 }
 
 
+#ifdef USE_HTTPSRR
+static DOHcode store_https(const unsigned char *doh,
+                           int index,
+                           struct dohentry *d,
+                           uint16_t len)
+{
+  /* silently ignore RRs over the limit */
+  if(d->numhttps_rrs < DOH_MAX_HTTPS) {
+    struct dohhttps_rr *h = &d->https_rrs[d->numhttps_rrs];
+    h->val = Curl_memdup(&doh[index], len);
+    if(!h->val)
+      return DOH_OUT_OF_MEM;
+    h->len = len;
+    d->numhttps_rrs++;
+  }
+  return DOH_OK;
+}
+#endif
+
 static DOHcode store_cname(const unsigned char *doh,
 static DOHcode store_cname(const unsigned char *doh,
                            size_t dohlen,
                            size_t dohlen,
                            unsigned int index,
                            unsigned int index,
@@ -563,7 +643,8 @@ static DOHcode rdata(const unsigned char *doh,
   /* RDATA
   /* RDATA
      - A (TYPE 1):  4 bytes
      - A (TYPE 1):  4 bytes
      - AAAA (TYPE 28): 16 bytes
      - AAAA (TYPE 28): 16 bytes
-     - NS (TYPE 2): N bytes */
+     - NS (TYPE 2): N bytes
+     - HTTPS (TYPE 65): N bytes */
   DOHcode rc;
   DOHcode rc;
 
 
   switch(type) {
   switch(type) {
@@ -581,6 +662,13 @@ static DOHcode rdata(const unsigned char *doh,
     if(rc)
     if(rc)
       return rc;
       return rc;
     break;
     break;
+#ifdef USE_HTTPSRR
+  case DNS_TYPE_HTTPS:
+    rc = store_https(doh, index, d, rdlength);
+    if(rc)
+      return rc;
+    break;
+#endif
   case DNS_TYPE_CNAME:
   case DNS_TYPE_CNAME:
     rc = store_cname(doh, dohlen, index, d);
     rc = store_cname(doh, dohlen, index, d);
     if(rc)
     if(rc)
@@ -737,7 +825,11 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh,
   if(index != dohlen)
   if(index != dohlen)
     return DOH_DNS_MALFORMAT; /* something is wrong */
     return DOH_DNS_MALFORMAT; /* something is wrong */
 
 
+#ifdef USE_HTTTPS
+  if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr && !d->numhttps_rrs)
+#else
   if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
   if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
+#endif
     /* nothing stored! */
     /* nothing stored! */
     return DOH_NO_CONTENT;
     return DOH_NO_CONTENT;
 
 
@@ -776,6 +868,16 @@ static void showdoh(struct Curl_easy *data,
       infof(data, "%s", buffer);
       infof(data, "%s", buffer);
     }
     }
   }
   }
+#ifdef USE_HTTPSRR
+  for(i = 0; i < d->numhttps_rrs; i++) {
+# ifdef CURLDEBUG
+    local_print_buf(data, "DoH HTTPS",
+                    d->https_rrs[i].val, d->https_rrs[i].len);
+# else
+    infof(data, "DoH HTTPS RR: length %d", d->https_rrs[i].len);
+# endif
+  }
+#endif
   for(i = 0; i < d->numcname; i++) {
   for(i = 0; i < d->numcname; i++) {
     infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i]));
     infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i]));
   }
   }
@@ -804,7 +906,7 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname,
   struct Curl_addrinfo *prevai = NULL;
   struct Curl_addrinfo *prevai = NULL;
   struct Curl_addrinfo *firstai = NULL;
   struct Curl_addrinfo *firstai = NULL;
   struct sockaddr_in *addr;
   struct sockaddr_in *addr;
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   struct sockaddr_in6 *addr6;
   struct sockaddr_in6 *addr6;
 #endif
 #endif
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
@@ -820,7 +922,7 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname,
     size_t ss_size;
     size_t ss_size;
     CURL_SA_FAMILY_T addrtype;
     CURL_SA_FAMILY_T addrtype;
     if(de->addr[i].type == DNS_TYPE_AAAA) {
     if(de->addr[i].type == DNS_TYPE_AAAA) {
-#ifndef ENABLE_IPV6
+#ifndef USE_IPV6
       /* we can't handle IPv6 addresses */
       /* we can't handle IPv6 addresses */
       continue;
       continue;
 #else
 #else
@@ -869,7 +971,7 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname,
       addr->sin_port = htons((unsigned short)port);
       addr->sin_port = htons((unsigned short)port);
       break;
       break;
 
 
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
     case AF_INET6:
     case AF_INET6:
       addr6 = (void *)ai->ai_addr; /* storage area for this info */
       addr6 = (void *)ai->ai_addr; /* storage area for this info */
       DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
       DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
@@ -895,7 +997,18 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname,
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
 static const char *type2name(DNStype dnstype)
 static const char *type2name(DNStype dnstype)
 {
 {
-  return (dnstype == DNS_TYPE_A)?"A":"AAAA";
+  switch(dnstype) {
+    case DNS_TYPE_A:
+      return "A";
+    case DNS_TYPE_AAAA:
+      return "AAAA";
+#ifdef USE_HTTPSRR
+    case DNS_TYPE_HTTPS:
+      return "HTTPS";
+#endif
+    default:
+       return "unknown";
+  }
 }
 }
 #endif
 #endif
 
 
@@ -905,8 +1018,270 @@ UNITTEST void de_cleanup(struct dohentry *d)
   for(i = 0; i < d->numcname; i++) {
   for(i = 0; i < d->numcname; i++) {
     Curl_dyn_free(&d->cname[i]);
     Curl_dyn_free(&d->cname[i]);
   }
   }
+#ifdef USE_HTTPSRR
+  for(i = 0; i < d->numhttps_rrs; i++)
+    free(d->https_rrs[i].val);
+#endif
+}
+
+#ifdef USE_HTTPSRR
+
+/*
+ * @brief decode the DNS name in a binary RRData
+ * @param buf points to the buffer (in/out)
+ * @param remaining points to the remaining buffer length (in/out)
+ * @param dnsname returns the string form name on success
+ * @return is 1 for success, error otherwise
+ *
+ * The encoding here is defined in
+ * https://tools.ietf.org/html/rfc1035#section-3.1
+ *
+ * The input buffer pointer will be modified so it points to
+ * just after the end of the DNS name encoding on output. (And
+ * that's why it's an "unsigned char **" :-)
+ */
+static CURLcode local_decode_rdata_name(unsigned char **buf, size_t *remaining,
+                                        char **dnsname)
+{
+  unsigned char *cp = NULL;
+  int rem = 0;
+  unsigned char clen = 0; /* chunk len */
+  struct dynbuf thename;
+
+  DEBUGASSERT(buf && remaining && dnsname);
+  if(!buf || !remaining || !dnsname)
+    return CURLE_OUT_OF_MEMORY;
+  rem = (int)*remaining;
+  if(rem <= 0) {
+    Curl_dyn_free(&thename);
+    return CURLE_OUT_OF_MEMORY;
+  }
+  Curl_dyn_init(&thename, CURL_MAXLEN_host_name);
+  cp = *buf;
+  clen = *cp++;
+  if(clen == 0) {
+    /* special case - return "." as name */
+    if(Curl_dyn_addn(&thename, ".", 1))
+      return CURLE_OUT_OF_MEMORY;
+  }
+  while(clen) {
+    if(clen >= rem) {
+      Curl_dyn_free(&thename);
+      return CURLE_OUT_OF_MEMORY;
+    }
+    if(Curl_dyn_addn(&thename, cp, clen) ||
+       Curl_dyn_addn(&thename, ".", 1))
+      return CURLE_TOO_LARGE;
+
+    cp += clen;
+    rem -= (clen + 1);
+    if(rem <= 0) {
+      Curl_dyn_free(&thename);
+      return CURLE_OUT_OF_MEMORY;
+    }
+    clen = *cp++;
+  }
+  *buf = cp;
+  *remaining = rem - 1;
+  *dnsname = Curl_dyn_ptr(&thename);
+  return CURLE_OK;
 }
 }
 
 
+static CURLcode local_decode_rdata_alpn(unsigned char *rrval, size_t len,
+                                        char **alpns)
+{
+  /*
+   * spec here is as per draft-ietf-dnsop-svcb-https, section-7.1.1
+   * encoding is catenated list of strings each preceded by a one
+   * octet length
+   * output is comma-sep list of the strings
+   * implementations may or may not handle quoting of comma within
+   * string values, so we might see a comma within the wire format
+   * version of a string, in which case we'll precede that by a
+   * backslash - same goes for a backslash character, and of course
+   * we need to use two backslashes in strings when we mean one;-)
+   */
+  int remaining = (int) len;
+  char *oval;
+  size_t i;
+  unsigned char *cp = rrval;
+  struct dynbuf dval;
+
+  if(!alpns)
+    return CURLE_OUT_OF_MEMORY;
+  Curl_dyn_init(&dval, DYN_DOH_RESPONSE);
+  remaining = (int)len;
+  cp = rrval;
+  while(remaining > 0) {
+    size_t tlen = (size_t) *cp++;
+
+    /* if not 1st time, add comma */
+    if(remaining != (int)len && Curl_dyn_addn(&dval, ",", 1))
+      goto err;
+    remaining--;
+    if(tlen > (size_t)remaining)
+      goto err;
+    /* add escape char if needed, clunky but easier to read */
+    for(i = 0; i != tlen; i++) {
+      if('\\' == *cp || ',' == *cp) {
+        if(Curl_dyn_addn(&dval, "\\", 1))
+          goto err;
+      }
+      if(Curl_dyn_addn(&dval, cp++, 1))
+        goto err;
+    }
+    remaining -= (int)tlen;
+  }
+  /* this string is always null terminated */
+  oval = Curl_dyn_ptr(&dval);
+  if(!oval)
+    goto err;
+  *alpns = oval;
+  return CURLE_OK;
+err:
+  Curl_dyn_free(&dval);
+  return CURLE_BAD_CONTENT_ENCODING;
+}
+
+#ifdef CURLDEBUG
+static CURLcode test_alpn_escapes(void)
+{
+  /* we'll use an example from draft-ietf-dnsop-svcb, figure 10 */
+  static unsigned char example[] = {
+    0x08,                                           /* length 8 */
+    0x66, 0x5c, 0x6f, 0x6f, 0x2c, 0x62, 0x61, 0x72, /* value "f\\oo,bar" */
+    0x02,                                           /* length 2 */
+    0x68, 0x32                                      /* value "h2" */
+  };
+  size_t example_len = sizeof(example);
+  char *aval = NULL;
+  static const char *expected = "f\\\\oo\\,bar,h2";
+
+  if(local_decode_rdata_alpn(example, example_len, &aval) != CURLE_OK)
+    return CURLE_BAD_CONTENT_ENCODING;
+  if(strlen(aval) != strlen(expected))
+    return CURLE_BAD_CONTENT_ENCODING;
+  if(memcmp(aval, expected, strlen(aval)))
+    return CURLE_BAD_CONTENT_ENCODING;
+  return CURLE_OK;
+}
+#endif
+
+static CURLcode Curl_doh_decode_httpsrr(unsigned char *rrval, size_t len,
+                                        struct Curl_https_rrinfo **hrr)
+{
+  size_t remaining = len;
+  unsigned char *cp = rrval;
+  uint16_t pcode = 0, plen = 0;
+  struct Curl_https_rrinfo *lhrr = NULL;
+  char *dnsname = NULL;
+
+#ifdef CURLDEBUG
+  /* a few tests of escaping, shouldn't be here but ok for now */
+  if(test_alpn_escapes() != CURLE_OK)
+    return CURLE_OUT_OF_MEMORY;
+#endif
+  lhrr = calloc(1, sizeof(struct Curl_https_rrinfo));
+  if(!lhrr)
+    return CURLE_OUT_OF_MEMORY;
+  lhrr->val = Curl_memdup(rrval, len);
+  if(!lhrr->val)
+    goto err;
+  lhrr->len = len;
+  if(remaining <= 2)
+    goto err;
+  lhrr->priority = (uint16_t)((cp[0] << 8) + cp[1]);
+  cp += 2;
+  remaining -= (uint16_t)2;
+  if(local_decode_rdata_name(&cp, &remaining, &dnsname) != CURLE_OK)
+    goto err;
+  lhrr->target = dnsname;
+  while(remaining >= 4) {
+    pcode = (uint16_t)((*cp << 8) + (*(cp + 1)));
+    cp += 2;
+    plen = (uint16_t)((*cp << 8) + (*(cp + 1)));
+    cp += 2;
+    remaining -= 4;
+    if(pcode == HTTPS_RR_CODE_ALPN) {
+      if(local_decode_rdata_alpn(cp, plen, &lhrr->alpns) != CURLE_OK)
+        goto err;
+    }
+    if(pcode == HTTPS_RR_CODE_NO_DEF_ALPN)
+      lhrr->no_def_alpn = TRUE;
+    else if(pcode == HTTPS_RR_CODE_IPV4) {
+      lhrr->ipv4hints = Curl_memdup(cp, plen);
+      if(!lhrr->ipv4hints)
+        goto err;
+      lhrr->ipv4hints_len = (size_t)plen;
+    }
+    else if(pcode == HTTPS_RR_CODE_ECH) {
+      lhrr->echconfiglist = Curl_memdup(cp, plen);
+      if(!lhrr->echconfiglist)
+        goto err;
+      lhrr->echconfiglist_len = (size_t)plen;
+    }
+    else if(pcode == HTTPS_RR_CODE_IPV6) {
+      lhrr->ipv6hints = Curl_memdup(cp, plen);
+      if(!lhrr->ipv6hints)
+        goto err;
+      lhrr->ipv6hints_len = (size_t)plen;
+    }
+    if(plen > 0 && plen <= remaining) {
+      cp += plen;
+      remaining -= plen;
+    }
+  }
+  DEBUGASSERT(!remaining);
+  *hrr = lhrr;
+  return CURLE_OK;
+err:
+  if(lhrr) {
+    free(lhrr->target);
+    free(lhrr->echconfiglist);
+    free(lhrr->val);
+    free(lhrr);
+  }
+  return CURLE_OUT_OF_MEMORY;
+}
+
+# ifdef CURLDEBUG
+static void local_print_httpsrr(struct Curl_easy *data,
+                                struct Curl_https_rrinfo *hrr)
+{
+  DEBUGASSERT(hrr);
+  infof(data, "HTTPS RR: priority %d, target: %s",
+        hrr->priority, hrr->target);
+  if(hrr->alpns)
+    infof(data, "HTTPS RR: alpns %s", hrr->alpns);
+  else
+    infof(data, "HTTPS RR: no alpns");
+  if(hrr->no_def_alpn)
+    infof(data, "HTTPS RR: no_def_alpn set");
+  else
+    infof(data, "HTTPS RR: no_def_alpn not set");
+  if(hrr->ipv4hints) {
+    local_print_buf(data, "HTTPS RR: ipv4hints",
+                    hrr->ipv4hints, hrr->ipv4hints_len);
+  }
+  else
+    infof(data, "HTTPS RR: no ipv4hints");
+  if(hrr->echconfiglist) {
+    local_print_buf(data, "HTTPS RR: ECHConfigList",
+                    hrr->echconfiglist, hrr->echconfiglist_len);
+  }
+  else
+    infof(data, "HTTPS RR: no ECHConfigList");
+  if(hrr->ipv6hints) {
+    local_print_buf(data, "HTTPS RR: ipv6hint",
+                    hrr->ipv6hints, hrr->ipv6hints_len);
+  }
+  else
+    infof(data, "HTTPS RR: no ipv6hints");
+  return;
+}
+# endif
+#endif
+
 CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
 CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
                               struct Curl_dns_entry **dnsp)
                               struct Curl_dns_entry **dnsp)
 {
 {
@@ -923,9 +1298,15 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
       CURLE_COULDNT_RESOLVE_HOST;
       CURLE_COULDNT_RESOLVE_HOST;
   }
   }
   else if(!dohp->pending) {
   else if(!dohp->pending) {
+#ifndef USE_HTTPSRR
     DOHcode rc[DOH_PROBE_SLOTS] = {
     DOHcode rc[DOH_PROBE_SLOTS] = {
       DOH_OK, DOH_OK
       DOH_OK, DOH_OK
     };
     };
+#else
+    DOHcode rc[DOH_PROBE_SLOTS] = {
+      DOH_OK, DOH_OK, DOH_OK
+    };
+#endif
     struct dohentry de;
     struct dohentry de;
     int slot;
     int slot;
     /* remove DoH handles from multi handle and close them */
     /* remove DoH handles from multi handle and close them */
@@ -991,6 +1372,22 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
     } /* address processing done */
     } /* address processing done */
 
 
     /* Now process any build-specific attributes retrieved from DNS */
     /* Now process any build-specific attributes retrieved from DNS */
+#ifdef USE_HTTPSRR
+    if(de.numhttps_rrs > 0 && result == CURLE_OK && *dnsp) {
+      struct Curl_https_rrinfo *hrr = NULL;
+      result = Curl_doh_decode_httpsrr(de.https_rrs->val, de.https_rrs->len,
+                                       &hrr);
+      if(result) {
+        infof(data, "Failed to decode HTTPS RR");
+        return result;
+      }
+      infof(data, "Some HTTPS RR to process");
+# ifdef CURLDEBUG
+      local_print_httpsrr(data, hrr);
+# endif
+      (*dnsp)->hinfo = hrr;
+    }
+#endif
 
 
     /* All done */
     /* All done */
     de_cleanup(&de);
     de_cleanup(&de);

+ 38 - 3
lib/doh.h

@@ -26,6 +26,9 @@
 
 
 #include "urldata.h"
 #include "urldata.h"
 #include "curl_addrinfo.h"
 #include "curl_addrinfo.h"
+#ifdef USE_HTTPSRR
+# include <stdint.h>
+#endif
 
 
 #ifndef CURL_DISABLE_DOH
 #ifndef CURL_DISABLE_DOH
 
 
@@ -51,7 +54,8 @@ typedef enum {
   DNS_TYPE_NS = 2,
   DNS_TYPE_NS = 2,
   DNS_TYPE_CNAME = 5,
   DNS_TYPE_CNAME = 5,
   DNS_TYPE_AAAA = 28,
   DNS_TYPE_AAAA = 28,
-  DNS_TYPE_DNAME = 39           /* RFC6672 */
+  DNS_TYPE_DNAME = 39,           /* RFC6672 */
+  DNS_TYPE_HTTPS = 65
 } DNStype;
 } DNStype;
 
 
 /* one of these for each DoH request */
 /* one of these for each DoH request */
@@ -84,10 +88,9 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
 CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
 CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
                               struct Curl_dns_entry **dns);
                               struct Curl_dns_entry **dns);
 
 
-int Curl_doh_getsock(struct connectdata *conn, curl_socket_t *socks);
-
 #define DOH_MAX_ADDR 24
 #define DOH_MAX_ADDR 24
 #define DOH_MAX_CNAME 4
 #define DOH_MAX_CNAME 4
+#define DOH_MAX_HTTPS 4
 
 
 struct dohaddr {
 struct dohaddr {
   int type;
   int type;
@@ -97,12 +100,44 @@ struct dohaddr {
   } ip;
   } ip;
 };
 };
 
 
+#ifdef USE_HTTPSRR
+
+/*
+ * These are the code points for DNS wire format SvcParams as
+ * per draft-ietf-dnsop-svcb-https
+ * Not all are supported now, and even those that are may need
+ * more work in future to fully support the spec.
+ */
+#define HTTPS_RR_CODE_ALPN            0x01
+#define HTTPS_RR_CODE_NO_DEF_ALPN     0x02
+#define HTTPS_RR_CODE_PORT            0x03
+#define HTTPS_RR_CODE_IPV4            0x04
+#define HTTPS_RR_CODE_ECH             0x05
+#define HTTPS_RR_CODE_IPV6            0x06
+
+/*
+ * These may need escaping when found within an alpn string
+ * value.
+ */
+#define COMMA_CHAR                    ','
+#define BACKSLASH_CHAR                '\\'
+
+struct dohhttps_rr {
+  uint16_t len; /* raw encoded length */
+  unsigned char *val; /* raw encoded octets */
+};
+#endif
+
 struct dohentry {
 struct dohentry {
   struct dynbuf cname[DOH_MAX_CNAME];
   struct dynbuf cname[DOH_MAX_CNAME];
   struct dohaddr addr[DOH_MAX_ADDR];
   struct dohaddr addr[DOH_MAX_ADDR];
   int numaddr;
   int numaddr;
   unsigned int ttl;
   unsigned int ttl;
   int numcname;
   int numcname;
+#ifdef USE_HTTPSRR
+  struct dohhttps_rr https_rrs[DOH_MAX_HTTPS];
+  int numhttps_rrs;
+#endif
 };
 };
 
 
 
 

+ 1 - 1
lib/dynbuf.c

@@ -213,7 +213,7 @@ CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
   }
   }
   /* If we failed, we cleanup the whole buffer and return error */
   /* If we failed, we cleanup the whole buffer and return error */
   Curl_dyn_free(s);
   Curl_dyn_free(s);
-  return CURLE_OK;
+  return CURLE_OUT_OF_MEMORY;
 #endif
 #endif
 }
 }
 
 

+ 38 - 40
lib/easy.c

@@ -58,7 +58,6 @@
 #include "multiif.h"
 #include "multiif.h"
 #include "select.h"
 #include "select.h"
 #include "cfilters.h"
 #include "cfilters.h"
-#include "cw-out.h"
 #include "sendf.h" /* for failf function prototype */
 #include "sendf.h" /* for failf function prototype */
 #include "connect.h" /* for Curl_getconnectinfo */
 #include "connect.h" /* for Curl_getconnectinfo */
 #include "slist.h"
 #include "slist.h"
@@ -729,6 +728,8 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
     /* clear this as early as possible */
     /* clear this as early as possible */
     data->set.errorbuffer[0] = 0;
     data->set.errorbuffer[0] = 0;
 
 
+  data->state.os_errno = 0;
+
   if(data->multi) {
   if(data->multi) {
     failf(data, "easy handle already used in multi handle");
     failf(data, "easy handle already used in multi handle");
     return CURLE_FAILED_INIT;
     return CURLE_FAILED_INIT;
@@ -1086,6 +1087,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
   int oldstate;
   int oldstate;
   int newstate;
   int newstate;
   bool recursive = FALSE;
   bool recursive = FALSE;
+  bool keep_changed, unpause_read, not_all_paused;
 
 
   if(!GOOD_EASY_HANDLE(data) || !data->conn)
   if(!GOOD_EASY_HANDLE(data) || !data->conn)
     /* crazy input, don't continue */
     /* crazy input, don't continue */
@@ -1101,51 +1103,47 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
     ((action & CURLPAUSE_RECV)?KEEP_RECV_PAUSE:0) |
     ((action & CURLPAUSE_RECV)?KEEP_RECV_PAUSE:0) |
     ((action & CURLPAUSE_SEND)?KEEP_SEND_PAUSE:0);
     ((action & CURLPAUSE_SEND)?KEEP_SEND_PAUSE:0);
 
 
-  if((newstate & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) == oldstate) {
-    /* Not changing any pause state, return */
-    DEBUGF(infof(data, "pause: no change, early return"));
-    return CURLE_OK;
-  }
-
-  /* Unpause parts in active mime tree. */
-  if((k->keepon & ~newstate & KEEP_SEND_PAUSE) &&
-     (data->mstate == MSTATE_PERFORMING ||
-      data->mstate == MSTATE_RATELIMITING)) {
-    result = Curl_creader_unpause(data);
-    if(result)
-      return result;
-  }
-
-  /* put it back in the keepon */
+  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_readwrite(). 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;
   k->keepon = newstate;
 
 
-  if(!(newstate & KEEP_RECV_PAUSE)) {
-    Curl_conn_ev_data_pause(data, FALSE);
-    result = Curl_cw_out_flush(data);
-    if(result)
-      return result;
-  }
-
-  /* if there's no error and we're not pausing both directions, we want
-     to have this handle checked soon */
-  if((newstate & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) !=
-     (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) {
-    Curl_expire(data, 0, EXPIRE_RUN_NOW); /* get this handle going again */
-
+  /* If not completely pausing both directions now, run again in any case. */
+  if(not_all_paused) {
+    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;
-
-    if(!Curl_cw_out_is_paused(data))
-      /* if not pausing again, 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_CSELECT_OUT;
-    if(data->multi) {
-      if(Curl_update_timer(data->multi))
-        return CURLE_ABORTED_BY_CALLBACK;
+    /* Simulate socket events on next run for unpaused directions */
+    if(!(newstate & KEEP_SEND_PAUSE))
+      data->state.select_bits |= CURL_CSELECT_OUT;
+    if(!(newstate & KEEP_RECV_PAUSE))
+      data->state.select_bits |= CURL_CSELECT_IN;
+    /* On changes, tell application to update its timers. */
+    if(keep_changed && data->multi) {
+      if(Curl_update_timer(data->multi)) {
+        result = CURLE_ABORTED_BY_CALLBACK;
+        goto out;
+      }
     }
     }
   }
   }
 
 
-  if(!data->state.done)
+  if(unpause_read) {
+    result = Curl_creader_unpause(data);
+    if(result)
+      goto out;
+  }
+
+out:
+  if(!result && !data->state.done && keep_changed)
     /* This transfer may have been moved in or out of the bundle, update the
     /* This transfer may have been moved in or out of the bundle, update the
        corresponding socket callback, if used */
        corresponding socket callback, if used */
     result = Curl_updatesocket(data);
     result = Curl_updatesocket(data);
@@ -1305,7 +1303,7 @@ static int conn_upkeep(struct Curl_easy *data,
     conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE);
     conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE);
   }
   }
   else {
   else {
-    /* Do the generic action on the FIRSTSOCKE filter chain */
+    /* Do the generic action on the FIRSTSOCKET filter chain */
     Curl_conn_keep_alive(data, conn, FIRSTSOCKET);
     Curl_conn_keep_alive(data, conn, FIRSTSOCKET);
   }
   }
   Curl_detach_connection(data);
   Curl_detach_connection(data);

+ 2 - 1
lib/easyoptions.c

@@ -86,6 +86,7 @@ struct curl_easyoption Curl_easyopts[] = {
   {"DOH_SSL_VERIFYPEER", CURLOPT_DOH_SSL_VERIFYPEER, CURLOT_LONG, 0},
   {"DOH_SSL_VERIFYPEER", CURLOPT_DOH_SSL_VERIFYPEER, CURLOT_LONG, 0},
   {"DOH_SSL_VERIFYSTATUS", CURLOPT_DOH_SSL_VERIFYSTATUS, CURLOT_LONG, 0},
   {"DOH_SSL_VERIFYSTATUS", CURLOPT_DOH_SSL_VERIFYSTATUS, CURLOT_LONG, 0},
   {"DOH_URL", CURLOPT_DOH_URL, CURLOT_STRING, 0},
   {"DOH_URL", CURLOPT_DOH_URL, CURLOT_STRING, 0},
+  {"ECH", CURLOPT_ECH, CURLOT_STRING, 0},
   {"EGDSOCKET", CURLOPT_EGDSOCKET, CURLOT_STRING, 0},
   {"EGDSOCKET", CURLOPT_EGDSOCKET, CURLOT_STRING, 0},
   {"ENCODING", CURLOPT_ACCEPT_ENCODING, CURLOT_STRING, CURLOT_FLAG_ALIAS},
   {"ENCODING", CURLOPT_ACCEPT_ENCODING, CURLOT_STRING, CURLOT_FLAG_ALIAS},
   {"ERRORBUFFER", CURLOPT_ERRORBUFFER, CURLOT_OBJECT, 0},
   {"ERRORBUFFER", CURLOPT_ERRORBUFFER, CURLOT_OBJECT, 0},
@@ -375,6 +376,6 @@ struct curl_easyoption Curl_easyopts[] = {
  */
  */
 int Curl_easyopts_check(void)
 int Curl_easyopts_check(void)
 {
 {
-  return ((CURLOPT_LASTENTRY%10000) != (324 + 1));
+  return ((CURLOPT_LASTENTRY%10000) != (325 + 1));
 }
 }
 #endif
 #endif

+ 76 - 34
lib/file.c

@@ -50,6 +50,14 @@
 #include <fcntl.h>
 #include <fcntl.h>
 #endif
 #endif
 
 
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
 #include "strtoofft.h"
 #include "strtoofft.h"
 #include "urldata.h"
 #include "urldata.h"
 #include <curl/curl.h>
 #include <curl/curl.h>
@@ -101,7 +109,7 @@ static CURLcode file_setup_connection(struct Curl_easy *data,
  */
  */
 
 
 const struct Curl_handler Curl_handler_file = {
 const struct Curl_handler Curl_handler_file = {
-  "FILE",                               /* scheme */
+  "file",                               /* scheme */
   file_setup_connection,                /* setup_connection */
   file_setup_connection,                /* setup_connection */
   file_do,                              /* do_it */
   file_do,                              /* do_it */
   file_done,                            /* done */
   file_done,                            /* done */
@@ -115,6 +123,7 @@ const struct Curl_handler Curl_handler_file = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* perform_getsock */
   file_disconnect,                      /* disconnect */
   file_disconnect,                      /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* attach connection */
   0,                                    /* defport */
   0,                                    /* defport */
@@ -446,12 +455,9 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
     fstated = TRUE;
     fstated = TRUE;
   }
   }
 
 
-  if(fstated && !data->state.range && data->set.timecondition) {
-    if(!Curl_meets_timecondition(data, data->info.filetime)) {
-      *done = TRUE;
-      return CURLE_OK;
-    }
-  }
+  if(fstated && !data->state.range && data->set.timecondition &&
+     !Curl_meets_timecondition(data, data->info.filetime))
+    return CURLE_OK;
 
 
   if(fstated) {
   if(fstated) {
     time_t filetime;
     time_t filetime;
@@ -543,49 +549,85 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
     Curl_pgrsSetDownloadSize(data, expected_size);
     Curl_pgrsSetDownloadSize(data, expected_size);
 
 
   if(data->state.resume_from) {
   if(data->state.resume_from) {
-    if(data->state.resume_from !=
-       lseek(fd, data->state.resume_from, SEEK_SET))
+    if(!S_ISDIR(statbuf.st_mode)) {
+      if(data->state.resume_from !=
+          lseek(fd, data->state.resume_from, SEEK_SET))
+        return CURLE_BAD_DOWNLOAD_RESUME;
+    }
+    else {
       return CURLE_BAD_DOWNLOAD_RESUME;
       return CURLE_BAD_DOWNLOAD_RESUME;
+    }
   }
   }
 
 
   result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen);
   result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen);
   if(result)
   if(result)
     goto out;
     goto out;
 
 
-  while(!result) {
-    ssize_t nread;
-    /* Don't fill a whole buffer if we want less than all data */
-    size_t bytestoread;
+  if(!S_ISDIR(statbuf.st_mode)) {
+    while(!result) {
+      ssize_t nread;
+      /* Don't fill a whole buffer if we want less than all data */
+      size_t bytestoread;
 
 
-    if(size_known) {
-      bytestoread = (expected_size < (curl_off_t)(xfer_blen-1)) ?
-        curlx_sotouz(expected_size) : (xfer_blen-1);
-    }
-    else
-      bytestoread = xfer_blen-1;
+      if(size_known) {
+        bytestoread = (expected_size < (curl_off_t)(xfer_blen-1)) ?
+          curlx_sotouz(expected_size) : (xfer_blen-1);
+      }
+      else
+        bytestoread = xfer_blen-1;
 
 
-    nread = read(fd, xfer_buf, bytestoread);
+      nread = read(fd, xfer_buf, bytestoread);
 
 
-    if(nread > 0)
-      xfer_buf[nread] = 0;
+      if(nread > 0)
+        xfer_buf[nread] = 0;
 
 
-    if(nread <= 0 || (size_known && (expected_size == 0)))
-      break;
+      if(nread <= 0 || (size_known && (expected_size == 0)))
+        break;
 
 
-    if(size_known)
-      expected_size -= nread;
+      if(size_known)
+        expected_size -= nread;
 
 
-    result = Curl_client_write(data, CLIENTWRITE_BODY, xfer_buf, nread);
-    if(result)
-      goto out;
+      result = Curl_client_write(data, CLIENTWRITE_BODY, xfer_buf, nread);
+      if(result)
+        goto out;
 
 
-    if(Curl_pgrsUpdate(data))
-      result = CURLE_ABORTED_BY_CALLBACK;
-    else
-      result = Curl_speedcheck(data, Curl_now());
-    if(result)
+      if(Curl_pgrsUpdate(data))
+        result = CURLE_ABORTED_BY_CALLBACK;
+      else
+        result = Curl_speedcheck(data, Curl_now());
+      if(result)
+        goto out;
+    }
+  }
+  else {
+#ifdef HAVE_OPENDIR
+    DIR *dir = opendir(file->path);
+    struct dirent *entry;
+
+    if(!dir) {
+      result = CURLE_READ_ERROR;
       goto out;
       goto out;
+    }
+    else {
+      while((entry = readdir(dir))) {
+        if(entry->d_name[0] != '.') {
+          result = Curl_client_write(data, CLIENTWRITE_BODY,
+                   entry->d_name, strlen(entry->d_name));
+          if(result)
+            break;
+          result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1);
+          if(result)
+            break;
+        }
+      }
+      closedir(dir);
+    }
+#else
+    failf(data, "Directory listing not yet implemented on this platform.");
+    result = CURLE_READ_ERROR;
+#endif
   }
   }
+
   if(Curl_pgrsUpdate(data))
   if(Curl_pgrsUpdate(data))
     result = CURLE_ABORTED_BY_CALLBACK;
     result = CURLE_ABORTED_BY_CALLBACK;
 
 

+ 121 - 102
lib/ftp.c

@@ -95,19 +95,89 @@
 
 
 #ifdef CURL_DISABLE_VERBOSE_STRINGS
 #ifdef CURL_DISABLE_VERBOSE_STRINGS
 #define ftp_pasv_verbose(a,b,c,d)  Curl_nop_stmt
 #define ftp_pasv_verbose(a,b,c,d)  Curl_nop_stmt
+#define FTP_CSTATE(c)   ""
+#define FTP_DSTATE(d)   ""
+#else /* CURL_DISABLE_VERBOSE_STRINGS */
+  /* for tracing purposes */
+static const char * const ftp_state_names[]={
+  "STOP",
+  "WAIT220",
+  "AUTH",
+  "USER",
+  "PASS",
+  "ACCT",
+  "PBSZ",
+  "PROT",
+  "CCC",
+  "PWD",
+  "SYST",
+  "NAMEFMT",
+  "QUOTE",
+  "RETR_PREQUOTE",
+  "STOR_PREQUOTE",
+  "POSTQUOTE",
+  "CWD",
+  "MKD",
+  "MDTM",
+  "TYPE",
+  "LIST_TYPE",
+  "RETR_TYPE",
+  "STOR_TYPE",
+  "SIZE",
+  "RETR_SIZE",
+  "STOR_SIZE",
+  "REST",
+  "RETR_REST",
+  "PORT",
+  "PRET",
+  "PASV",
+  "LIST",
+  "RETR",
+  "STOR",
+  "QUIT"
+};
+#define FTP_CSTATE(c)   ((c)? ftp_state_names[(c)->proto.ftpc.state] : "???")
+#define FTP_DSTATE(d)   (((d) && (d)->conn)? \
+                         ftp_state_names[(d)->conn->proto.ftpc.state] : "???")
+
+#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
+
+/* This is the ONLY way to change FTP state! */
+static void _ftp_state(struct Curl_easy *data,
+                       ftpstate newstate
+#ifdef DEBUGBUILD
+                       , int lineno
+#endif
+  )
+{
+  struct connectdata *conn = data->conn;
+  struct ftp_conn *ftpc = &conn->proto.ftpc;
+
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+#ifdef DEBUGBUILD
+  (void)lineno;
+#endif
+#else /* CURL_DISABLE_VERBOSE_STRINGS */
+  if(ftpc->state != newstate)
+#ifdef DEBUGBUILD
+    CURL_TRC_FTP(data, "[%s] -> [%s] (line %d)", FTP_DSTATE(data),
+                 ftp_state_names[newstate], lineno);
+#else
+    CURL_TRC_FTP(data, "[%s] -> [%s]", FTP_DSTATE(data),
+                 ftp_state_names[newstate]);
 #endif
 #endif
+#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
+
+  ftpc->state = newstate;
+}
+
 
 
 /* Local API functions */
 /* Local API functions */
 #ifndef DEBUGBUILD
 #ifndef DEBUGBUILD
-static void _ftp_state(struct Curl_easy *data,
-                       ftpstate newstate);
 #define ftp_state(x,y) _ftp_state(x,y)
 #define ftp_state(x,y) _ftp_state(x,y)
-#else
-static void _ftp_state(struct Curl_easy *data,
-                       ftpstate newstate,
-                       int lineno);
+#else /* !DEBUGBUILD */
 #define ftp_state(x,y) _ftp_state(x,y,__LINE__)
 #define ftp_state(x,y) _ftp_state(x,y,__LINE__)
-#endif
+#endif /* DEBUGBUILD */
 
 
 static CURLcode ftp_sendquote(struct Curl_easy *data,
 static CURLcode ftp_sendquote(struct Curl_easy *data,
                               struct connectdata *conn,
                               struct connectdata *conn,
@@ -163,7 +233,7 @@ static CURLcode ftp_dophase_done(struct Curl_easy *data,
  */
  */
 
 
 const struct Curl_handler Curl_handler_ftp = {
 const struct Curl_handler Curl_handler_ftp = {
-  "FTP",                           /* scheme */
+  "ftp",                           /* scheme */
   ftp_setup_connection,            /* setup_connection */
   ftp_setup_connection,            /* setup_connection */
   ftp_do,                          /* do_it */
   ftp_do,                          /* do_it */
   ftp_done,                        /* done */
   ftp_done,                        /* done */
@@ -177,6 +247,7 @@ const struct Curl_handler Curl_handler_ftp = {
   ZERO_NULL,                       /* perform_getsock */
   ZERO_NULL,                       /* perform_getsock */
   ftp_disconnect,                  /* disconnect */
   ftp_disconnect,                  /* disconnect */
   ZERO_NULL,                       /* write_resp */
   ZERO_NULL,                       /* write_resp */
+  ZERO_NULL,                       /* write_resp_hd */
   ZERO_NULL,                       /* connection_check */
   ZERO_NULL,                       /* connection_check */
   ZERO_NULL,                       /* attach connection */
   ZERO_NULL,                       /* attach connection */
   PORT_FTP,                        /* defport */
   PORT_FTP,                        /* defport */
@@ -194,7 +265,7 @@ const struct Curl_handler Curl_handler_ftp = {
  */
  */
 
 
 const struct Curl_handler Curl_handler_ftps = {
 const struct Curl_handler Curl_handler_ftps = {
-  "FTPS",                          /* scheme */
+  "ftps",                          /* scheme */
   ftp_setup_connection,            /* setup_connection */
   ftp_setup_connection,            /* setup_connection */
   ftp_do,                          /* do_it */
   ftp_do,                          /* do_it */
   ftp_done,                        /* done */
   ftp_done,                        /* done */
@@ -208,6 +279,7 @@ const struct Curl_handler Curl_handler_ftps = {
   ZERO_NULL,                       /* perform_getsock */
   ZERO_NULL,                       /* perform_getsock */
   ftp_disconnect,                  /* disconnect */
   ftp_disconnect,                  /* disconnect */
   ZERO_NULL,                       /* write_resp */
   ZERO_NULL,                       /* write_resp */
+  ZERO_NULL,                       /* write_resp_hd */
   ZERO_NULL,                       /* connection_check */
   ZERO_NULL,                       /* connection_check */
   ZERO_NULL,                       /* attach connection */
   ZERO_NULL,                       /* attach connection */
   PORT_FTPS,                       /* defport */
   PORT_FTPS,                       /* defport */
@@ -221,6 +293,7 @@ const struct Curl_handler Curl_handler_ftps = {
 static void close_secondarysocket(struct Curl_easy *data,
 static void close_secondarysocket(struct Curl_easy *data,
                                   struct connectdata *conn)
                                   struct connectdata *conn)
 {
 {
+  CURL_TRC_FTP(data, "[%s] closing DATA connection", FTP_DSTATE(data));
   Curl_conn_close(data, SECONDARYSOCKET);
   Curl_conn_close(data, SECONDARYSOCKET);
   Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET);
   Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET);
 }
 }
@@ -360,7 +433,7 @@ static CURLcode AcceptServerConnect(struct Curl_easy *data)
   struct connectdata *conn = data->conn;
   struct connectdata *conn = data->conn;
   curl_socket_t sock = conn->sock[SECONDARYSOCKET];
   curl_socket_t sock = conn->sock[SECONDARYSOCKET];
   curl_socket_t s = CURL_SOCKET_BAD;
   curl_socket_t s = CURL_SOCKET_BAD;
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   struct Curl_sockaddr_storage add;
   struct Curl_sockaddr_storage add;
 #else
 #else
   struct sockaddr_in add;
   struct sockaddr_in add;
@@ -386,8 +459,10 @@ static CURLcode AcceptServerConnect(struct Curl_easy *data)
   (void)curlx_nonblock(s, TRUE); /* enable non-blocking */
   (void)curlx_nonblock(s, TRUE); /* enable non-blocking */
   /* Replace any filter on SECONDARY with one listening on this socket */
   /* Replace any filter on SECONDARY with one listening on this socket */
   result = Curl_conn_tcp_accepted_set(data, conn, SECONDARYSOCKET, &s);
   result = Curl_conn_tcp_accepted_set(data, conn, SECONDARYSOCKET, &s);
-  if(result)
+  if(result) {
+    sclose(s);
     return result;
     return result;
+  }
 
 
   if(data->set.fsockopt) {
   if(data->set.fsockopt) {
     int error = 0;
     int error = 0;
@@ -562,7 +637,7 @@ static CURLcode InitiateTransfer(struct Curl_easy *data)
   struct connectdata *conn = data->conn;
   struct connectdata *conn = data->conn;
   bool connected;
   bool connected;
 
 
-  DEBUGF(infof(data, "ftp InitiateTransfer()"));
+  CURL_TRC_FTP(data, "InitiateTransfer()");
   if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port &&
   if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port &&
      !Curl_conn_is_ssl(conn, SECONDARYSOCKET)) {
      !Curl_conn_is_ssl(conn, SECONDARYSOCKET)) {
     result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET);
     result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET);
@@ -645,7 +720,7 @@ static CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected)
   }
   }
 
 
 out:
 out:
-  DEBUGF(infof(data, "ftp AllowServerConnect() -> %d", result));
+  CURL_TRC_FTP(data, "AllowServerConnect() -> %d", result);
   return result;
   return result;
 }
 }
 
 
@@ -827,73 +902,6 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
   return result;
   return result;
 }
 }
 
 
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-  /* for debug purposes */
-static const char * const ftp_state_names[]={
-  "STOP",
-  "WAIT220",
-  "AUTH",
-  "USER",
-  "PASS",
-  "ACCT",
-  "PBSZ",
-  "PROT",
-  "CCC",
-  "PWD",
-  "SYST",
-  "NAMEFMT",
-  "QUOTE",
-  "RETR_PREQUOTE",
-  "STOR_PREQUOTE",
-  "POSTQUOTE",
-  "CWD",
-  "MKD",
-  "MDTM",
-  "TYPE",
-  "LIST_TYPE",
-  "RETR_TYPE",
-  "STOR_TYPE",
-  "SIZE",
-  "RETR_SIZE",
-  "STOR_SIZE",
-  "REST",
-  "RETR_REST",
-  "PORT",
-  "PRET",
-  "PASV",
-  "LIST",
-  "RETR",
-  "STOR",
-  "QUIT"
-};
-#endif
-
-/* This is the ONLY way to change FTP state! */
-static void _ftp_state(struct Curl_easy *data,
-                       ftpstate newstate
-#ifdef DEBUGBUILD
-                       , int lineno
-#endif
-  )
-{
-  struct connectdata *conn = data->conn;
-  struct ftp_conn *ftpc = &conn->proto.ftpc;
-
-#if defined(DEBUGBUILD)
-
-#if defined(CURL_DISABLE_VERBOSE_STRINGS)
-  (void) lineno;
-#else
-  if(ftpc->state != newstate)
-    infof(data, "FTP %p (line %d) state change from %s to %s",
-          (void *)ftpc, lineno, ftp_state_names[ftpc->state],
-          ftp_state_names[newstate]);
-#endif
-#endif
-
-  ftpc->state = newstate;
-}
-
 static CURLcode ftp_state_user(struct Curl_easy *data,
 static CURLcode ftp_state_user(struct Curl_easy *data,
                                struct connectdata *conn)
                                struct connectdata *conn)
 {
 {
@@ -937,7 +945,7 @@ static int ftp_domore_getsock(struct Curl_easy *data,
    * remote site, or we could wait for that site to connect to us. Or just
    * remote site, or we could wait for that site to connect to us. Or just
    * handle ordinary commands.
    * handle ordinary commands.
    */
    */
-  DEBUGF(infof(data, "ftp_domore_getsock()"));
+  CURL_TRC_FTP(data, "[%s] ftp_domore_getsock()", FTP_DSTATE(data));
 
 
   if(FTP_STOP == ftpc->state) {
   if(FTP_STOP == ftpc->state) {
     /* if stopped and still in this state, then we're also waiting for a
     /* if stopped and still in this state, then we're also waiting for a
@@ -1027,7 +1035,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
   char hbuf[NI_MAXHOST];
   char hbuf[NI_MAXHOST];
   struct sockaddr *sa = (struct sockaddr *)&ss;
   struct sockaddr *sa = (struct sockaddr *)&ss;
   struct sockaddr_in * const sa4 = (void *)sa;
   struct sockaddr_in * const sa4 = (void *)sa;
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   struct sockaddr_in6 * const sa6 = (void *)sa;
   struct sockaddr_in6 * const sa6 = (void *)sa;
 #endif
 #endif
   static const char mode[][5] = { "EPRT", "PORT" };
   static const char mode[][5] = { "EPRT", "PORT" };
@@ -1054,7 +1062,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
      (strlen(data->set.str[STRING_FTPPORT]) > 1)) {
      (strlen(data->set.str[STRING_FTPPORT]) > 1)) {
     char *ip_end = NULL;
     char *ip_end = NULL;
 
 
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
     if(*string_ftpport == '[') {
     if(*string_ftpport == '[') {
       /* [ipv6]:port(-range) */
       /* [ipv6]:port(-range) */
       char *ip_start = string_ftpport + 1;
       char *ip_start = string_ftpport + 1;
@@ -1076,7 +1084,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
         if(ip_end) {
         if(ip_end) {
           /* either ipv6 or (ipv4|domain|interface):port(-range) */
           /* either ipv6 or (ipv4|domain|interface):port(-range) */
           addrlen = ip_end - string_ftpport;
           addrlen = ip_end - string_ftpport;
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
           if(Curl_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) {
           if(Curl_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) {
             /* ipv6 */
             /* ipv6 */
             port_min = port_max = 0;
             port_min = port_max = 0;
@@ -1122,7 +1130,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
 
 
       /* 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(conn->remote_addr->family,
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
                         Curl_ipv6_scope(&conn->remote_addr->sa_addr),
                         Curl_ipv6_scope(&conn->remote_addr->sa_addr),
                         conn->scope_id,
                         conn->scope_id,
 #endif
 #endif
@@ -1154,7 +1162,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
       goto out;
       goto out;
     }
     }
     switch(sa->sa_family) {
     switch(sa->sa_family) {
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
     case AF_INET6:
     case AF_INET6:
       r = Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
       r = Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
       break;
       break;
@@ -1204,7 +1212,8 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
           Curl_strerror(error, buffer, sizeof(buffer)));
           Curl_strerror(error, buffer, sizeof(buffer)));
     goto out;
     goto out;
   }
   }
-  DEBUGF(infof(data, "ftp_state_use_port(), opened socket"));
+  CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), opened socket",
+               FTP_DSTATE(data));
 
 
   /* step 3, bind to a suitable local address */
   /* step 3, bind to a suitable local address */
 
 
@@ -1214,7 +1223,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
   for(port = port_min; port <= port_max;) {
   for(port = port_min; port <= port_max;) {
     if(sa->sa_family == AF_INET)
     if(sa->sa_family == AF_INET)
       sa4->sin_port = htons(port);
       sa4->sin_port = htons(port);
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
     else
     else
       sa6->sin6_port = htons(port);
       sa6->sin6_port = htons(port);
 #endif
 #endif
@@ -1265,7 +1274,8 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
           Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
           Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
     goto out;
     goto out;
   }
   }
-  DEBUGF(infof(data, "ftp_state_use_port(), socket bound to port %d", port));
+  CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), socket bound to port %d",
+               FTP_DSTATE(data), port);
 
 
   /* step 4, listen on the socket */
   /* step 4, listen on the socket */
 
 
@@ -1274,7 +1284,8 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
           Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
           Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
     goto out;
     goto out;
   }
   }
-  DEBUGF(infof(data, "ftp_state_use_port(), listening on %d", port));
+  CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), listening on %d",
+               FTP_DSTATE(data), port);
 
 
   /* step 5, send the proper FTP command */
   /* step 5, send the proper FTP command */
 
 
@@ -1282,7 +1293,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
      below */
      below */
   Curl_printable_address(ai, myhost, sizeof(myhost));
   Curl_printable_address(ai, myhost, sizeof(myhost));
 
 
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
   if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
     /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
     /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
        request and enable EPRT again! */
        request and enable EPRT again! */
@@ -1309,7 +1320,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
     case AF_INET:
     case AF_INET:
       port = ntohs(sa4->sin_port);
       port = ntohs(sa4->sin_port);
       break;
       break;
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
     case AF_INET6:
     case AF_INET6:
       port = ntohs(sa6->sin6_port);
       port = ntohs(sa6->sin6_port);
       break;
       break;
@@ -2349,7 +2360,7 @@ static CURLcode ftp_state_retr(struct Curl_easy *data,
   struct connectdata *conn = data->conn;
   struct connectdata *conn = data->conn;
   struct ftp_conn *ftpc = &conn->proto.ftpc;
   struct ftp_conn *ftpc = &conn->proto.ftpc;
 
 
-  DEBUGF(infof(data, "ftp_state_retr()"));
+  CURL_TRC_FTP(data, "[%s] ftp_state_retr()", FTP_DSTATE(data));
   if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
   if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
     failf(data, "Maximum file size exceeded");
     failf(data, "Maximum file size exceeded");
     return CURLE_FILESIZE_EXCEEDED;
     return CURLE_FILESIZE_EXCEEDED;
@@ -3064,7 +3075,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
         }
         }
       }
       }
       ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
       ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
-      DEBUGF(infof(data, "protocol connect phase DONE"));
+      CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data));
       break;
       break;
 
 
     case FTP_SYST:
     case FTP_SYST:
@@ -3109,7 +3120,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
       }
       }
 
 
       ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
       ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
-      DEBUGF(infof(data, "protocol connect phase DONE"));
+      CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data));
       break;
       break;
 
 
     case FTP_NAMEFMT:
     case FTP_NAMEFMT:
@@ -3120,7 +3131,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
       }
       }
 
 
       ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
       ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
-      DEBUGF(infof(data, "protocol connect phase DONE"));
+      CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data));
       break;
       break;
 
 
     case FTP_QUOTE:
     case FTP_QUOTE:
@@ -3549,6 +3560,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
   /* Send any post-transfer QUOTE strings? */
   /* Send any post-transfer QUOTE strings? */
   if(!status && !result && !premature && data->set.postquote)
   if(!status && !result && !premature && data->set.postquote)
     result = ftp_sendquote(data, conn, data->set.postquote);
     result = ftp_sendquote(data, conn, data->set.postquote);
+  CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_DSTATE(data), result);
   Curl_safefree(ftp->pathalloc);
   Curl_safefree(ftp->pathalloc);
   return result;
   return result;
 }
 }
@@ -3817,7 +3829,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
   if(!ftpc->wait_data_conn) {
   if(!ftpc->wait_data_conn) {
     /* no waiting for the data connection so this is now complete */
     /* no waiting for the data connection so this is now complete */
     *completep = 1;
     *completep = 1;
-    DEBUGF(infof(data, "DO-MORE phase ends with %d", (int)result));
+    CURL_TRC_FTP(data, "[%s] DO-MORE phase ends with %d", FTP_DSTATE(data),
+                 (int)result);
   }
   }
 
 
   return result;
   return result;
@@ -3841,7 +3854,7 @@ CURLcode ftp_perform(struct Curl_easy *data,
   /* this is FTP and no proxy */
   /* this is FTP and no proxy */
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
 
 
-  DEBUGF(infof(data, "DO phase starts"));
+  CURL_TRC_FTP(data, "[%s] DO phase starts", FTP_DSTATE(data));
 
 
   if(data->req.no_body) {
   if(data->req.no_body) {
     /* requested no body means no transfer... */
     /* requested no body means no transfer... */
@@ -3861,10 +3874,15 @@ CURLcode ftp_perform(struct Curl_easy *data,
 
 
   *connected = Curl_conn_is_connected(data->conn, SECONDARYSOCKET);
   *connected = Curl_conn_is_connected(data->conn, SECONDARYSOCKET);
 
 
-  infof(data, "ftp_perform ends with SECONDARY: %d", *connected);
+  if(*connected)
+    infof(data, "[FTP] [%s] perform, DATA connection established",
+          FTP_DSTATE(data));
+  else
+    CURL_TRC_FTP(data, "[%s] perform, awaiting DATA connect",
+                 FTP_DSTATE(data));
 
 
   if(*dophase_done)
   if(*dophase_done)
-    DEBUGF(infof(data, "DO phase is complete1"));
+    CURL_TRC_FTP(data, "[%s] DO phase is complete1", FTP_DSTATE(data));
 
 
   return result;
   return result;
 }
 }
@@ -4431,11 +4449,11 @@ static CURLcode ftp_doing(struct Curl_easy *data,
   CURLcode result = ftp_multi_statemach(data, dophase_done);
   CURLcode result = ftp_multi_statemach(data, dophase_done);
 
 
   if(result)
   if(result)
-    DEBUGF(infof(data, "DO phase failed"));
+    CURL_TRC_FTP(data, "[%s] DO phase failed", FTP_DSTATE(data));
   else if(*dophase_done) {
   else if(*dophase_done) {
     result = ftp_dophase_done(data, FALSE /* not connected */);
     result = ftp_dophase_done(data, FALSE /* not connected */);
 
 
-    DEBUGF(infof(data, "DO phase is complete2"));
+    CURL_TRC_FTP(data, "[%s] DO phase is complete2", FTP_DSTATE(data));
   }
   }
   return result;
   return result;
 }
 }
@@ -4559,6 +4577,7 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data,
   ftpc->use_ssl = data->set.use_ssl;
   ftpc->use_ssl = data->set.use_ssl;
   ftpc->ccc = data->set.ftp_ccc;
   ftpc->ccc = data->set.ftp_ccc;
 
 
+  CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(conn), result);
   return result;
   return result;
 }
 }
 
 

+ 1 - 1
lib/ftplistparser.c

@@ -349,7 +349,7 @@ static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data,
   Curl_set_in_callback(data, false);
   Curl_set_in_callback(data, false);
 
 
   if(add) {
   if(add) {
-    Curl_llist_insert_next(llist, llist->tail, finfo, &infop->list);
+    Curl_llist_append(llist, finfo, &infop->list);
   }
   }
   else {
   else {
     Curl_fileinfo_cleanup(infop);
     Curl_fileinfo_cleanup(infop);

+ 4 - 0
lib/getinfo.c

@@ -161,7 +161,11 @@ static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info,
     *param_charp = data->info.primary.local_ip;
     *param_charp = data->info.primary.local_ip;
     break;
     break;
   case CURLINFO_RTSP_SESSION_ID:
   case CURLINFO_RTSP_SESSION_ID:
+#ifndef CURL_DISABLE_RTSP
     *param_charp = data->set.str[STRING_RTSP_SESSION_ID];
     *param_charp = data->set.str[STRING_RTSP_SESSION_ID];
+#else
+    *param_charp = NULL;
+#endif
     break;
     break;
   case CURLINFO_SCHEME:
   case CURLINFO_SCHEME:
     *param_charp = data->info.conn_scheme;
     *param_charp = data->info.conn_scheme;

+ 4 - 2
lib/gopher.c

@@ -62,7 +62,7 @@ static CURLcode gopher_connecting(struct Curl_easy *data, bool *done);
  */
  */
 
 
 const struct Curl_handler Curl_handler_gopher = {
 const struct Curl_handler Curl_handler_gopher = {
-  "GOPHER",                             /* scheme */
+  "gopher",                             /* scheme */
   ZERO_NULL,                            /* setup_connection */
   ZERO_NULL,                            /* setup_connection */
   gopher_do,                            /* do_it */
   gopher_do,                            /* do_it */
   ZERO_NULL,                            /* done */
   ZERO_NULL,                            /* done */
@@ -76,6 +76,7 @@ const struct Curl_handler Curl_handler_gopher = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* attach connection */
   PORT_GOPHER,                          /* defport */
   PORT_GOPHER,                          /* defport */
@@ -86,7 +87,7 @@ const struct Curl_handler Curl_handler_gopher = {
 
 
 #ifdef USE_SSL
 #ifdef USE_SSL
 const struct Curl_handler Curl_handler_gophers = {
 const struct Curl_handler Curl_handler_gophers = {
-  "GOPHERS",                            /* scheme */
+  "gophers",                            /* scheme */
   ZERO_NULL,                            /* setup_connection */
   ZERO_NULL,                            /* setup_connection */
   gopher_do,                            /* do_it */
   gopher_do,                            /* do_it */
   ZERO_NULL,                            /* done */
   ZERO_NULL,                            /* done */
@@ -100,6 +101,7 @@ const struct Curl_handler Curl_handler_gophers = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* attach connection */
   PORT_GOPHER,                          /* defport */
   PORT_GOPHER,                          /* defport */

+ 30 - 27
lib/hash.c

@@ -57,7 +57,7 @@ hash_element_dtor(void *user, void *element)
  */
  */
 void
 void
 Curl_hash_init(struct Curl_hash *h,
 Curl_hash_init(struct Curl_hash *h,
-               int slots,
+               size_t slots,
                hash_function hfunc,
                hash_function hfunc,
                comp_function comparator,
                comp_function comparator,
                Curl_hash_dtor dtor)
                Curl_hash_dtor dtor)
@@ -111,7 +111,7 @@ Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p)
   DEBUGASSERT(h);
   DEBUGASSERT(h);
   DEBUGASSERT(h->slots);
   DEBUGASSERT(h->slots);
   if(!h->table) {
   if(!h->table) {
-    int i;
+    size_t i;
     h->table = malloc(h->slots * sizeof(struct Curl_llist));
     h->table = malloc(h->slots * sizeof(struct Curl_llist));
     if(!h->table)
     if(!h->table)
       return NULL; /* OOM */
       return NULL; /* OOM */
@@ -132,7 +132,7 @@ Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p)
 
 
   he = mk_hash_element(key, key_len, p);
   he = mk_hash_element(key, key_len, p);
   if(he) {
   if(he) {
-    Curl_llist_insert_next(l, l->tail, he, &he->list);
+    Curl_llist_append(l, he, &he->list);
     ++h->size;
     ++h->size;
     return p; /* return the new entry */
     return p; /* return the new entry */
   }
   }
@@ -192,25 +192,6 @@ Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len)
   return NULL;
   return NULL;
 }
 }
 
 
-#if defined(DEBUGBUILD) && defined(AGGRESSIVE_TEST)
-void
-Curl_hash_apply(Curl_hash *h, void *user,
-                void (*cb)(void *user, void *ptr))
-{
-  struct Curl_llist_element  *le;
-  int                  i;
-
-  for(i = 0; i < h->slots; ++i) {
-    for(le = (h->table[i])->head;
-        le;
-        le = le->next) {
-      Curl_hash_element *el = le->ptr;
-      cb(user, el->ptr);
-    }
-  }
-}
-#endif
-
 /* Destroys all the entries in the given hash and resets its attributes,
 /* Destroys all the entries in the given hash and resets its attributes,
  * prepping the given hash for [static|dynamic] deallocation.
  * prepping the given hash for [static|dynamic] deallocation.
  *
  *
@@ -222,7 +203,7 @@ void
 Curl_hash_destroy(struct Curl_hash *h)
 Curl_hash_destroy(struct Curl_hash *h)
 {
 {
   if(h->table) {
   if(h->table) {
-    int i;
+    size_t i;
     for(i = 0; i < h->slots; ++i) {
     for(i = 0; i < h->slots; ++i) {
       Curl_llist_destroy(&h->table[i], (void *) h);
       Curl_llist_destroy(&h->table[i], (void *) h);
     }
     }
@@ -250,7 +231,7 @@ Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user,
   struct Curl_llist_element *le;
   struct Curl_llist_element *le;
   struct Curl_llist_element *lnext;
   struct Curl_llist_element *lnext;
   struct Curl_llist *list;
   struct Curl_llist *list;
-  int i;
+  size_t i;
 
 
   if(!h || !h->table)
   if(!h || !h->table)
     return;
     return;
@@ -316,7 +297,7 @@ Curl_hash_next_element(struct Curl_hash_iterator *iter)
 
 
   /* If we have reached the end of the list, find the next one */
   /* If we have reached the end of the list, find the next one */
   if(!iter->current_element) {
   if(!iter->current_element) {
-    int i;
+    size_t i;
     for(i = iter->slot_index; i < h->slots; i++) {
     for(i = iter->slot_index; i < h->slots; i++) {
       if(h->table[i].head) {
       if(h->table[i].head) {
         iter->current_element = h->table[i].head;
         iter->current_element = h->table[i].head;
@@ -339,7 +320,7 @@ void Curl_hash_print(struct Curl_hash *h,
 {
 {
   struct Curl_hash_iterator iter;
   struct Curl_hash_iterator iter;
   struct Curl_hash_element *he;
   struct Curl_hash_element *he;
-  int last_index = -1;
+  size_t last_index = ~0;
 
 
   if(!h)
   if(!h)
     return;
     return;
@@ -352,7 +333,7 @@ void Curl_hash_print(struct Curl_hash *h,
   while(he) {
   while(he) {
     if(iter.slot_index != last_index) {
     if(iter.slot_index != last_index) {
       fprintf(stderr, "index %d:", iter.slot_index);
       fprintf(stderr, "index %d:", iter.slot_index);
-      if(last_index >= 0) {
+      if(last_index != ~0) {
         fprintf(stderr, "\n");
         fprintf(stderr, "\n");
       }
       }
       last_index = iter.slot_index;
       last_index = iter.slot_index;
@@ -368,3 +349,25 @@ void Curl_hash_print(struct Curl_hash *h,
   fprintf(stderr, "\n");
   fprintf(stderr, "\n");
 }
 }
 #endif
 #endif
+
+void Curl_hash_offt_init(struct Curl_hash *h,
+                         size_t slots,
+                         Curl_hash_dtor dtor)
+{
+  Curl_hash_init(h, slots, Curl_hash_str, Curl_str_key_compare, dtor);
+}
+
+void *Curl_hash_offt_set(struct Curl_hash *h, curl_off_t id, void *elem)
+{
+  return Curl_hash_add(h, &id, sizeof(id), elem);
+}
+
+int Curl_hash_offt_remove(struct Curl_hash *h, curl_off_t id)
+{
+  return Curl_hash_delete(h, &id, sizeof(id));
+}
+
+void *Curl_hash_offt_get(struct Curl_hash *h, curl_off_t id)
+{
+  return Curl_hash_pick(h, &id, sizeof(id));
+}

+ 11 - 5
lib/hash.h

@@ -54,7 +54,7 @@ struct Curl_hash {
   /* Comparator function to compare keys */
   /* Comparator function to compare keys */
   comp_function comp_func;
   comp_function comp_func;
   Curl_hash_dtor   dtor;
   Curl_hash_dtor   dtor;
-  int slots;
+  size_t slots;
   size_t size;
   size_t size;
 };
 };
 
 
@@ -67,12 +67,12 @@ struct Curl_hash_element {
 
 
 struct Curl_hash_iterator {
 struct Curl_hash_iterator {
   struct Curl_hash *hash;
   struct Curl_hash *hash;
-  int slot_index;
+  size_t slot_index;
   struct Curl_llist_element *current_element;
   struct Curl_llist_element *current_element;
 };
 };
 
 
 void Curl_hash_init(struct Curl_hash *h,
 void Curl_hash_init(struct Curl_hash *h,
-                    int slots,
+                    size_t slots,
                     hash_function hfunc,
                     hash_function hfunc,
                     comp_function comparator,
                     comp_function comparator,
                     Curl_hash_dtor dtor);
                     Curl_hash_dtor dtor);
@@ -80,8 +80,6 @@ void Curl_hash_init(struct Curl_hash *h,
 void *Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p);
 void *Curl_hash_add(struct Curl_hash *h, void *key, size_t key_len, void *p);
 int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len);
 int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len);
 void *Curl_hash_pick(struct Curl_hash *, void *key, size_t key_len);
 void *Curl_hash_pick(struct Curl_hash *, void *key, size_t key_len);
-void Curl_hash_apply(struct Curl_hash *h, void *user,
-                     void (*cb)(void *user, void *ptr));
 #define Curl_hash_count(h) ((h)->size)
 #define Curl_hash_count(h) ((h)->size)
 void Curl_hash_destroy(struct Curl_hash *h);
 void Curl_hash_destroy(struct Curl_hash *h);
 void Curl_hash_clean(struct Curl_hash *h);
 void Curl_hash_clean(struct Curl_hash *h);
@@ -98,5 +96,13 @@ Curl_hash_next_element(struct Curl_hash_iterator *iter);
 void Curl_hash_print(struct Curl_hash *h,
 void Curl_hash_print(struct Curl_hash *h,
                      void (*func)(void *));
                      void (*func)(void *));
 
 
+/* Hash for `curl_off_t` as key */
+void Curl_hash_offt_init(struct Curl_hash *h, size_t slots,
+                         Curl_hash_dtor dtor);
+
+void *Curl_hash_offt_set(struct Curl_hash *h, curl_off_t id, void *elem);
+int Curl_hash_offt_remove(struct Curl_hash *h, curl_off_t id);
+void *Curl_hash_offt_get(struct Curl_hash *h, curl_off_t id);
+
 
 
 #endif /* HEADER_CURL_HASH_H */
 #endif /* HEADER_CURL_HASH_H */

+ 5 - 5
lib/headers.c

@@ -253,7 +253,7 @@ static CURLcode unfold_value(struct Curl_easy *data, const char *value,
   newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1);
   newhs = Curl_saferealloc(hs, sizeof(*hs) + vlen + oalloc + 1);
   if(!newhs)
   if(!newhs)
     return CURLE_OUT_OF_MEMORY;
     return CURLE_OUT_OF_MEMORY;
-  /* ->name' and ->value point into ->buffer (to keep the header allocation
+  /* ->name and ->value point into ->buffer (to keep the header allocation
      in a single memory block), which now potentially have moved. Adjust
      in a single memory block), which now potentially have moved. Adjust
      them. */
      them. */
   newhs->name = newhs->buffer;
   newhs->name = newhs->buffer;
@@ -264,8 +264,7 @@ static CURLcode unfold_value(struct Curl_easy *data, const char *value,
   newhs->value[olen + vlen] = 0; /* null-terminate at newline */
   newhs->value[olen + vlen] = 0; /* null-terminate at newline */
 
 
   /* insert this node into the list of headers */
   /* insert this node into the list of headers */
-  Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
-                         newhs, &newhs->node);
+  Curl_llist_append(&data->state.httphdrs, newhs, &newhs->node);
   data->state.prevhead = newhs;
   data->state.prevhead = newhs;
   return CURLE_OK;
   return CURLE_OK;
 }
 }
@@ -328,8 +327,7 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
     hs->request = data->state.requests;
     hs->request = data->state.requests;
 
 
     /* insert this node into the list of headers */
     /* insert this node into the list of headers */
-    Curl_llist_insert_next(&data->state.httphdrs, data->state.httphdrs.tail,
-                           hs, &hs->node);
+    Curl_llist_append(&data->state.httphdrs, hs, &hs->node);
     data->state.prevhead = hs;
     data->state.prevhead = hs;
   }
   }
   else
   else
@@ -361,6 +359,8 @@ static CURLcode hds_cw_collect_write(struct Curl_easy *data,
         (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER :
         (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER :
          CURLH_HEADER)));
          CURLH_HEADER)));
     CURLcode result = Curl_headers_push(data, buf, htype);
     CURLcode result = Curl_headers_push(data, buf, htype);
+    CURL_TRC_WRITE(data, "header_collect pushed(type=%x, len=%zu) -> %d",
+                   htype, blen, result);
     if(result)
     if(result)
       return result;
       return result;
   }
   }

+ 33 - 21
lib/hostip.c

@@ -144,7 +144,7 @@ void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf,
     (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize);
     (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize);
     break;
     break;
   }
   }
-#ifdef ENABLE_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;
@@ -167,17 +167,12 @@ create_hostcache_id(const char *name,
                     int port, char *ptr, size_t buflen)
                     int port, char *ptr, size_t buflen)
 {
 {
   size_t len = nlen ? nlen : strlen(name);
   size_t len = nlen ? nlen : strlen(name);
-  size_t olen = 0;
   DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN);
   DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN);
   if(len > (buflen - 7))
   if(len > (buflen - 7))
     len = buflen - 7;
     len = buflen - 7;
   /* store and lower case the name */
   /* store and lower case the name */
-  while(len--) {
-    *ptr++ = Curl_raw_tolower(*name++);
-    olen++;
-  }
-  olen += msnprintf(ptr, 7, ":%u", port);
-  return olen;
+  Curl_strntolower(ptr, name, len);
+  return msnprintf(&ptr[len], 7, ":%u", port) + len;
 }
 }
 
 
 struct hostcache_prune_data {
 struct hostcache_prune_data {
@@ -249,7 +244,7 @@ void Curl_hostcache_prune(struct Curl_easy *data)
   if(data->share)
   if(data->share)
     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
 
 
-  time(&now);
+  now = time(NULL);
 
 
   do {
   do {
     /* Remove outdated and unused entries from the hostcache */
     /* Remove outdated and unused entries from the hostcache */
@@ -303,7 +298,7 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
     /* See whether the returned entry is stale. Done before we release lock */
     /* See whether the returned entry is stale. Done before we release lock */
     struct hostcache_prune_data user;
     struct hostcache_prune_data user;
 
 
-    time(&user.now);
+    user.now = time(NULL);
     user.cache_timeout = data->set.dns_cache_timeout;
     user.cache_timeout = data->set.dns_cache_timeout;
     user.oldest = 0;
     user.oldest = 0;
 
 
@@ -523,7 +518,7 @@ Curl_cache_addr(struct Curl_easy *data,
   return dns;
   return dns;
 }
 }
 
 
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
 /* return a static IPv6 ::1 for the name */
 /* return a static IPv6 ::1 for the name */
 static struct Curl_addrinfo *get_localhost6(int port, const char *name)
 static struct Curl_addrinfo *get_localhost6(int port, const char *name)
 {
 {
@@ -600,7 +595,7 @@ static struct Curl_addrinfo *get_localhost(int port, const char *name)
   return ca6;
   return ca6;
 }
 }
 
 
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
 /*
 /*
  * Curl_ipv6works() returns TRUE if IPv6 seems to work.
  * Curl_ipv6works() returns TRUE if IPv6 seems to work.
  */
  */
@@ -632,7 +627,7 @@ bool Curl_ipv6works(struct Curl_easy *data)
     return (ipv6_works>0)?TRUE:FALSE;
     return (ipv6_works>0)?TRUE:FALSE;
   }
   }
 }
 }
-#endif /* ENABLE_IPV6 */
+#endif /* USE_IPV6 */
 
 
 /*
 /*
  * Curl_host_is_ipnum() returns TRUE if the given string is a numerical IPv4
  * Curl_host_is_ipnum() returns TRUE if the given string is a numerical IPv4
@@ -641,11 +636,11 @@ bool Curl_ipv6works(struct Curl_easy *data)
 bool Curl_host_is_ipnum(const char *hostname)
 bool Curl_host_is_ipnum(const char *hostname)
 {
 {
   struct in_addr in;
   struct in_addr in;
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   struct in6_addr in6;
   struct in6_addr in6;
 #endif
 #endif
   if(Curl_inet_pton(AF_INET, hostname, &in) > 0
   if(Curl_inet_pton(AF_INET, hostname, &in) > 0
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
      || Curl_inet_pton(AF_INET6, hostname, &in6) > 0
      || Curl_inet_pton(AF_INET6, hostname, &in6) > 0
 #endif
 #endif
     )
     )
@@ -760,7 +755,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
       if(!addr)
       if(!addr)
         return CURLRESOLV_ERROR;
         return CURLRESOLV_ERROR;
     }
     }
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
     else {
     else {
       struct in6_addr in6;
       struct in6_addr in6;
       /* check if this is an IPv6 address string */
       /* check if this is an IPv6 address string */
@@ -771,7 +766,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
           return CURLRESOLV_ERROR;
           return CURLRESOLV_ERROR;
       }
       }
     }
     }
-#endif /* ENABLE_IPV6 */
+#endif /* USE_IPV6 */
 
 
 #else /* if USE_RESOLVE_ON_IPS */
 #else /* if USE_RESOLVE_ON_IPS */
 #ifndef CURL_DISABLE_DOH
 #ifndef CURL_DISABLE_DOH
@@ -779,7 +774,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
     if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
     if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
       /* This is a dotted IP address 123.123.123.123-style */
       /* This is a dotted IP address 123.123.123.123-style */
       ipnum = TRUE;
       ipnum = TRUE;
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
     else {
     else {
       struct in6_addr in6;
       struct in6_addr in6;
       /* check if this is an IPv6 address string */
       /* check if this is an IPv6 address string */
@@ -787,7 +782,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
         /* This is an IPv6 address literal */
         /* This is an IPv6 address literal */
         ipnum = TRUE;
         ipnum = TRUE;
     }
     }
-#endif /* ENABLE_IPV6 */
+#endif /* USE_IPV6 */
 #endif /* CURL_DISABLE_DOH */
 #endif /* CURL_DISABLE_DOH */
 
 
 #endif /* !USE_RESOLVE_ON_IPS */
 #endif /* !USE_RESOLVE_ON_IPS */
@@ -1070,6 +1065,23 @@ static void freednsentry(void *freethis)
   dns->inuse--;
   dns->inuse--;
   if(dns->inuse == 0) {
   if(dns->inuse == 0) {
     Curl_freeaddrinfo(dns->addr);
     Curl_freeaddrinfo(dns->addr);
+#ifdef USE_HTTPSRR
+    if(dns->hinfo) {
+      if(dns->hinfo->target)
+        free(dns->hinfo->target);
+      if(dns->hinfo->alpns)
+        free(dns->hinfo->alpns);
+      if(dns->hinfo->ipv4hints)
+        free(dns->hinfo->ipv4hints);
+      if(dns->hinfo->echconfiglist)
+        free(dns->hinfo->echconfiglist);
+      if(dns->hinfo->ipv6hints)
+        free(dns->hinfo->ipv6hints);
+      if(dns->hinfo->val)
+        free(dns->hinfo->val);
+      free(dns->hinfo);
+    }
+#endif
     free(dns);
     free(dns);
   }
   }
 }
 }
@@ -1077,7 +1089,7 @@ static void freednsentry(void *freethis)
 /*
 /*
  * Curl_init_dnscache() inits a new DNS cache.
  * Curl_init_dnscache() inits a new DNS cache.
  */
  */
-void Curl_init_dnscache(struct Curl_hash *hash, int size)
+void Curl_init_dnscache(struct Curl_hash *hash, size_t size)
 {
 {
   Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare,
   Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare,
                  freednsentry);
                  freednsentry);
@@ -1210,7 +1222,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
         memcpy(address, addr_begin, alen);
         memcpy(address, addr_begin, alen);
         address[alen] = '\0';
         address[alen] = '\0';
 
 
-#ifndef ENABLE_IPV6
+#ifndef USE_IPV6
         if(strchr(address, ':')) {
         if(strchr(address, ':')) {
           infof(data, "Ignoring resolve address '%s', missing IPv6 support.",
           infof(data, "Ignoring resolve address '%s', missing IPv6 support.",
                 address);
                 address);

+ 39 - 2
lib/hostip.h

@@ -32,6 +32,10 @@
 
 
 #include <setjmp.h>
 #include <setjmp.h>
 
 
+#ifdef USE_HTTPSRR
+# include <stdint.h>
+#endif
+
 /* Allocate enough memory to hold the full name information structs and
 /* Allocate enough memory to hold the full name information structs and
  * everything. OSF1 is known to require at least 8872 bytes. The buffer
  * everything. OSF1 is known to require at least 8872 bytes. The buffer
  * required for storing all possible aliases and IP numbers is according to
  * required for storing all possible aliases and IP numbers is according to
@@ -58,8 +62,41 @@ struct connectdata;
  */
  */
 struct Curl_hash *Curl_global_host_cache_init(void);
 struct Curl_hash *Curl_global_host_cache_init(void);
 
 
+#ifdef USE_HTTPSRR
+
+#define CURL_MAXLEN_host_name 253
+
+struct Curl_https_rrinfo {
+  size_t len; /* raw encoded length */
+  unsigned char *val; /* raw encoded octets */
+  /*
+   * fields from HTTPS RR, with the mandatory fields
+   * first (priority, target), then the others in the
+   * order of the keytag numbers defined at
+   * https://datatracker.ietf.org/doc/html/rfc9460#section-14.3.2
+   */
+  uint16_t priority;
+  char *target;
+  char *alpns; /* keytag = 1 */
+  bool no_def_alpn; /* keytag = 2 */
+  /*
+   * we don't support ports (keytag = 3) as we don't support
+   * port-switching yet
+   */
+  unsigned char *ipv4hints; /* keytag = 4 */
+  size_t ipv4hints_len;
+  unsigned char *echconfiglist; /* keytag = 5 */
+  size_t echconfiglist_len;
+  unsigned char *ipv6hints; /* keytag = 6 */
+  size_t ipv6hints_len;
+};
+#endif
+
 struct Curl_dns_entry {
 struct Curl_dns_entry {
   struct Curl_addrinfo *addr;
   struct Curl_addrinfo *addr;
+#ifdef USE_HTTPSRR
+  struct Curl_https_rrinfo *hinfo;
+#endif
   /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (doesn't time out) */
   /* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (doesn't time out) */
   time_t timestamp;
   time_t timestamp;
   /* use-counter, use Curl_resolv_unlock to release reference */
   /* use-counter, use Curl_resolv_unlock to release reference */
@@ -96,7 +133,7 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
                                    struct Curl_dns_entry **dnsentry,
                                    struct Curl_dns_entry **dnsentry,
                                    timediff_t timeoutms);
                                    timediff_t timeoutms);
 
 
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
 /*
 /*
  * Curl_ipv6works() returns TRUE if IPv6 seems to work.
  * Curl_ipv6works() returns TRUE if IPv6 seems to work.
  */
  */
@@ -129,7 +166,7 @@ void Curl_resolv_unlock(struct Curl_easy *data,
                         struct Curl_dns_entry *dns);
                         struct Curl_dns_entry *dns);
 
 
 /* init a new dns cache */
 /* init a new dns cache */
-void Curl_init_dnscache(struct Curl_hash *hash, int hashsize);
+void Curl_init_dnscache(struct Curl_hash *hash, size_t hashsize);
 
 
 /* prune old entries from the DNS cache */
 /* prune old entries from the DNS cache */
 void Curl_hostcache_prune(struct Curl_easy *data);
 void Curl_hostcache_prune(struct Curl_easy *data);

+ 7 - 9
lib/hsts.c

@@ -107,11 +107,6 @@ void Curl_hsts_cleanup(struct hsts **hp)
   }
   }
 }
 }
 
 
-static struct stsentry *hsts_entry(void)
-{
-  return calloc(1, sizeof(struct stsentry));
-}
-
 static CURLcode hsts_create(struct hsts *h,
 static CURLcode hsts_create(struct hsts *h,
                             const char *hostname,
                             const char *hostname,
                             bool subdomains,
                             bool subdomains,
@@ -127,7 +122,7 @@ static CURLcode hsts_create(struct hsts *h,
     --hlen;
     --hlen;
   if(hlen) {
   if(hlen) {
     char *duphost;
     char *duphost;
-    struct stsentry *sts = hsts_entry();
+    struct stsentry *sts = calloc(1, sizeof(struct stsentry));
     if(!sts)
     if(!sts)
       return CURLE_OUT_OF_MEMORY;
       return CURLE_OUT_OF_MEMORY;
 
 
@@ -140,7 +135,7 @@ static CURLcode hsts_create(struct hsts *h,
     sts->host = duphost;
     sts->host = duphost;
     sts->expires = expires;
     sts->expires = expires;
     sts->includeSubDomains = subdomains;
     sts->includeSubDomains = subdomains;
-    Curl_llist_insert_next(&h->list, h->list.tail, sts, &sts->node);
+    Curl_llist_append(&h->list, sts, &sts->node);
   }
   }
   return CURLE_OK;
   return CURLE_OK;
 }
 }
@@ -528,8 +523,11 @@ static CURLcode hsts_load(struct hsts *h, const char *file)
       char *lineptr = Curl_dyn_ptr(&buf);
       char *lineptr = Curl_dyn_ptr(&buf);
       while(*lineptr && ISBLANK(*lineptr))
       while(*lineptr && ISBLANK(*lineptr))
         lineptr++;
         lineptr++;
-      if(*lineptr == '#')
-        /* skip commented lines */
+      /*
+       * Skip empty or commented lines, since we know the line will have a
+       * trailing newline from Curl_get_line we can treat length 1 as empty.
+       */
+      if((*lineptr == '#') || strlen(lineptr) <= 1)
         continue;
         continue;
 
 
       hsts_add(h, lineptr);
       hsts_add(h, lineptr);

File diff suppressed because it is too large
+ 372 - 383
lib/http.c


+ 7 - 8
lib/http.h

@@ -44,7 +44,7 @@ typedef enum {
 
 
 #ifndef CURL_DISABLE_HTTP
 #ifndef CURL_DISABLE_HTTP
 
 
-#if defined(ENABLE_QUIC)
+#if defined(USE_HTTP3)
 #include <stdint.h>
 #include <stdint.h>
 #endif
 #endif
 
 
@@ -102,8 +102,8 @@ CURLcode Curl_http_target(struct Curl_easy *data, struct connectdata *conn,
                           struct dynbuf *req);
                           struct dynbuf *req);
 CURLcode Curl_http_statusline(struct Curl_easy *data,
 CURLcode Curl_http_statusline(struct Curl_easy *data,
                               struct connectdata *conn);
                               struct connectdata *conn);
-CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
-                          char *headp, size_t hdlen);
+CURLcode Curl_http_header(struct Curl_easy *data,
+                          const char *hd, size_t hdlen);
 CURLcode Curl_transferencode(struct Curl_easy *data);
 CURLcode Curl_transferencode(struct Curl_easy *data);
 CURLcode Curl_http_req_set_reader(struct Curl_easy *data,
 CURLcode Curl_http_req_set_reader(struct Curl_easy *data,
                                   Curl_HttpReq httpreq,
                                   Curl_HttpReq httpreq,
@@ -134,6 +134,9 @@ int Curl_http_getsock_do(struct Curl_easy *data, struct connectdata *conn,
 CURLcode Curl_http_write_resp(struct Curl_easy *data,
 CURLcode Curl_http_write_resp(struct Curl_easy *data,
                               const char *buf, size_t blen,
                               const char *buf, size_t blen,
                               bool is_eos);
                               bool is_eos);
+CURLcode Curl_http_write_resp_hd(struct Curl_easy *data,
+                                 const char *hd, size_t hdlen,
+                                 bool is_eos);
 
 
 /* These functions are in http.c */
 /* These functions are in http.c */
 CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
 CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
@@ -185,12 +188,8 @@ void Curl_http_exp100_got100(struct Curl_easy *data);
  * HTTP unique setup
  * HTTP unique setup
  ***************************************************************************/
  ***************************************************************************/
 struct HTTP {
 struct HTTP {
-#ifndef CURL_DISABLE_HTTP
-  void *h2_ctx;              /* HTTP/2 implementation context */
-  void *h3_ctx;              /* HTTP/3 implementation context */
-#else
+  /* TODO: no longer used, we should remove it from SingleRequest */
   char unused;
   char unused;
-#endif
 };
 };
 
 
 CURLcode Curl_http_size(struct Curl_easy *data);
 CURLcode Curl_http_size(struct Curl_easy *data);

+ 181 - 100
lib/http2.c

@@ -29,6 +29,7 @@
 #include <nghttp2/nghttp2.h>
 #include <nghttp2/nghttp2.h>
 #include "urldata.h"
 #include "urldata.h"
 #include "bufq.h"
 #include "bufq.h"
+#include "hash.h"
 #include "http1.h"
 #include "http1.h"
 #include "http2.h"
 #include "http2.h"
 #include "http.h"
 #include "http.h"
@@ -127,7 +128,9 @@ struct cf_h2_ctx {
   struct bufq inbufq;           /* network input */
   struct bufq inbufq;           /* network input */
   struct bufq outbufq;          /* network output */
   struct bufq outbufq;          /* network output */
   struct bufc_pool stream_bufcp; /* spares for stream buffers */
   struct bufc_pool stream_bufcp; /* spares for stream buffers */
+  struct dynbuf scratch;        /* scratch buffer for temp use */
 
 
+  struct Curl_hash streams; /* hash of `data->id` to `h2_stream_ctx` */
   size_t drain_total; /* sum of all stream's UrlState drain */
   size_t drain_total; /* sum of all stream's UrlState drain */
   uint32_t max_concurrent_streams;
   uint32_t max_concurrent_streams;
   int32_t goaway_error;
   int32_t goaway_error;
@@ -153,6 +156,9 @@ static void cf_h2_ctx_clear(struct cf_h2_ctx *ctx)
   Curl_bufq_free(&ctx->inbufq);
   Curl_bufq_free(&ctx->inbufq);
   Curl_bufq_free(&ctx->outbufq);
   Curl_bufq_free(&ctx->outbufq);
   Curl_bufcp_free(&ctx->stream_bufcp);
   Curl_bufcp_free(&ctx->stream_bufcp);
+  Curl_dyn_free(&ctx->scratch);
+  Curl_hash_clean(&ctx->streams);
+  Curl_hash_destroy(&ctx->streams);
   memset(ctx, 0, sizeof(*ctx));
   memset(ctx, 0, sizeof(*ctx));
   ctx->call_data = save;
   ctx->call_data = save;
 }
 }
@@ -187,6 +193,7 @@ struct h2_stream_ctx {
 
 
   int status_code; /* HTTP response status code */
   int status_code; /* HTTP response status code */
   uint32_t error; /* stream error code */
   uint32_t error; /* stream error code */
+  CURLcode xfer_result; /* Result of writing out response */
   uint32_t local_window_size; /* the local recv window size */
   uint32_t local_window_size; /* the local recv window size */
   int32_t id; /* HTTP/2 protocol identifier for stream */
   int32_t id; /* HTTP/2 protocol identifier for stream */
   BIT(resp_hds_complete); /* we have a complete, final response */
   BIT(resp_hds_complete); /* we have a complete, final response */
@@ -198,13 +205,58 @@ struct h2_stream_ctx {
                        buffered data in stream->sendbuf to upload. */
                        buffered data in stream->sendbuf to upload. */
 };
 };
 
 
-#define H2_STREAM_CTX(d)    ((struct h2_stream_ctx *)(((d) && \
-                              (d)->req.p.http)? \
-                             ((struct HTTP *)(d)->req.p.http)->h2_ctx \
-                               : NULL))
-#define H2_STREAM_LCTX(d)   ((struct HTTP *)(d)->req.p.http)->h2_ctx
-#define H2_STREAM_ID(d)     (H2_STREAM_CTX(d)? \
-                             H2_STREAM_CTX(d)->id : -2)
+#define H2_STREAM_CTX(ctx,data)   ((struct h2_stream_ctx *)(\
+            data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL))
+
+static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx)
+{
+  struct h2_stream_ctx *stream;
+
+  (void)ctx;
+  stream = calloc(1, sizeof(*stream));
+  if(!stream)
+    return NULL;
+
+  stream->id = -1;
+  Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
+                  H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
+  Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
+  Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST);
+  stream->resp_hds_len = 0;
+  stream->bodystarted = FALSE;
+  stream->status_code = -1;
+  stream->closed = FALSE;
+  stream->close_handled = FALSE;
+  stream->error = NGHTTP2_NO_ERROR;
+  stream->local_window_size = H2_STREAM_WINDOW_SIZE;
+  stream->upload_left = 0;
+  stream->nrcvd_data = 0;
+  return stream;
+}
+
+static void free_push_headers(struct h2_stream_ctx *stream)
+{
+  size_t i;
+  for(i = 0; i<stream->push_headers_used; i++)
+    free(stream->push_headers[i]);
+  Curl_safefree(stream->push_headers);
+  stream->push_headers_used = 0;
+}
+
+static void h2_stream_ctx_free(struct h2_stream_ctx *stream)
+{
+  Curl_bufq_free(&stream->sendbuf);
+  Curl_h1_req_parse_free(&stream->h1);
+  Curl_dynhds_free(&stream->resp_trailers);
+  free_push_headers(stream);
+  free(stream);
+}
+
+static void h2_stream_hash_free(void *stream)
+{
+  DEBUGASSERT(stream);
+  h2_stream_ctx_free((struct h2_stream_ctx *)stream);
+}
 
 
 /*
 /*
  * Mark this transfer to get "drained".
  * Mark this transfer to get "drained".
@@ -241,49 +293,29 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf,
     failf(data, "initialization failure, transfer not http initialized");
     failf(data, "initialization failure, transfer not http initialized");
     return CURLE_FAILED_INIT;
     return CURLE_FAILED_INIT;
   }
   }
-  stream = H2_STREAM_CTX(data);
+  stream = H2_STREAM_CTX(ctx, data);
   if(stream) {
   if(stream) {
     *pstream = stream;
     *pstream = stream;
     return CURLE_OK;
     return CURLE_OK;
   }
   }
 
 
-  stream = calloc(1, sizeof(*stream));
+  stream = h2_stream_ctx_create(ctx);
   if(!stream)
   if(!stream)
     return CURLE_OUT_OF_MEMORY;
     return CURLE_OUT_OF_MEMORY;
 
 
-  stream->id = -1;
-  Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
-                  H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
-  Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
-  Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST);
-  stream->resp_hds_len = 0;
-  stream->bodystarted = FALSE;
-  stream->status_code = -1;
-  stream->closed = FALSE;
-  stream->close_handled = FALSE;
-  stream->error = NGHTTP2_NO_ERROR;
-  stream->local_window_size = H2_STREAM_WINDOW_SIZE;
-  stream->upload_left = 0;
-  stream->nrcvd_data = 0;
+  if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) {
+    h2_stream_ctx_free(stream);
+    return CURLE_OUT_OF_MEMORY;
+  }
 
 
-  H2_STREAM_LCTX(data) = stream;
   *pstream = stream;
   *pstream = stream;
   return CURLE_OK;
   return CURLE_OK;
 }
 }
 
 
-static void free_push_headers(struct h2_stream_ctx *stream)
-{
-  size_t i;
-  for(i = 0; i<stream->push_headers_used; i++)
-    free(stream->push_headers[i]);
-  Curl_safefree(stream->push_headers);
-  stream->push_headers_used = 0;
-}
-
 static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
 static void http2_data_done(struct Curl_cfilter *cf, 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(data);
+  struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
 
 
   DEBUGASSERT(ctx);
   DEBUGASSERT(ctx);
   if(!stream)
   if(!stream)
@@ -310,12 +342,7 @@ static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
       nghttp2_session_send(ctx->h2);
       nghttp2_session_send(ctx->h2);
   }
   }
 
 
-  Curl_bufq_free(&stream->sendbuf);
-  Curl_h1_req_parse_free(&stream->h1);
-  Curl_dynhds_free(&stream->resp_trailers);
-  free_push_headers(stream);
-  free(stream);
-  H2_STREAM_LCTX(data) = NULL;
+  Curl_hash_offt_remove(&ctx->streams, data->id);
 }
 }
 
 
 static int h2_client_new(struct Curl_cfilter *cf,
 static int h2_client_new(struct Curl_cfilter *cf,
@@ -408,6 +435,8 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
   Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES);
   Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES);
   Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0);
   Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0);
   Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0);
   Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0);
+  Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
+  Curl_hash_offt_init(&ctx->streams, 63, h2_stream_hash_free);
   ctx->last_stream_id = 2147483647;
   ctx->last_stream_id = 2147483647;
 
 
   rc = nghttp2_session_callbacks_new(&cbs);
   rc = nghttp2_session_callbacks_new(&cbs);
@@ -706,6 +735,7 @@ static ssize_t send_callback(nghttp2_session *h2,
    the struct are hidden from the user. */
    the struct are hidden from the user. */
 struct curl_pushheaders {
 struct curl_pushheaders {
   struct Curl_easy *data;
   struct Curl_easy *data;
+  struct h2_stream_ctx *stream;
   const nghttp2_push_promise *frame;
   const nghttp2_push_promise *frame;
 };
 };
 
 
@@ -719,9 +749,8 @@ char *curl_pushheader_bynum(struct curl_pushheaders *h, size_t num)
   if(!h || !GOOD_EASY_HANDLE(h->data))
   if(!h || !GOOD_EASY_HANDLE(h->data))
     return NULL;
     return NULL;
   else {
   else {
-    struct h2_stream_ctx *stream = H2_STREAM_CTX(h->data);
-    if(stream && num < stream->push_headers_used)
-      return stream->push_headers[num];
+    if(h->stream && num < h->stream->push_headers_used)
+      return h->stream->push_headers[num];
   }
   }
   return NULL;
   return NULL;
 }
 }
@@ -744,7 +773,7 @@ char *curl_pushheader_byname(struct curl_pushheaders *h, const char *header)
      !strcmp(header, ":") || strchr(header + 1, ':'))
      !strcmp(header, ":") || strchr(header + 1, ':'))
     return NULL;
     return NULL;
 
 
-  stream = H2_STREAM_CTX(h->data);
+  stream = h->stream;
   if(!stream)
   if(!stream)
     return NULL;
     return NULL;
 
 
@@ -804,7 +833,7 @@ static int set_transfer_url(struct Curl_easy *data,
 
 
   v = curl_pushheader_byname(hp, HTTP_PSEUDO_AUTHORITY);
   v = curl_pushheader_byname(hp, HTTP_PSEUDO_AUTHORITY);
   if(v) {
   if(v) {
-    uc = Curl_url_set_authority(u, v, CURLU_DISALLOW_USER);
+    uc = Curl_url_set_authority(u, v);
     if(uc) {
     if(uc) {
       rc = 2;
       rc = 2;
       goto fail;
       goto fail;
@@ -867,12 +896,10 @@ static int push_promise(struct Curl_cfilter *cf,
       goto fail;
       goto fail;
     }
     }
 
 
-    heads.data = data;
-    heads.frame = frame;
     /* ask the application */
     /* ask the application */
     CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ask application");
     CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ask application");
 
 
-    stream = H2_STREAM_CTX(data);
+    stream = H2_STREAM_CTX(ctx, data);
     if(!stream) {
     if(!stream) {
       failf(data, "Internal NULL stream");
       failf(data, "Internal NULL stream");
       discard_newhandle(cf, newhandle);
       discard_newhandle(cf, newhandle);
@@ -880,6 +907,10 @@ static int push_promise(struct Curl_cfilter *cf,
       goto fail;
       goto fail;
     }
     }
 
 
+    heads.data = data;
+    heads.stream = stream;
+    heads.frame = frame;
+
     rv = set_transfer_url(newhandle, &heads);
     rv = set_transfer_url(newhandle, &heads);
     if(rv) {
     if(rv) {
       discard_newhandle(cf, newhandle);
       discard_newhandle(cf, newhandle);
@@ -945,12 +976,39 @@ fail:
   return rv;
   return rv;
 }
 }
 
 
-static CURLcode recvbuf_write_hds(struct Curl_cfilter *cf,
+static void h2_xfer_write_resp_hd(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
                                   struct Curl_easy *data,
-                                  const char *buf, size_t blen)
+                                  struct h2_stream_ctx *stream,
+                                  const char *buf, size_t blen, bool eos)
 {
 {
-  (void)cf;
-  return Curl_xfer_write_resp(data, (char *)buf, blen, FALSE);
+
+  /* If we already encountered an error, skip further writes */
+  if(!stream->xfer_result) {
+    stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos);
+    if(stream->xfer_result)
+      CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of headers",
+                  stream->id, stream->xfer_result, blen);
+  }
+}
+
+static void h2_xfer_write_resp(struct Curl_cfilter *cf,
+                               struct Curl_easy *data,
+                               struct h2_stream_ctx *stream,
+                               const char *buf, size_t blen, bool eos)
+{
+
+  /* If we already encountered an error, skip further writes */
+  if(!stream->xfer_result)
+    stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos);
+  /* If the transfer write is errored, we do not want any more data */
+  if(stream->xfer_result) {
+    struct cf_h2_ctx *ctx = cf->ctx;
+    CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of data, "
+                "RST-ing stream",
+                stream->id, stream->xfer_result, blen);
+    nghttp2_submit_rst_stream(ctx->h2, 0, stream->id,
+                              NGHTTP2_ERR_CALLBACK_FAILURE);
+  }
 }
 }
 
 
 static CURLcode on_stream_frame(struct Curl_cfilter *cf,
 static CURLcode on_stream_frame(struct Curl_cfilter *cf,
@@ -958,9 +1016,8 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
                                 const nghttp2_frame *frame)
                                 const nghttp2_frame *frame)
 {
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   struct cf_h2_ctx *ctx = cf->ctx;
-  struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
+  struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
   int32_t stream_id = frame->hd.stream_id;
   int32_t stream_id = frame->hd.stream_id;
-  CURLcode result;
   int rv;
   int rv;
 
 
   if(!stream) {
   if(!stream) {
@@ -1008,9 +1065,7 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
       stream->status_code = -1;
       stream->status_code = -1;
     }
     }
 
 
-    result = recvbuf_write_hds(cf, data, STRCONST("\r\n"));
-    if(result)
-      return result;
+    h2_xfer_write_resp_hd(cf, data, stream, STRCONST("\r\n"), stream->closed);
 
 
     if(stream->status_code / 100 != 1) {
     if(stream->status_code / 100 != 1) {
       stream->resp_hds_complete = TRUE;
       stream->resp_hds_complete = TRUE;
@@ -1189,7 +1244,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
          * 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)) {
         if(CURL_WANT_SEND(data)) {
-          struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
+          struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
           if(stream)
           if(stream)
             drain_stream(cf, data, stream);
             drain_stream(cf, data, stream);
         }
         }
@@ -1229,7 +1284,6 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
   struct cf_h2_ctx *ctx = cf->ctx;
   struct cf_h2_ctx *ctx = cf->ctx;
   struct h2_stream_ctx *stream;
   struct h2_stream_ctx *stream;
   struct Curl_easy *data_s;
   struct Curl_easy *data_s;
-  CURLcode result;
   (void)flags;
   (void)flags;
 
 
   DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
   DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
@@ -1248,13 +1302,11 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
     return 0;
     return 0;
   }
   }
 
 
-  stream = H2_STREAM_CTX(data_s);
+  stream = H2_STREAM_CTX(ctx, data_s);
   if(!stream)
   if(!stream)
     return NGHTTP2_ERR_CALLBACK_FAILURE;
     return NGHTTP2_ERR_CALLBACK_FAILURE;
 
 
-  result = Curl_xfer_write_resp(data_s, (char *)mem, len, FALSE);
-  if(result && result != CURLE_AGAIN)
-    return NGHTTP2_ERR_CALLBACK_FAILURE;
+  h2_xfer_write_resp(cf, data_s, stream, (char *)mem, len, FALSE);
 
 
   nghttp2_session_consume(ctx->h2, stream_id, len);
   nghttp2_session_consume(ctx->h2, stream_id, len);
   stream->nrcvd_data += (curl_off_t)len;
   stream->nrcvd_data += (curl_off_t)len;
@@ -1268,6 +1320,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
                            uint32_t error_code, void *userp)
                            uint32_t error_code, void *userp)
 {
 {
   struct Curl_cfilter *cf = userp;
   struct Curl_cfilter *cf = userp;
+  struct cf_h2_ctx *ctx = cf->ctx;
   struct Curl_easy *data_s, *call_data = CF_DATA_CURRENT(cf);
   struct Curl_easy *data_s, *call_data = CF_DATA_CURRENT(cf);
   struct h2_stream_ctx *stream;
   struct h2_stream_ctx *stream;
   int rv;
   int rv;
@@ -1292,7 +1345,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
     (void)nghttp2_session_set_stream_user_data(session, stream_id, 0);
     (void)nghttp2_session_set_stream_user_data(session, stream_id, 0);
     return NGHTTP2_ERR_CALLBACK_FAILURE;
     return NGHTTP2_ERR_CALLBACK_FAILURE;
   }
   }
-  stream = H2_STREAM_CTX(data_s);
+  stream = H2_STREAM_CTX(ctx, data_s);
   if(!stream) {
   if(!stream) {
     CURL_TRC_CF(data_s, cf,
     CURL_TRC_CF(data_s, cf,
                 "[%d] on_stream_close, GOOD easy but no stream", stream_id);
                 "[%d] on_stream_close, GOOD easy but no stream", stream_id);
@@ -1327,6 +1380,7 @@ static int on_begin_headers(nghttp2_session *session,
                             const nghttp2_frame *frame, void *userp)
                             const nghttp2_frame *frame, void *userp)
 {
 {
   struct Curl_cfilter *cf = userp;
   struct Curl_cfilter *cf = userp;
+  struct cf_h2_ctx *ctx = cf->ctx;
   struct h2_stream_ctx *stream;
   struct h2_stream_ctx *stream;
   struct Curl_easy *data_s = NULL;
   struct Curl_easy *data_s = NULL;
 
 
@@ -1340,7 +1394,7 @@ static int on_begin_headers(nghttp2_session *session,
     return 0;
     return 0;
   }
   }
 
 
-  stream = H2_STREAM_CTX(data_s);
+  stream = H2_STREAM_CTX(ctx, data_s);
   if(!stream || !stream->bodystarted) {
   if(!stream || !stream->bodystarted) {
     return 0;
     return 0;
   }
   }
@@ -1356,6 +1410,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
                      void *userp)
                      void *userp)
 {
 {
   struct Curl_cfilter *cf = userp;
   struct Curl_cfilter *cf = userp;
+  struct cf_h2_ctx *ctx = cf->ctx;
   struct h2_stream_ctx *stream;
   struct h2_stream_ctx *stream;
   struct Curl_easy *data_s;
   struct Curl_easy *data_s;
   int32_t stream_id = frame->hd.stream_id;
   int32_t stream_id = frame->hd.stream_id;
@@ -1371,7 +1426,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
        internal error more than anything else! */
        internal error more than anything else! */
     return NGHTTP2_ERR_CALLBACK_FAILURE;
     return NGHTTP2_ERR_CALLBACK_FAILURE;
 
 
-  stream = H2_STREAM_CTX(data_s);
+  stream = H2_STREAM_CTX(ctx, data_s);
   if(!stream) {
   if(!stream) {
     failf(data_s, "Internal NULL stream");
     failf(data_s, "Internal NULL stream");
     return NGHTTP2_ERR_CALLBACK_FAILURE;
     return NGHTTP2_ERR_CALLBACK_FAILURE;
@@ -1465,14 +1520,15 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
     result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO);
     result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO);
     if(result)
     if(result)
       return NGHTTP2_ERR_CALLBACK_FAILURE;
       return NGHTTP2_ERR_CALLBACK_FAILURE;
-    result = recvbuf_write_hds(cf, data_s, STRCONST("HTTP/2 "));
-    if(result)
-      return NGHTTP2_ERR_CALLBACK_FAILURE;
-    result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen);
-    if(result)
-      return NGHTTP2_ERR_CALLBACK_FAILURE;
-    /* the space character after the status code is mandatory */
-    result = recvbuf_write_hds(cf, data_s, STRCONST(" \r\n"));
+    Curl_dyn_reset(&ctx->scratch);
+    result = Curl_dyn_addn(&ctx->scratch, STRCONST("HTTP/2 "));
+    if(!result)
+      result = Curl_dyn_addn(&ctx->scratch, value, valuelen);
+    if(!result)
+      result = Curl_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
+    if(!result)
+      h2_xfer_write_resp_hd(cf, data_s, stream, Curl_dyn_ptr(&ctx->scratch),
+                            Curl_dyn_len(&ctx->scratch), FALSE);
     if(result)
     if(result)
       return NGHTTP2_ERR_CALLBACK_FAILURE;
       return NGHTTP2_ERR_CALLBACK_FAILURE;
     /* if we receive data for another handle, wake that up */
     /* if we receive data for another handle, wake that up */
@@ -1487,16 +1543,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
   /* nghttp2 guarantees that namelen > 0, and :status was already
   /* nghttp2 guarantees that namelen > 0, and :status was already
      received, and this is not pseudo-header field . */
      received, and this is not pseudo-header field . */
   /* convert to an HTTP1-style header */
   /* convert to an HTTP1-style header */
-  result = recvbuf_write_hds(cf, data_s, (const char *)name, namelen);
-  if(result)
-    return NGHTTP2_ERR_CALLBACK_FAILURE;
-  result = recvbuf_write_hds(cf, data_s, STRCONST(": "));
-  if(result)
-    return NGHTTP2_ERR_CALLBACK_FAILURE;
-  result = recvbuf_write_hds(cf, data_s, (const char *)value, valuelen);
-  if(result)
-    return NGHTTP2_ERR_CALLBACK_FAILURE;
-  result = recvbuf_write_hds(cf, data_s, STRCONST("\r\n"));
+  Curl_dyn_reset(&ctx->scratch);
+  result = Curl_dyn_addn(&ctx->scratch, (const char *)name, namelen);
+  if(!result)
+    result = Curl_dyn_addn(&ctx->scratch, STRCONST(": "));
+  if(!result)
+    result = Curl_dyn_addn(&ctx->scratch, (const char *)value, valuelen);
+  if(!result)
+    result = Curl_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
+  if(!result)
+    h2_xfer_write_resp_hd(cf, data_s, stream, Curl_dyn_ptr(&ctx->scratch),
+                          Curl_dyn_len(&ctx->scratch), FALSE);
   if(result)
   if(result)
     return NGHTTP2_ERR_CALLBACK_FAILURE;
     return NGHTTP2_ERR_CALLBACK_FAILURE;
   /* if we receive data for another handle, wake that up */
   /* if we receive data for another handle, wake that up */
@@ -1517,6 +1574,7 @@ static ssize_t req_body_read_callback(nghttp2_session *session,
                                       void *userp)
                                       void *userp)
 {
 {
   struct Curl_cfilter *cf = userp;
   struct Curl_cfilter *cf = userp;
+  struct cf_h2_ctx *ctx = cf->ctx;
   struct Curl_easy *data_s;
   struct Curl_easy *data_s;
   struct h2_stream_ctx *stream = NULL;
   struct h2_stream_ctx *stream = NULL;
   CURLcode result;
   CURLcode result;
@@ -1533,7 +1591,7 @@ static ssize_t req_body_read_callback(nghttp2_session *session,
          internal error more than anything else! */
          internal error more than anything else! */
       return NGHTTP2_ERR_CALLBACK_FAILURE;
       return NGHTTP2_ERR_CALLBACK_FAILURE;
 
 
-    stream = H2_STREAM_CTX(data_s);
+    stream = H2_STREAM_CTX(ctx, data_s);
     if(!stream)
     if(!stream)
       return NGHTTP2_ERR_CALLBACK_FAILURE;
       return NGHTTP2_ERR_CALLBACK_FAILURE;
   }
   }
@@ -1620,7 +1678,7 @@ static CURLcode http2_data_done_send(struct Curl_cfilter *cf,
 {
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   struct cf_h2_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
-  struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
+  struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
 
 
   if(!ctx || !ctx->h2 || !stream)
   if(!ctx || !ctx->h2 || !stream)
     goto out;
     goto out;
@@ -1658,6 +1716,15 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
     return -1;
     return -1;
   }
   }
   else if(stream->error != NGHTTP2_NO_ERROR) {
   else if(stream->error != NGHTTP2_NO_ERROR) {
+    if(stream->resp_hds_complete && data->req.no_body) {
+      CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did "
+                  "not want a body anyway, ignore: %s (err %u)",
+                  stream->id, nghttp2_http2_strerror(stream->error),
+                  stream->error);
+      stream->close_handled = TRUE;
+      *err = CURLE_OK;
+      goto out;
+    }
     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);
@@ -1736,11 +1803,12 @@ static int sweight_in_effect(const struct Curl_easy *data)
  * struct.
  * struct.
  */
  */
 
 
-static void h2_pri_spec(struct Curl_easy *data,
+static void h2_pri_spec(struct cf_h2_ctx *ctx,
+                        struct Curl_easy *data,
                         nghttp2_priority_spec *pri_spec)
                         nghttp2_priority_spec *pri_spec)
 {
 {
   struct Curl_data_priority *prio = &data->set.priority;
   struct Curl_data_priority *prio = &data->set.priority;
-  struct h2_stream_ctx *depstream = H2_STREAM_CTX(prio->parent);
+  struct h2_stream_ctx *depstream = H2_STREAM_CTX(ctx, prio->parent);
   int32_t depstream_id = depstream? depstream->id:0;
   int32_t depstream_id = depstream? depstream->id:0;
   nghttp2_priority_spec_init(pri_spec, depstream_id,
   nghttp2_priority_spec_init(pri_spec, depstream_id,
                              sweight_wanted(data),
                              sweight_wanted(data),
@@ -1758,7 +1826,7 @@ 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(data);
+  struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
   int rv = 0;
   int rv = 0;
 
 
   if(stream && stream->id > 0 &&
   if(stream && stream->id > 0 &&
@@ -1768,7 +1836,7 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
     /* send new weight and/or dependency */
     /* send new weight and/or dependency */
     nghttp2_priority_spec pri_spec;
     nghttp2_priority_spec pri_spec;
 
 
-    h2_pri_spec(data, &pri_spec);
+    h2_pri_spec(ctx, data, &pri_spec);
     CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id);
     CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id);
     DEBUGASSERT(stream->id != -1);
     DEBUGASSERT(stream->id != -1);
     rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE,
     rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE,
@@ -1799,7 +1867,12 @@ static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
 
 
   (void)buf;
   (void)buf;
   *err = CURLE_AGAIN;
   *err = CURLE_AGAIN;
-  if(stream->closed) {
+  if(stream->xfer_result) {
+    CURL_TRC_CF(data, cf, "[%d] xfer write failed", stream->id);
+    *err = stream->xfer_result;
+    nread = -1;
+  }
+  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);
     nread = http2_handle_stream_close(cf, data, stream, err);
   }
   }
@@ -1838,7 +1911,7 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
    * it is time to stop due to connection close or us not processing
    * it is time to stop due to connection close or us not processing
    * all network input */
    * all network input */
   while(!ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) {
   while(!ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) {
-    stream = H2_STREAM_CTX(data);
+    stream = H2_STREAM_CTX(ctx, data);
     if(stream && (stream->closed || !data_max_bytes)) {
     if(stream && (stream->closed || !data_max_bytes)) {
       /* We would like to abort here and stop processing, so that
       /* We would like to abort here and stop processing, so that
        * the transfer loop can handle the data/close here. However,
        * the transfer loop can handle the data/close here. However,
@@ -1884,7 +1957,7 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
                           char *buf, size_t len, CURLcode *err)
                           char *buf, size_t len, CURLcode *err)
 {
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   struct cf_h2_ctx *ctx = cf->ctx;
-  struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
+  struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
   ssize_t nread = -1;
   ssize_t nread = -1;
   CURLcode result;
   CURLcode result;
   struct cf_call_data save;
   struct cf_call_data save;
@@ -2016,7 +2089,7 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream,
     goto out;
     goto out;
   }
   }
 
 
-  h2_pri_spec(data, &pri_spec);
+  h2_pri_spec(ctx, data, &pri_spec);
   if(!nghttp2_session_check_request_allowed(ctx->h2))
   if(!nghttp2_session_check_request_allowed(ctx->h2))
     CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)");
     CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)");
 
 
@@ -2113,7 +2186,7 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                           const void *buf, size_t len, CURLcode *err)
                           const void *buf, size_t len, CURLcode *err)
 {
 {
   struct cf_h2_ctx *ctx = cf->ctx;
   struct cf_h2_ctx *ctx = cf->ctx;
-  struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
+  struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
   struct cf_call_data save;
   struct cf_call_data save;
   int rv;
   int rv;
   ssize_t nwritten;
   ssize_t nwritten;
@@ -2294,7 +2367,7 @@ static void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
   sock = Curl_conn_cf_get_socket(cf, data);
   sock = Curl_conn_cf_get_socket(cf, data);
   Curl_pollset_check(data, ps, sock, &want_recv, &want_send);
   Curl_pollset_check(data, ps, sock, &want_recv, &want_send);
   if(want_recv || want_send) {
   if(want_recv || want_send) {
-    struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
+    struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
     struct cf_call_data save;
     struct cf_call_data save;
     bool c_exhaust, s_exhaust;
     bool c_exhaust, s_exhaust;
 
 
@@ -2395,7 +2468,7 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf,
 {
 {
 #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
 #ifdef NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
   struct cf_h2_ctx *ctx = cf->ctx;
   struct cf_h2_ctx *ctx = cf->ctx;
-  struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
+  struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
 
 
   DEBUGASSERT(data);
   DEBUGASSERT(data);
   if(ctx && ctx->h2 && stream) {
   if(ctx && ctx->h2 && stream) {
@@ -2480,7 +2553,7 @@ static bool cf_h2_data_pending(struct Curl_cfilter *cf,
                                const struct Curl_easy *data)
                                const 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(data);
+  struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
 
 
   if(ctx && (!Curl_bufq_is_empty(&ctx->inbufq)
   if(ctx && (!Curl_bufq_is_empty(&ctx->inbufq)
             || (stream && !Curl_bufq_is_empty(&stream->sendbuf))))
             || (stream && !Curl_bufq_is_empty(&stream->sendbuf))))
@@ -2539,6 +2612,11 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf,
     *pres1 = (effective_max > INT_MAX)? INT_MAX : (int)effective_max;
     *pres1 = (effective_max > INT_MAX)? INT_MAX : (int)effective_max;
     CF_DATA_RESTORE(cf, save);
     CF_DATA_RESTORE(cf, save);
     return CURLE_OK;
     return CURLE_OK;
+  case CF_QUERY_STREAM_ERROR: {
+    struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
+    *pres1 = stream? (int)stream->error : 0;
+    return CURLE_OK;
+  }
   default:
   default:
     break;
     break;
   }
   }
@@ -2768,8 +2846,11 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
    CURLE_HTTP2_STREAM error! */
    CURLE_HTTP2_STREAM error! */
 bool Curl_h2_http_1_1_error(struct Curl_easy *data)
 bool Curl_h2_http_1_1_error(struct Curl_easy *data)
 {
 {
-  struct h2_stream_ctx *stream = H2_STREAM_CTX(data);
-  return (stream && stream->error == NGHTTP2_HTTP_1_1_REQUIRED);
+  if(Curl_conn_is_http2(data, data->conn, FIRSTSOCKET)) {
+    int err = Curl_conn_get_stream_error(data, data->conn, FIRSTSOCKET);
+    return (err == NGHTTP2_HTTP_1_1_REQUIRED);
+  }
+  return FALSE;
 }
 }
 
 
 #else /* !USE_NGHTTP2 */
 #else /* !USE_NGHTTP2 */

+ 1 - 4
lib/http_aws_sigv4.c

@@ -158,10 +158,7 @@ static CURLcode make_headers(struct Curl_easy *data,
   msnprintf(date_full_hdr, DATE_FULL_HDR_LEN,
   msnprintf(date_full_hdr, DATE_FULL_HDR_LEN,
             "x-%s-date:%s", provider1, timestamp);
             "x-%s-date:%s", provider1, timestamp);
 
 
-  if(Curl_checkheaders(data, STRCONST("Host"))) {
-    head = NULL;
-  }
-  else {
+  if(!Curl_checkheaders(data, STRCONST("Host"))) {
     char full_host[FULL_HOST_LEN + 1];
     char full_host[FULL_HOST_LEN + 1];
 
 
     if(data->state.aptr.host) {
     if(data->state.aptr.host) {

+ 16 - 1
lib/http_chunks.c

@@ -28,6 +28,7 @@
 
 
 #include "urldata.h" /* it includes http_chunks.h */
 #include "urldata.h" /* it includes http_chunks.h */
 #include "curl_printf.h"
 #include "curl_printf.h"
+#include "curl_trc.h"
 #include "sendf.h"   /* for the client write stuff */
 #include "sendf.h"   /* for the client write stuff */
 #include "dynbuf.h"
 #include "dynbuf.h"
 #include "content_encoding.h"
 #include "content_encoding.h"
@@ -185,8 +186,11 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
         if(0 == ch->datasize) {
         if(0 == ch->datasize) {
           ch->state = CHUNK_TRAILER; /* now check for trailers */
           ch->state = CHUNK_TRAILER; /* now check for trailers */
         }
         }
-        else
+        else {
           ch->state = CHUNK_DATA;
           ch->state = CHUNK_DATA;
+          CURL_TRC_WRITE(data, "http_chunked, chunk start of %"
+                         CURL_FORMAT_CURL_OFF_T " bytes", ch->datasize);
+        }
       }
       }
 
 
       buf++;
       buf++;
@@ -221,6 +225,9 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
       ch->datasize -= piece; /* decrease amount left to expect */
       ch->datasize -= piece; /* decrease amount left to expect */
       buf += piece;    /* move read pointer forward */
       buf += piece;    /* move read pointer forward */
       blen -= piece;   /* decrease space left in this round */
       blen -= piece;   /* decrease space left in this round */
+      CURL_TRC_WRITE(data, "http_chunked, write %zu body bytes, %"
+                     CURL_FORMAT_CURL_OFF_T " bytes in chunk remain",
+                     piece, ch->datasize);
 
 
       if(0 == ch->datasize)
       if(0 == ch->datasize)
         /* end of data this round, we now expect a trailing CRLF */
         /* end of data this round, we now expect a trailing CRLF */
@@ -340,11 +347,14 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
            even if there's no more chunks to read */
            even if there's no more chunks to read */
         ch->datasize = blen;
         ch->datasize = blen;
         ch->state = CHUNK_DONE;
         ch->state = CHUNK_DONE;
+        CURL_TRC_WRITE(data, "http_chunk, response complete");
         return CURLE_OK;
         return CURLE_OK;
       }
       }
       else {
       else {
         ch->state = CHUNK_FAILED;
         ch->state = CHUNK_FAILED;
         ch->last_code = CHUNKE_BAD_CHUNK;
         ch->last_code = CHUNKE_BAD_CHUNK;
+        CURL_TRC_WRITE(data, "http_chunk error, expected 0x0a, seeing 0x%ux",
+                       (unsigned int)*buf);
         return CURLE_RECV_ERROR;
         return CURLE_RECV_ERROR;
       }
       }
     case CHUNK_DONE:
     case CHUNK_DONE:
@@ -498,6 +508,7 @@ static CURLcode add_last_chunk(struct Curl_easy *data,
   int rc;
   int rc;
 
 
   if(!data->set.trailer_callback) {
   if(!data->set.trailer_callback) {
+    CURL_TRC_READ(data, "http_chunk, added last, empty chunk");
     return Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("0\r\n\r\n"), &n);
     return Curl_bufq_cwrite(&ctx->chunkbuf, STRCONST("0\r\n\r\n"), &n);
   }
   }
 
 
@@ -535,6 +546,8 @@ static CURLcode add_last_chunk(struct Curl_easy *data,
 
 
 out:
 out:
   curl_slist_free_all(trailers);
   curl_slist_free_all(trailers);
+  CURL_TRC_READ(data, "http_chunk, added last chunk with trailers "
+                "from client -> %d", result);
   return result;
   return result;
 }
 }
 
 
@@ -581,6 +594,8 @@ static CURLcode add_chunk(struct Curl_easy *data,
       result = Curl_bufq_cwrite(&ctx->chunkbuf, buf, nread, &n);
       result = Curl_bufq_cwrite(&ctx->chunkbuf, buf, nread, &n);
     if(!result)
     if(!result)
       result = Curl_bufq_cwrite(&ctx->chunkbuf, "\r\n", 2, &n);
       result = Curl_bufq_cwrite(&ctx->chunkbuf, "\r\n", 2, &n);
+    CURL_TRC_READ(data, "http_chunk, made chunk of %zu bytes -> %d",
+                 nread, result);
     if(result)
     if(result)
       return result;
       return result;
   }
   }

+ 20 - 5
lib/http_negotiate.c

@@ -120,16 +120,29 @@ 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)
 {
 {
-  struct negotiatedata *neg_ctx = proxy ? &conn->proxyneg :
-    &conn->negotiate;
-  struct auth *authp = proxy ? &data->state.authproxy : &data->state.authhost;
-  curlnegotiate *state = proxy ? &conn->proxy_negotiate_state :
-    &conn->http_negotiate_state;
+  struct negotiatedata *neg_ctx;
+  struct auth *authp;
+  curlnegotiate *state;
   char *base64 = NULL;
   char *base64 = NULL;
   size_t len = 0;
   size_t len = 0;
   char *userp;
   char *userp;
   CURLcode result;
   CURLcode result;
 
 
+  if(proxy) {
+#ifndef CURL_DISABLE_PROXY
+    neg_ctx = &conn->proxyneg;
+    authp = &data->state.authproxy;
+    state = &conn->proxy_negotiate_state;
+#else
+    return CURLE_NOT_BUILT_IN;
+#endif
+  }
+  else {
+    neg_ctx = &conn->negotiate;
+    authp = &data->state.authhost;
+    state = &conn->http_negotiate_state;
+  }
+
   authp->done = FALSE;
   authp->done = FALSE;
 
 
   if(*state == GSS_AUTHRECV) {
   if(*state == GSS_AUTHRECV) {
@@ -171,8 +184,10 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data,
                     base64);
                     base64);
 
 
     if(proxy) {
     if(proxy) {
+#ifndef CURL_DISABLE_PROXY
       Curl_safefree(data->state.aptr.proxyuserpwd);
       Curl_safefree(data->state.aptr.proxyuserpwd);
       data->state.aptr.proxyuserpwd = userp;
       data->state.aptr.proxyuserpwd = userp;
+#endif
     }
     }
     else {
     else {
       Curl_safefree(data->state.aptr.userpwd);
       Curl_safefree(data->state.aptr.userpwd);

+ 0 - 5
lib/http_ntlm.c

@@ -40,7 +40,6 @@
 #include "strcase.h"
 #include "strcase.h"
 #include "http_ntlm.h"
 #include "http_ntlm.h"
 #include "curl_ntlm_core.h"
 #include "curl_ntlm_core.h"
-#include "curl_ntlm_wb.h"
 #include "curl_base64.h"
 #include "curl_base64.h"
 #include "vauth/vauth.h"
 #include "vauth/vauth.h"
 #include "url.h"
 #include "url.h"
@@ -266,10 +265,6 @@ void Curl_http_auth_cleanup_ntlm(struct connectdata *conn)
 {
 {
   Curl_auth_cleanup_ntlm(&conn->ntlm);
   Curl_auth_cleanup_ntlm(&conn->ntlm);
   Curl_auth_cleanup_ntlm(&conn->proxyntlm);
   Curl_auth_cleanup_ntlm(&conn->proxyntlm);
-
-#if defined(NTLM_WB_ENABLED)
-  Curl_http_auth_cleanup_ntlm_wb(conn);
-#endif
 }
 }
 
 
 #endif /* !CURL_DISABLE_HTTP && USE_NTLM */
 #endif /* !CURL_DISABLE_HTTP && USE_NTLM */

+ 1 - 1
lib/http_proxy.c

@@ -293,7 +293,7 @@ static void http_proxy_cf_close(struct Curl_cfilter *cf,
 
 
 struct Curl_cftype Curl_cft_http_proxy = {
 struct Curl_cftype Curl_cft_http_proxy = {
   "HTTP-PROXY",
   "HTTP-PROXY",
-  CF_TYPE_IP_CONNECT,
+  CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
   0,
   0,
   http_proxy_cf_destroy,
   http_proxy_cf_destroy,
   http_proxy_cf_connect,
   http_proxy_cf_connect,

+ 68 - 18
lib/idn.c

@@ -50,6 +50,63 @@
 #include "curl_memory.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 #include "memdebug.h"
 
 
+/* for macOS and iOS targets */
+#if defined(USE_APPLE_IDN)
+#include <unicode/uidna.h>
+
+static CURLcode mac_idn_to_ascii(const char *in, char **out)
+{
+  UErrorCode err = U_ZERO_ERROR;
+  UIDNA* idna = uidna_openUTS46(UIDNA_CHECK_BIDI, &err);
+  if(U_FAILURE(err)) {
+    return CURLE_OUT_OF_MEMORY;
+  }
+  else {
+    UIDNAInfo info = UIDNA_INFO_INITIALIZER;
+    char buffer[256] = {0};
+    (void)uidna_nameToASCII_UTF8(idna, in, -1, buffer,
+      sizeof(buffer), &info, &err);
+    uidna_close(idna);
+    if(U_FAILURE(err)) {
+      return CURLE_URL_MALFORMAT;
+    }
+    else {
+      *out = strdup(buffer);
+      if(*out)
+        return CURLE_OK;
+      else
+        return CURLE_OUT_OF_MEMORY;
+    }
+  }
+}
+
+static CURLcode mac_ascii_to_idn(const char *in, char **out)
+{
+  UErrorCode err = U_ZERO_ERROR;
+  UIDNA* idna = uidna_openUTS46(UIDNA_CHECK_BIDI, &err);
+  if(U_FAILURE(err)) {
+    return CURLE_OUT_OF_MEMORY;
+  }
+  else {
+    UIDNAInfo info = UIDNA_INFO_INITIALIZER;
+    char buffer[256] = {0};
+    (void)uidna_nameToUnicodeUTF8(idna, in, -1, buffer,
+      sizeof(buffer), &info, &err);
+    uidna_close(idna);
+    if(U_FAILURE(err)) {
+      return CURLE_URL_MALFORMAT;
+    }
+    else {
+      *out = strdup(buffer);
+      if(*out)
+        return CURLE_OK;
+      else
+        return CURLE_OUT_OF_MEMORY;
+    }
+  }
+}
+#endif
+
 #ifdef USE_WIN32_IDN
 #ifdef USE_WIN32_IDN
 /* using Windows kernel32 and normaliz libraries. */
 /* using Windows kernel32 and normaliz libraries. */
 
 
@@ -181,6 +238,8 @@ static CURLcode idn_decode(const char *input, char **output)
     result = CURLE_NOT_BUILT_IN;
     result = CURLE_NOT_BUILT_IN;
 #elif defined(USE_WIN32_IDN)
 #elif defined(USE_WIN32_IDN)
   result = win32_idn_to_ascii(input, &decoded);
   result = win32_idn_to_ascii(input, &decoded);
+#elif defined(USE_APPLE_IDN)
+  result = mac_idn_to_ascii(input, &decoded);
 #endif
 #endif
   if(!result)
   if(!result)
     *output = decoded;
     *output = decoded;
@@ -198,6 +257,10 @@ static CURLcode idn_encode(const char *puny, char **output)
   CURLcode result = win32_ascii_to_idn(puny, &enc);
   CURLcode result = win32_ascii_to_idn(puny, &enc);
   if(result)
   if(result)
     return result;
     return result;
+#elif defined(USE_APPLE_IDN)
+  CURLcode result = mac_ascii_to_idn(puny, &enc);
+  if(result)
+    return result;
 #endif
 #endif
   *output = enc;
   *output = enc;
   return CURLE_OK;
   return CURLE_OK;
@@ -246,11 +309,7 @@ CURLcode Curl_idn_encode(const char *puny, char **output)
  */
  */
 void Curl_free_idnconverted_hostname(struct hostname *host)
 void Curl_free_idnconverted_hostname(struct hostname *host)
 {
 {
-  if(host->encalloc) {
-    /* must be freed with idn2_free() if allocated by libidn */
-    Curl_idn_free(host->encalloc);
-    host->encalloc = NULL;
-  }
+  Curl_safefree(host->encalloc);
 }
 }
 
 
 #endif /* USE_IDN */
 #endif /* USE_IDN */
@@ -267,20 +326,11 @@ CURLcode Curl_idnconvert_hostname(struct hostname *host)
   /* Check name for non-ASCII and convert hostname if we can */
   /* Check name for non-ASCII and convert hostname if we can */
   if(!Curl_is_ASCII_name(host->name)) {
   if(!Curl_is_ASCII_name(host->name)) {
     char *decoded;
     char *decoded;
-    CURLcode result = idn_decode(host->name, &decoded);
-    if(!result) {
-      if(!*decoded) {
-        /* zero length is a bad host name */
-        Curl_idn_free(decoded);
-        return CURLE_URL_MALFORMAT;
-      }
-      /* successful */
-      host->encalloc = decoded;
-      /* change the name pointer to point to the encoded hostname */
-      host->name = host->encalloc;
-    }
-    else
+    CURLcode result = Curl_idn_decode(host->name, &decoded);
+    if(result)
       return result;
       return result;
+    /* successful */
+    host->name = host->encalloc = decoded;
   }
   }
 #endif
 #endif
   return CURLE_OK;
   return CURLE_OK;

+ 1 - 6
lib/idn.h

@@ -26,16 +26,11 @@
 
 
 bool Curl_is_ASCII_name(const char *hostname);
 bool Curl_is_ASCII_name(const char *hostname);
 CURLcode Curl_idnconvert_hostname(struct hostname *host);
 CURLcode Curl_idnconvert_hostname(struct hostname *host);
-#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN)
+#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN)
 #define USE_IDN
 #define USE_IDN
 void Curl_free_idnconverted_hostname(struct hostname *host);
 void Curl_free_idnconverted_hostname(struct hostname *host);
 CURLcode Curl_idn_decode(const char *input, char **output);
 CURLcode Curl_idn_decode(const char *input, char **output);
 CURLcode Curl_idn_encode(const char *input, char **output);
 CURLcode Curl_idn_encode(const char *input, char **output);
-#ifdef USE_LIBIDN2
-#define Curl_idn_free(x) idn2_free(x)
-#else
-#define Curl_idn_free(x) free(x)
-#endif
 
 
 #else
 #else
 #define Curl_free_idnconverted_hostname(x)
 #define Curl_free_idnconverted_hostname(x)

+ 11 - 11
lib/if2ip.c

@@ -62,7 +62,7 @@
 
 
 /* ------------------------------------------------------------------ */
 /* ------------------------------------------------------------------ */
 
 
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
 /* Return the scope of the given address. */
 /* Return the scope of the given address. */
 unsigned int Curl_ipv6_scope(const struct sockaddr *sa)
 unsigned int Curl_ipv6_scope(const struct sockaddr *sa)
 {
 {
@@ -97,17 +97,17 @@ unsigned int Curl_ipv6_scope(const struct sockaddr *sa)
 #if defined(HAVE_GETIFADDRS)
 #if defined(HAVE_GETIFADDRS)
 
 
 if2ip_result_t Curl_if2ip(int af,
 if2ip_result_t Curl_if2ip(int af,
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
                           unsigned int remote_scope,
                           unsigned int remote_scope,
                           unsigned int local_scope_id,
                           unsigned int local_scope_id,
 #endif
 #endif
                           const char *interf,
                           const char *interf,
-                          char *buf, int buf_size)
+                          char *buf, size_t buf_size)
 {
 {
   struct ifaddrs *iface, *head;
   struct ifaddrs *iface, *head;
   if2ip_result_t res = IF2IP_NOT_FOUND;
   if2ip_result_t res = IF2IP_NOT_FOUND;
 
 
-#if defined(ENABLE_IPV6) && \
+#if defined(USE_IPV6) && \
     !defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
     !defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
   (void) local_scope_id;
   (void) local_scope_id;
 #endif
 #endif
@@ -121,7 +121,7 @@ if2ip_result_t Curl_if2ip(int af,
             const char *ip;
             const char *ip;
             char scope[12] = "";
             char scope[12] = "";
             char ipstr[64];
             char ipstr[64];
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
             if(af == AF_INET6) {
             if(af == AF_INET6) {
 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
               unsigned int scopeid = 0;
               unsigned int scopeid = 0;
@@ -182,12 +182,12 @@ if2ip_result_t Curl_if2ip(int af,
 #elif defined(HAVE_IOCTL_SIOCGIFADDR)
 #elif defined(HAVE_IOCTL_SIOCGIFADDR)
 
 
 if2ip_result_t Curl_if2ip(int af,
 if2ip_result_t Curl_if2ip(int af,
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
                           unsigned int remote_scope,
                           unsigned int remote_scope,
                           unsigned int local_scope_id,
                           unsigned int local_scope_id,
 #endif
 #endif
                           const char *interf,
                           const char *interf,
-                          char *buf, int buf_size)
+                          char *buf, size_t buf_size)
 {
 {
   struct ifreq req;
   struct ifreq req;
   struct in_addr in;
   struct in_addr in;
@@ -196,7 +196,7 @@ if2ip_result_t Curl_if2ip(int af,
   size_t len;
   size_t len;
   const char *r;
   const char *r;
 
 
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   (void)remote_scope;
   (void)remote_scope;
   (void)local_scope_id;
   (void)local_scope_id;
 #endif
 #endif
@@ -237,15 +237,15 @@ if2ip_result_t Curl_if2ip(int af,
 #else
 #else
 
 
 if2ip_result_t Curl_if2ip(int af,
 if2ip_result_t Curl_if2ip(int af,
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
                           unsigned int remote_scope,
                           unsigned int remote_scope,
                           unsigned int local_scope_id,
                           unsigned int local_scope_id,
 #endif
 #endif
                           const char *interf,
                           const char *interf,
-                          char *buf, int buf_size)
+                          char *buf, size_t buf_size)
 {
 {
     (void) af;
     (void) af;
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
     (void) remote_scope;
     (void) remote_scope;
     (void) local_scope_id;
     (void) local_scope_id;
 #endif
 #endif

+ 3 - 3
lib/if2ip.h

@@ -32,7 +32,7 @@
 #define IPV6_SCOPE_UNIQUELOCAL  3       /* Unique local */
 #define IPV6_SCOPE_UNIQUELOCAL  3       /* Unique local */
 #define IPV6_SCOPE_NODELOCAL    4       /* Loopback. */
 #define IPV6_SCOPE_NODELOCAL    4       /* Loopback. */
 
 
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
 unsigned int Curl_ipv6_scope(const struct sockaddr *sa);
 unsigned int Curl_ipv6_scope(const struct sockaddr *sa);
 #else
 #else
 #define Curl_ipv6_scope(x) 0
 #define Curl_ipv6_scope(x) 0
@@ -45,12 +45,12 @@ typedef enum {
 } if2ip_result_t;
 } if2ip_result_t;
 
 
 if2ip_result_t Curl_if2ip(int af,
 if2ip_result_t Curl_if2ip(int af,
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
                           unsigned int remote_scope,
                           unsigned int remote_scope,
                           unsigned int local_scope_id,
                           unsigned int local_scope_id,
 #endif
 #endif
                           const char *interf,
                           const char *interf,
-                          char *buf, int buf_size);
+                          char *buf, size_t buf_size);
 
 
 #ifdef __INTERIX
 #ifdef __INTERIX
 
 

+ 4 - 2
lib/imap.c

@@ -117,7 +117,7 @@ static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out);
  */
  */
 
 
 const struct Curl_handler Curl_handler_imap = {
 const struct Curl_handler Curl_handler_imap = {
-  "IMAP",                           /* scheme */
+  "imap",                           /* scheme */
   imap_setup_connection,            /* setup_connection */
   imap_setup_connection,            /* setup_connection */
   imap_do,                          /* do_it */
   imap_do,                          /* do_it */
   imap_done,                        /* done */
   imap_done,                        /* done */
@@ -131,6 +131,7 @@ const struct Curl_handler Curl_handler_imap = {
   ZERO_NULL,                        /* perform_getsock */
   ZERO_NULL,                        /* perform_getsock */
   imap_disconnect,                  /* disconnect */
   imap_disconnect,                  /* disconnect */
   ZERO_NULL,                        /* write_resp */
   ZERO_NULL,                        /* write_resp */
+  ZERO_NULL,                        /* write_resp_hd */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* attach connection */
   ZERO_NULL,                        /* attach connection */
   PORT_IMAP,                        /* defport */
   PORT_IMAP,                        /* defport */
@@ -146,7 +147,7 @@ const struct Curl_handler Curl_handler_imap = {
  */
  */
 
 
 const struct Curl_handler Curl_handler_imaps = {
 const struct Curl_handler Curl_handler_imaps = {
-  "IMAPS",                          /* scheme */
+  "imaps",                          /* scheme */
   imap_setup_connection,            /* setup_connection */
   imap_setup_connection,            /* setup_connection */
   imap_do,                          /* do_it */
   imap_do,                          /* do_it */
   imap_done,                        /* done */
   imap_done,                        /* done */
@@ -160,6 +161,7 @@ const struct Curl_handler Curl_handler_imaps = {
   ZERO_NULL,                        /* perform_getsock */
   ZERO_NULL,                        /* perform_getsock */
   imap_disconnect,                  /* disconnect */
   imap_disconnect,                  /* disconnect */
   ZERO_NULL,                        /* write_resp */
   ZERO_NULL,                        /* write_resp */
+  ZERO_NULL,                        /* write_resp_hd */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* attach connection */
   ZERO_NULL,                        /* attach connection */
   PORT_IMAPS,                       /* defport */
   PORT_IMAPS,                       /* defport */

+ 2 - 2
lib/inet_ntop.c

@@ -42,11 +42,11 @@
 #define INT16SZ          2
 #define INT16SZ          2
 
 
 /*
 /*
- * If ENABLE_IPV6 is disabled, we still want to parse IPv6 addresses, so make
+ * If USE_IPV6 is disabled, we still want to parse IPv6 addresses, so make
  * sure we have _some_ value for AF_INET6 without polluting our fake value
  * sure we have _some_ value for AF_INET6 without polluting our fake value
  * everywhere.
  * everywhere.
  */
  */
-#if !defined(ENABLE_IPV6) && !defined(AF_INET6)
+#if !defined(USE_IPV6) && !defined(AF_INET6)
 #define AF_INET6 (AF_INET + 1)
 #define AF_INET6 (AF_INET + 1)
 #endif
 #endif
 
 

+ 2 - 2
lib/inet_pton.c

@@ -39,11 +39,11 @@
 #define INT16SZ          2
 #define INT16SZ          2
 
 
 /*
 /*
- * If ENABLE_IPV6 is disabled, we still want to parse IPv6 addresses, so make
+ * If USE_IPV6 is disabled, we still want to parse IPv6 addresses, so make
  * sure we have _some_ value for AF_INET6 without polluting our fake value
  * sure we have _some_ value for AF_INET6 without polluting our fake value
  * everywhere.
  * everywhere.
  */
  */
-#if !defined(ENABLE_IPV6) && !defined(AF_INET6)
+#if !defined(USE_IPV6) && !defined(AF_INET6)
 #define AF_INET6 (AF_INET + 1)
 #define AF_INET6 (AF_INET + 1)
 #endif
 #endif
 
 

+ 31 - 25
lib/krb5.c

@@ -524,24 +524,33 @@ static CURLcode read_data(struct Curl_easy *data, int sockindex,
     return result;
     return result;
 
 
   if(len) {
   if(len) {
-    /* only realloc if there was a length */
     len = ntohl(len);
     len = ntohl(len);
     if(len > CURL_MAX_INPUT_LENGTH)
     if(len > CURL_MAX_INPUT_LENGTH)
-      len = 0;
-    else
-      buf->data = Curl_saferealloc(buf->data, len);
+      return CURLE_TOO_LARGE;
+
+    Curl_dyn_reset(&buf->buf);
   }
   }
-  if(!len || !buf->data)
-    return CURLE_OUT_OF_MEMORY;
+  else
+    return CURLE_RECV_ERROR;
 
 
-  result = socket_read(data, sockindex, buf->data, len);
-  if(result)
-    return result;
-  nread = conn->mech->decode(conn->app_data, buf->data, len,
-                             conn->data_prot, conn);
+  do {
+    char buffer[1024];
+    nread = CURLMIN(len, (int)sizeof(buffer));
+    result = socket_read(data, sockindex, buffer, nread);
+    if(result)
+      return result;
+    result = Curl_dyn_addn(&buf->buf, buffer, nread);
+    if(result)
+      return result;
+    len -= nread;
+  } while(len);
+  /* this decodes the dynbuf *in place* */
+  nread = conn->mech->decode(conn->app_data,
+                             Curl_dyn_ptr(&buf->buf),
+                             len, conn->data_prot, conn);
   if(nread < 0)
   if(nread < 0)
     return CURLE_RECV_ERROR;
     return CURLE_RECV_ERROR;
-  buf->size = (size_t)nread;
+  Curl_dyn_setlen(&buf->buf, nread);
   buf->index = 0;
   buf->index = 0;
   return CURLE_OK;
   return CURLE_OK;
 }
 }
@@ -549,9 +558,10 @@ static CURLcode read_data(struct Curl_easy *data, int sockindex,
 static size_t
 static size_t
 buffer_read(struct krb5buffer *buf, void *data, size_t len)
 buffer_read(struct krb5buffer *buf, void *data, size_t len)
 {
 {
-  if(buf->size - buf->index < len)
-    len = buf->size - buf->index;
-  memcpy(data, (char *)buf->data + buf->index, len);
+  size_t size = Curl_dyn_len(&buf->buf);
+  if(size - buf->index < len)
+    len = size - buf->index;
+  memcpy(data, Curl_dyn_ptr(&buf->buf) + buf->index, len);
   buf->index += len;
   buf->index += len;
   return len;
   return len;
 }
 }
@@ -586,7 +596,7 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex,
   while(len > 0) {
   while(len > 0) {
     if(read_data(data, sockindex, &conn->in_buffer))
     if(read_data(data, sockindex, &conn->in_buffer))
       return -1;
       return -1;
-    if(conn->in_buffer.size == 0) {
+    if(Curl_dyn_len(&conn->in_buffer.buf) == 0) {
       if(bytes_read > 0)
       if(bytes_read > 0)
         conn->in_buffer.eof_flag = 1;
         conn->in_buffer.eof_flag = 1;
       return bytes_read;
       return bytes_read;
@@ -835,6 +845,7 @@ static CURLcode choose_mech(struct Curl_easy *data, struct connectdata *conn)
             mech->name);
             mech->name);
       return CURLE_FAILED_INIT;
       return CURLE_FAILED_INIT;
     }
     }
+    Curl_dyn_init(&conn->in_buffer.buf, CURL_MAX_INPUT_LENGTH);
   }
   }
 
 
   infof(data, "Trying mechanism %s...", mech->name);
   infof(data, "Trying mechanism %s...", mech->name);
@@ -899,15 +910,10 @@ Curl_sec_end(struct connectdata *conn)
 {
 {
   if(conn->mech && conn->mech->end)
   if(conn->mech && conn->mech->end)
     conn->mech->end(conn->app_data);
     conn->mech->end(conn->app_data);
-  free(conn->app_data);
-  conn->app_data = NULL;
-  if(conn->in_buffer.data) {
-    free(conn->in_buffer.data);
-    conn->in_buffer.data = NULL;
-    conn->in_buffer.size = 0;
-    conn->in_buffer.index = 0;
-    conn->in_buffer.eof_flag = 0;
-  }
+  Curl_safefree(conn->app_data);
+  Curl_dyn_free(&conn->in_buffer.buf);
+  conn->in_buffer.index = 0;
+  conn->in_buffer.eof_flag = 0;
   conn->sec_complete = 0;
   conn->sec_complete = 0;
   conn->data_prot = PROT_CLEAR;
   conn->data_prot = PROT_CLEAR;
   conn->mech = NULL;
   conn->mech = NULL;

+ 8 - 3
lib/ldap.c

@@ -164,7 +164,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done);
  */
  */
 
 
 const struct Curl_handler Curl_handler_ldap = {
 const struct Curl_handler Curl_handler_ldap = {
-  "LDAP",                               /* scheme */
+  "ldap",                               /* scheme */
   ZERO_NULL,                            /* setup_connection */
   ZERO_NULL,                            /* setup_connection */
   ldap_do,                              /* do_it */
   ldap_do,                              /* do_it */
   ZERO_NULL,                            /* done */
   ZERO_NULL,                            /* done */
@@ -178,6 +178,7 @@ const struct Curl_handler Curl_handler_ldap = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* attach connection */
   PORT_LDAP,                            /* defport */
   PORT_LDAP,                            /* defport */
@@ -192,7 +193,7 @@ const struct Curl_handler Curl_handler_ldap = {
  */
  */
 
 
 const struct Curl_handler Curl_handler_ldaps = {
 const struct Curl_handler Curl_handler_ldaps = {
-  "LDAPS",                              /* scheme */
+  "ldaps",                              /* scheme */
   ZERO_NULL,                            /* setup_connection */
   ZERO_NULL,                            /* setup_connection */
   ldap_do,                              /* do_it */
   ldap_do,                              /* do_it */
   ZERO_NULL,                            /* done */
   ZERO_NULL,                            /* done */
@@ -206,6 +207,7 @@ const struct Curl_handler Curl_handler_ldaps = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* attach connection */
   PORT_LDAPS,                           /* defport */
   PORT_LDAPS,                           /* defport */
@@ -483,6 +485,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
     }
     }
 */
 */
 #else
 #else
+    (void)ldap_option;
+    (void)ldap_ca;
     /* we should probably never come up to here since configure
     /* we should probably never come up to here since configure
        should check in first place if we can support LDAP SSL/TLS */
        should check in first place if we can support LDAP SSL/TLS */
     failf(data, "LDAP local: SSL/TLS not supported with this version "
     failf(data, "LDAP local: SSL/TLS not supported with this version "
@@ -544,7 +548,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
     goto quit;
     goto quit;
   }
   }
 
 
-  for(num = 0, entryIterator = ldap_first_entry(server, ldapmsg);
+  num = 0;
+  for(entryIterator = ldap_first_entry(server, ldapmsg);
       entryIterator;
       entryIterator;
       entryIterator = ldap_next_entry(server, entryIterator), num++) {
       entryIterator = ldap_next_entry(server, entryIterator), num++) {
     BerElement *ber = NULL;
     BerElement *ber = NULL;

+ 16 - 0
lib/llist.c

@@ -88,6 +88,22 @@ Curl_llist_insert_next(struct Curl_llist *list, struct Curl_llist_element *e,
   ++list->size;
   ++list->size;
 }
 }
 
 
+/*
+ * Curl_llist_append()
+ *
+ * Adds a new list element to the end of the list.
+ *
+ * The 'ne' argument should be a pointer into the object to store.
+ *
+ * @unittest: 1300
+ */
+void
+Curl_llist_append(struct Curl_llist *list, const void *p,
+                  struct Curl_llist_element *ne)
+{
+  Curl_llist_insert_next(list, list->tail, p, ne);
+}
+
 /*
 /*
  * @unittest: 1300
  * @unittest: 1300
  */
  */

+ 2 - 0
lib/llist.h

@@ -45,6 +45,8 @@ struct Curl_llist {
 void Curl_llist_init(struct Curl_llist *, Curl_llist_dtor);
 void Curl_llist_init(struct Curl_llist *, Curl_llist_dtor);
 void Curl_llist_insert_next(struct Curl_llist *, struct Curl_llist_element *,
 void Curl_llist_insert_next(struct Curl_llist *, struct Curl_llist_element *,
                             const void *, struct Curl_llist_element *node);
                             const void *, struct Curl_llist_element *node);
+void Curl_llist_append(struct Curl_llist *,
+                       const void *, struct Curl_llist_element *node);
 void Curl_llist_remove(struct Curl_llist *, struct Curl_llist_element *,
 void Curl_llist_remove(struct Curl_llist *, struct Curl_llist_element *,
                        void *);
                        void *);
 size_t Curl_llist_count(struct Curl_llist *);
 size_t Curl_llist_count(struct Curl_llist *);

+ 2 - 1
lib/md4.c

@@ -55,7 +55,8 @@
 #else
 #else
 #include <mbedtls/config.h>
 #include <mbedtls/config.h>
 #endif
 #endif
-#if(MBEDTLS_VERSION_NUMBER >= 0x02070000)
+#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) && \
+   (MBEDTLS_VERSION_NUMBER < 0x03000000)
   #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS
   #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS
 #endif
 #endif
 #endif /* USE_MBEDTLS */
 #endif /* USE_MBEDTLS */

+ 27 - 28
lib/mime.c

@@ -1413,36 +1413,35 @@ CURLcode curl_mime_filedata(curl_mimepart *part, const char *filename)
     char *base;
     char *base;
     struct_stat sbuf;
     struct_stat sbuf;
 
 
-    if(stat(filename, &sbuf) || access(filename, R_OK))
+    if(stat(filename, &sbuf))
       result = CURLE_READ_ERROR;
       result = CURLE_READ_ERROR;
-
-    part->data = strdup(filename);
-    if(!part->data)
-      result = CURLE_OUT_OF_MEMORY;
-
-    part->datasize = -1;
-    if(!result && S_ISREG(sbuf.st_mode)) {
-      part->datasize = filesize(filename, sbuf);
-      part->seekfunc = mime_file_seek;
-    }
-
-    part->readfunc = mime_file_read;
-    part->freefunc = mime_file_free;
-    part->kind = MIMEKIND_FILE;
-
-    /* As a side effect, set the filename to the current file's base name.
-       It is possible to withdraw this by explicitly calling
-       curl_mime_filename() with a NULL filename argument after the current
-       call. */
-    base = strippath(filename);
-    if(!base)
-      result = CURLE_OUT_OF_MEMORY;
     else {
     else {
-      CURLcode res = curl_mime_filename(part, base);
+      part->data = strdup(filename);
+      if(!part->data)
+        result = CURLE_OUT_OF_MEMORY;
+      else {
+        part->datasize = -1;
+        if(S_ISREG(sbuf.st_mode)) {
+          part->datasize = filesize(filename, sbuf);
+          part->seekfunc = mime_file_seek;
+        }
 
 
-      if(res)
-        result = res;
-      free(base);
+        part->readfunc = mime_file_read;
+        part->freefunc = mime_file_free;
+        part->kind = MIMEKIND_FILE;
+
+        /* As a side effect, set the filename to the current file's base name.
+           It is possible to withdraw this by explicitly calling
+           curl_mime_filename() with a NULL filename argument after the current
+           call. */
+        base = strippath(filename);
+        if(!base)
+          result = CURLE_OUT_OF_MEMORY;
+        else {
+          result = curl_mime_filename(part, base);
+          free(base);
+        }
+      }
     }
     }
   }
   }
   return result;
   return result;
@@ -1971,7 +1970,7 @@ static CURLcode cr_mime_read(struct Curl_easy *data,
   switch(nread) {
   switch(nread) {
   case 0:
   case 0:
     if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) {
     if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) {
-      failf(data, "client mime read EOF fail, only "
+      failf(data, "client mime read EOF fail, "
             "only %"CURL_FORMAT_CURL_OFF_T"/%"CURL_FORMAT_CURL_OFF_T
             "only %"CURL_FORMAT_CURL_OFF_T"/%"CURL_FORMAT_CURL_OFF_T
             " of needed bytes read", ctx->read_len, ctx->total_len);
             " of needed bytes read", ctx->read_len, ctx->total_len);
       return CURLE_READ_ERROR;
       return CURLE_READ_ERROR;

+ 20 - 20
lib/mprintf.c

@@ -77,7 +77,7 @@ static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 
 
 #define OUTCHAR(x)                                      \
 #define OUTCHAR(x)                                      \
   do {                                                  \
   do {                                                  \
-    if(!stream(x, userp))                               \
+    if(!stream((unsigned char)x, userp))                \
       done++;                                           \
       done++;                                           \
     else                                                \
     else                                                \
       return done; /* return on failure */              \
       return done; /* return on failure */              \
@@ -243,7 +243,7 @@ static int parsefmt(const char *format,
       struct va_input *iptr;
       struct va_input *iptr;
       bool loopit = TRUE;
       bool loopit = TRUE;
       fmt++;
       fmt++;
-      outlen = fmt - start - 1;
+      outlen = (size_t)(fmt - start - 1);
       if(*fmt == '%') {
       if(*fmt == '%') {
         /* this means a %% that should be output only as %. Create an output
         /* this means a %% that should be output only as %. Create an output
            segment. */
            segment. */
@@ -261,7 +261,8 @@ static int parsefmt(const char *format,
         continue; /* while */
         continue; /* while */
       }
       }
 
 
-      flags = width = precision = 0;
+      flags = 0;
+      width = precision = 0;
 
 
       if(use_dollar != DOLLAR_NOPE) {
       if(use_dollar != DOLLAR_NOPE) {
         param = dollarstring(fmt, &fmt);
         param = dollarstring(fmt, &fmt);
@@ -291,7 +292,7 @@ static int parsefmt(const char *format,
           break;
           break;
         case '-':
         case '-':
           flags |= FLAGS_LEFT;
           flags |= FLAGS_LEFT;
-          flags &= ~FLAGS_PAD_NIL;
+          flags &= ~(unsigned int)FLAGS_PAD_NIL;
           break;
           break;
         case '#':
         case '#':
           flags |= FLAGS_ALT;
           flags |= FLAGS_ALT;
@@ -549,7 +550,7 @@ static int parsefmt(const char *format,
       optr = &out[ocount++];
       optr = &out[ocount++];
       if(ocount > MAX_SEGMENTS)
       if(ocount > MAX_SEGMENTS)
         return PFMT_MANYSEGS;
         return PFMT_MANYSEGS;
-      optr->input = param;
+      optr->input = (unsigned int)param;
       optr->flags = flags;
       optr->flags = flags;
       optr->width = width;
       optr->width = width;
       optr->precision = precision;
       optr->precision = precision;
@@ -562,7 +563,7 @@ static int parsefmt(const char *format,
   }
   }
 
 
   /* is there a trailing piece */
   /* is there a trailing piece */
-  outlen = fmt - start;
+  outlen = (size_t)(fmt - start);
   if(outlen) {
   if(outlen) {
     optr = &out[ocount++];
     optr = &out[ocount++];
     if(ocount > MAX_SEGMENTS)
     if(ocount > MAX_SEGMENTS)
@@ -688,7 +689,7 @@ static int formatf(
     mp_intmax_t signed_num; /* Used to convert negative in positive.  */
     mp_intmax_t signed_num; /* Used to convert negative in positive.  */
     char *w;
     char *w;
     size_t outlen = optr->outlen;
     size_t outlen = optr->outlen;
-    int flags = optr->flags;
+    unsigned int flags = optr->flags;
 
 
     if(outlen) {
     if(outlen) {
       char *str = optr->start;
       char *str = optr->start;
@@ -710,7 +711,7 @@ static int formatf(
         else
         else
           width = -width;
           width = -width;
         flags |= FLAGS_LEFT;
         flags |= FLAGS_LEFT;
-        flags &= ~FLAGS_PAD_NIL;
+        flags &= ~(unsigned int)FLAGS_PAD_NIL;
       }
       }
     }
     }
     else
     else
@@ -867,7 +868,7 @@ number:
           str = nilstr;
           str = nilstr;
           len = sizeof(nilstr) - 1;
           len = sizeof(nilstr) - 1;
           /* Disable quotes around (nil) */
           /* Disable quotes around (nil) */
-          flags &= (~FLAGS_ALT);
+          flags &= ~(unsigned int)FLAGS_ALT;
         }
         }
         else {
         else {
           str = "";
           str = "";
@@ -886,13 +887,13 @@ number:
       if(flags & FLAGS_ALT)
       if(flags & FLAGS_ALT)
         OUTCHAR('"');
         OUTCHAR('"');
 
 
-      if(!(flags&FLAGS_LEFT))
+      if(!(flags & FLAGS_LEFT))
         while(width-- > 0)
         while(width-- > 0)
           OUTCHAR(' ');
           OUTCHAR(' ');
 
 
       for(; len && *str; len--)
       for(; len && *str; len--)
         OUTCHAR(*str++);
         OUTCHAR(*str++);
-      if(flags&FLAGS_LEFT)
+      if(flags & FLAGS_LEFT)
         while(width-- > 0)
         while(width-- > 0)
           OUTCHAR(' ');
           OUTCHAR(' ');
 
 
@@ -952,12 +953,13 @@ number:
       *fptr = 0;
       *fptr = 0;
 
 
       if(width >= 0) {
       if(width >= 0) {
+        size_t dlen;
         if(width >= (int)sizeof(work))
         if(width >= (int)sizeof(work))
           width = sizeof(work)-1;
           width = sizeof(work)-1;
         /* RECURSIVE USAGE */
         /* RECURSIVE USAGE */
-        len = curl_msnprintf(fptr, left, "%d", width);
-        fptr += len;
-        left -= len;
+        dlen = (size_t)curl_msnprintf(fptr, left, "%d", width);
+        fptr += dlen;
+        left -= dlen;
       }
       }
       if(prec >= 0) {
       if(prec >= 0) {
         /* for each digit in the integer part, we can have one less
         /* for each digit in the integer part, we can have one less
@@ -965,7 +967,7 @@ number:
         size_t maxprec = sizeof(work) - 2;
         size_t maxprec = sizeof(work) - 2;
         double val = iptr->val.dnum;
         double val = iptr->val.dnum;
         if(width > 0 && prec <= width)
         if(width > 0 && prec <= width)
-          maxprec -= width;
+          maxprec -= (size_t)width;
         while(val >= 10.0) {
         while(val >= 10.0) {
           val /= 10;
           val /= 10;
           maxprec--;
           maxprec--;
@@ -1039,7 +1041,7 @@ static int addbyter(unsigned char outc, void *f)
   struct nsprintf *infop = f;
   struct nsprintf *infop = f;
   if(infop->length < infop->max) {
   if(infop->length < infop->max) {
     /* only do this if we haven't reached max length yet */
     /* only do this if we haven't reached max length yet */
-    *infop->buffer++ = outc; /* store */
+    *infop->buffer++ = (char)outc; /* store */
     infop->length++; /* we are now one byte larger */
     infop->length++; /* we are now one byte larger */
     return 0;     /* fputc() returns like this on success */
     return 0;     /* fputc() returns like this on success */
   }
   }
@@ -1139,7 +1141,7 @@ char *curl_maprintf(const char *format, ...)
 static int storebuffer(unsigned char outc, void *f)
 static int storebuffer(unsigned char outc, void *f)
 {
 {
   char **buffer = f;
   char **buffer = f;
-  **buffer = outc;
+  **buffer = (char)outc;
   (*buffer)++;
   (*buffer)++;
   return 0;
   return 0;
 }
 }
@@ -1160,9 +1162,7 @@ static int fputc_wrapper(unsigned char outc, void *f)
   int out = outc;
   int out = outc;
   FILE *s = f;
   FILE *s = f;
   int rc = fputc(out, s);
   int rc = fputc(out, s);
-  if(rc == out)
-    return 0;
-  return 1;
+  return rc == EOF;
 }
 }
 
 
 int curl_mprintf(const char *format, ...)
 int curl_mprintf(const char *format, ...)

+ 4 - 3
lib/mqtt.c

@@ -75,7 +75,7 @@ static CURLcode mqtt_setup_conn(struct Curl_easy *data,
  */
  */
 
 
 const struct Curl_handler Curl_handler_mqtt = {
 const struct Curl_handler Curl_handler_mqtt = {
-  "MQTT",                             /* scheme */
+  "mqtt",                             /* scheme */
   mqtt_setup_conn,                    /* setup_connection */
   mqtt_setup_conn,                    /* setup_connection */
   mqtt_do,                            /* do_it */
   mqtt_do,                            /* do_it */
   mqtt_done,                          /* done */
   mqtt_done,                          /* done */
@@ -89,6 +89,7 @@ const struct Curl_handler Curl_handler_mqtt = {
   ZERO_NULL,                          /* perform_getsock */
   ZERO_NULL,                          /* perform_getsock */
   ZERO_NULL,                          /* disconnect */
   ZERO_NULL,                          /* disconnect */
   ZERO_NULL,                          /* write_resp */
   ZERO_NULL,                          /* write_resp */
+  ZERO_NULL,                          /* write_resp_hd */
   ZERO_NULL,                          /* connection_check */
   ZERO_NULL,                          /* connection_check */
   ZERO_NULL,                          /* attach connection */
   ZERO_NULL,                          /* attach connection */
   PORT_MQTT,                          /* defport */
   PORT_MQTT,                          /* defport */
@@ -776,12 +777,12 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
   case MQTT_REMAINING_LENGTH:
   case MQTT_REMAINING_LENGTH:
     do {
     do {
       result = Curl_xfer_recv(data, (char *)&byte, 1, &nread);
       result = Curl_xfer_recv(data, (char *)&byte, 1, &nread);
-      if(!nread)
+      if(result || !nread)
         break;
         break;
       Curl_debug(data, CURLINFO_HEADER_IN, (char *)&byte, 1);
       Curl_debug(data, CURLINFO_HEADER_IN, (char *)&byte, 1);
       mq->pkt_hd[mq->npacket++] = byte;
       mq->pkt_hd[mq->npacket++] = byte;
     } while((byte & 0x80) && (mq->npacket < 4));
     } while((byte & 0x80) && (mq->npacket < 4));
-    if(nread && (byte & 0x80))
+    if(!result && nread && (byte & 0x80))
       /* MQTT supports up to 127 * 128^0 + 127 * 128^1 + 127 * 128^2 +
       /* MQTT supports up to 127 * 128^0 + 127 * 128^1 + 127 * 128^2 +
          127 * 128^3 bytes. server tried to send more */
          127 * 128^3 bytes. server tried to send more */
       result = CURLE_WEIRD_SERVER_REPLY;
       result = CURLE_WEIRD_SERVER_REPLY;

+ 248 - 174
lib/multi.c

@@ -86,6 +86,8 @@
   ((x) && (x)->magic == CURL_MULTI_HANDLE)
   ((x) && (x)->magic == CURL_MULTI_HANDLE)
 #endif
 #endif
 
 
+static void move_pending_to_connect(struct Curl_multi *multi,
+                                    struct Curl_easy *data);
 static CURLMcode singlesocket(struct Curl_multi *multi,
 static CURLMcode singlesocket(struct Curl_multi *multi,
                               struct Curl_easy *data);
                               struct Curl_easy *data);
 static CURLMcode add_next_timeout(struct curltime now,
 static CURLMcode add_next_timeout(struct curltime now,
@@ -100,6 +102,7 @@ static void multi_xfer_bufs_free(struct Curl_multi *multi);
 static const char * const multi_statename[]={
 static const char * const multi_statename[]={
   "INIT",
   "INIT",
   "PENDING",
   "PENDING",
+  "SETUP",
   "CONNECT",
   "CONNECT",
   "RESOLVING",
   "RESOLVING",
   "CONNECTING",
   "CONNECTING",
@@ -149,6 +152,7 @@ static void mstate(struct Curl_easy *data, CURLMstate state
   static const init_multistate_func finit[MSTATE_LAST] = {
   static const init_multistate_func finit[MSTATE_LAST] = {
     NULL,              /* INIT */
     NULL,              /* INIT */
     NULL,              /* PENDING */
     NULL,              /* PENDING */
+    NULL,              /* SETUP */
     Curl_init_CONNECT, /* CONNECT */
     Curl_init_CONNECT, /* CONNECT */
     NULL,              /* RESOLVING */
     NULL,              /* RESOLVING */
     NULL,              /* CONNECTING */
     NULL,              /* CONNECTING */
@@ -360,7 +364,7 @@ static size_t hash_fd(void *key, size_t key_length, size_t slots_num)
  * per call."
  * per call."
  *
  *
  */
  */
-static void sh_init(struct Curl_hash *hash, int hashsize)
+static void sh_init(struct Curl_hash *hash, size_t hashsize)
 {
 {
   Curl_hash_init(hash, hashsize, hash_fd, fd_key_compare,
   Curl_hash_init(hash, hashsize, hash_fd, fd_key_compare,
                  sh_freeentry);
                  sh_freeentry);
@@ -374,13 +378,12 @@ static void sh_init(struct Curl_hash *hash, int hashsize)
  */
  */
 static void multi_addmsg(struct Curl_multi *multi, struct Curl_message *msg)
 static void multi_addmsg(struct Curl_multi *multi, struct Curl_message *msg)
 {
 {
-  Curl_llist_insert_next(&multi->msglist, multi->msglist.tail, msg,
-                         &msg->list);
+  Curl_llist_append(&multi->msglist, msg, &msg->list);
 }
 }
 
 
-struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
-                                     int chashsize, /* connection hash */
-                                     int dnssize) /* dns hash */
+struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */
+                                     size_t chashsize, /* connection hash */
+                                     size_t dnssize) /* dns hash */
 {
 {
   struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi));
   struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi));
 
 
@@ -549,6 +552,8 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
   if(data->set.errorbuffer)
   if(data->set.errorbuffer)
     data->set.errorbuffer[0] = 0;
     data->set.errorbuffer[0] = 0;
 
 
+  data->state.os_errno = 0;
+
   /* make the Curl_easy refer back to this multi handle - before Curl_expire()
   /* make the Curl_easy refer back to this multi handle - before Curl_expire()
      is called. */
      is called. */
   data->multi = multi;
   data->multi = multi;
@@ -1002,8 +1007,7 @@ void Curl_attach_connection(struct Curl_easy *data,
   DEBUGASSERT(!data->conn);
   DEBUGASSERT(!data->conn);
   DEBUGASSERT(conn);
   DEBUGASSERT(conn);
   data->conn = conn;
   data->conn = conn;
-  Curl_llist_insert_next(&conn->easyq, conn->easyq.tail, data,
-                         &data->conn_queue);
+  Curl_llist_append(&conn->easyq, data, &data->conn_queue);
   if(conn->handler && conn->handler->attach)
   if(conn->handler && conn->handler->attach)
     conn->handler->attach(data, conn);
     conn->handler->attach(data, conn);
   Curl_conn_ev_data_attach(conn, data);
   Curl_conn_ev_data_attach(conn, data);
@@ -1112,6 +1116,7 @@ static void multi_getsock(struct Curl_easy *data,
   switch(data->mstate) {
   switch(data->mstate) {
   case MSTATE_INIT:
   case MSTATE_INIT:
   case MSTATE_PENDING:
   case MSTATE_PENDING:
+  case MSTATE_SETUP:
   case MSTATE_CONNECT:
   case MSTATE_CONNECT:
     /* nothing to poll for yet */
     /* nothing to poll for yet */
     break;
     break;
@@ -1208,6 +1213,68 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi,
   return CURLM_OK;
   return CURLM_OK;
 }
 }
 
 
+CURLMcode curl_multi_waitfds(struct Curl_multi *multi,
+                             struct curl_waitfd *ufds,
+                             unsigned int size,
+                             unsigned int *fd_count)
+{
+  struct Curl_easy *data;
+  unsigned int nfds = 0;
+  struct easy_pollset ps;
+  unsigned int i;
+  CURLMcode result = CURLM_OK;
+  struct curl_waitfd *ufd;
+  unsigned int j;
+
+  if(!ufds)
+    return CURLM_BAD_FUNCTION_ARGUMENT;
+
+  if(!GOOD_MULTI_HANDLE(multi))
+    return CURLM_BAD_HANDLE;
+
+  if(multi->in_callback)
+    return CURLM_RECURSIVE_API_CALL;
+
+  memset(&ps, 0, sizeof(ps));
+  for(data = multi->easyp; data; data = data->next) {
+    multi_getsock(data, &ps);
+
+    for(i = 0; i < ps.num; i++) {
+      if(nfds < size) {
+        curl_socket_t fd = ps.sockets[i];
+        int fd_idx = -1;
+
+        /* Simple linear search to skip an already added descriptor */
+        for(j = 0; j < nfds; j++) {
+          if(ufds[j].fd == fd) {
+            fd_idx = (int)j;
+            break;
+          }
+        }
+
+        if(fd_idx < 0) {
+          ufd = &ufds[nfds++];
+          ufd->fd = ps.sockets[i];
+          ufd->events = 0;
+        }
+        else
+          ufd = &ufds[fd_idx];
+
+        if(ps.actions[i] & CURL_POLL_IN)
+          ufd->events |= CURL_WAIT_POLLIN;
+        if(ps.actions[i] & CURL_POLL_OUT)
+          ufd->events |= CURL_WAIT_POLLOUT;
+      }
+      else
+        return CURLM_OUT_OF_MEMORY;
+    }
+  }
+
+  if(fd_count)
+    *fd_count = nfds;
+  return result;
+}
+
 #ifdef USE_WINSOCK
 #ifdef USE_WINSOCK
 /* Reset FD_WRITE for TCP sockets. Nothing is actually sent. UDP sockets can't
 /* Reset FD_WRITE for TCP sockets. Nothing is actually sent. UDP sockets can't
  * be reset this way because an empty datagram would be sent. #9203
  * be reset this way because an empty datagram would be sent. #9203
@@ -1224,6 +1291,29 @@ static void reset_socket_fdwrite(curl_socket_t s)
 }
 }
 #endif
 #endif
 
 
+static CURLMcode ufds_increase(struct pollfd **pfds, unsigned int *pfds_len,
+                               unsigned int inc, bool *is_malloced)
+{
+  struct pollfd *new_fds, *old_fds = *pfds;
+  unsigned int new_len = *pfds_len + inc;
+
+  new_fds = calloc(new_len, sizeof(struct pollfd));
+  if(!new_fds) {
+    if(*is_malloced)
+      free(old_fds);
+    *pfds = NULL;
+    *pfds_len = 0;
+    return CURLM_OUT_OF_MEMORY;
+  }
+  memcpy(new_fds, old_fds, (*pfds_len) * sizeof(struct pollfd));
+  if(*is_malloced)
+    free(old_fds);
+  *pfds = new_fds;
+  *pfds_len = new_len;
+  *is_malloced = TRUE;
+  return CURLM_OK;
+}
+
 #define NUM_POLLS_ON_STACK 10
 #define NUM_POLLS_ON_STACK 10
 
 
 static CURLMcode multi_wait(struct Curl_multi *multi,
 static CURLMcode multi_wait(struct Curl_multi *multi,
@@ -1237,12 +1327,12 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
   struct Curl_easy *data;
   struct Curl_easy *data;
   struct easy_pollset ps;
   struct easy_pollset ps;
   size_t i;
   size_t i;
-  unsigned int nfds = 0;
-  unsigned int curlfds;
   long timeout_internal;
   long timeout_internal;
   int retcode = 0;
   int retcode = 0;
   struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
   struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
   struct pollfd *ufds = &a_few_on_stack[0];
   struct pollfd *ufds = &a_few_on_stack[0];
+  unsigned int ufds_len = NUM_POLLS_ON_STACK;
+  unsigned int nfds = 0, curl_nfds = 0; /* how many ufds are in use */
   bool ufds_malloc = FALSE;
   bool ufds_malloc = FALSE;
 #ifdef USE_WINSOCK
 #ifdef USE_WINSOCK
   WSANETWORKEVENTS wsa_events;
   WSANETWORKEVENTS wsa_events;
@@ -1261,13 +1351,6 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
   if(timeout_ms < 0)
   if(timeout_ms < 0)
     return CURLM_BAD_FUNCTION_ARGUMENT;
     return CURLM_BAD_FUNCTION_ARGUMENT;
 
 
-  /* Count up how many fds we have from the multi handle */
-  memset(&ps, 0, sizeof(ps));
-  for(data = multi->easyp; data; data = data->next) {
-    multi_getsock(data, &ps);
-    nfds += ps.num;
-  }
-
   /* If the internally desired timeout is actually shorter than requested from
   /* If the internally desired timeout is actually shorter than requested from
      the outside, then use the shorter time! But only if the internal timer
      the outside, then use the shorter time! But only if the internal timer
      is actually larger than -1! */
      is actually larger than -1! */
@@ -1275,70 +1358,60 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
   if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms))
   if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms))
     timeout_ms = (int)timeout_internal;
     timeout_ms = (int)timeout_internal;
 
 
-  curlfds = nfds; /* number of internal file descriptors */
-  nfds += extra_nfds; /* add the externally provided ones */
-
-#ifdef ENABLE_WAKEUP
-#ifdef USE_WINSOCK
-  if(use_wakeup) {
-#else
-  if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
-#endif
-    ++nfds;
-  }
-#endif
-
-  if(nfds > NUM_POLLS_ON_STACK) {
-    /* 'nfds' is a 32 bit value and 'struct pollfd' is typically 8 bytes
-       big, so at 2^29 sockets this value might wrap. When a process gets
-       the capability to actually handle over 500 million sockets this
-       calculation needs a integer overflow check. */
-    ufds = malloc(nfds * sizeof(struct pollfd));
-    if(!ufds)
-      return CURLM_OUT_OF_MEMORY;
-    ufds_malloc = TRUE;
-  }
-  nfds = 0;
-
-  /* only do the second loop if we found descriptors in the first stage run
-     above */
+  memset(ufds, 0, ufds_len * sizeof(struct pollfd));
+  memset(&ps, 0, sizeof(ps));
 
 
-  if(curlfds) {
-    /* Add the curl handles to our pollfds first */
-    for(data = multi->easyp; data; data = data->next) {
-      multi_getsock(data, &ps);
+  /* Add the curl handles to our pollfds first */
+  for(data = multi->easyp; data; data = data->next) {
+    multi_getsock(data, &ps);
 
 
-      for(i = 0; i < ps.num; i++) {
-        struct pollfd *ufd = &ufds[nfds++];
+    for(i = 0; i < ps.num; i++) {
+      short events = 0;
 #ifdef USE_WINSOCK
 #ifdef USE_WINSOCK
-        long mask = 0;
+      long mask = 0;
 #endif
 #endif
-        ufd->fd = ps.sockets[i];
-        ufd->events = 0;
-        if(ps.actions[i] & CURL_POLL_IN) {
+      if(ps.actions[i] & CURL_POLL_IN) {
 #ifdef USE_WINSOCK
 #ifdef USE_WINSOCK
-          mask |= FD_READ|FD_ACCEPT|FD_CLOSE;
+        mask |= FD_READ|FD_ACCEPT|FD_CLOSE;
 #endif
 #endif
-          ufd->events |= POLLIN;
-        }
-        if(ps.actions[i] & CURL_POLL_OUT) {
+        events |= POLLIN;
+      }
+      if(ps.actions[i] & CURL_POLL_OUT) {
 #ifdef USE_WINSOCK
 #ifdef USE_WINSOCK
-          mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
-          reset_socket_fdwrite(ps.sockets[i]);
+        mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
+        reset_socket_fdwrite(ps.sockets[i]);
 #endif
 #endif
-          ufd->events |= POLLOUT;
+        events |= POLLOUT;
+      }
+      if(events) {
+        if(nfds && ps.sockets[i] == ufds[nfds-1].fd) {
+          ufds[nfds-1].events |= events;
         }
         }
+        else {
+          if(nfds >= ufds_len) {
+            if(ufds_increase(&ufds, &ufds_len, 100, &ufds_malloc))
+              return CURLM_OUT_OF_MEMORY;
+          }
+          DEBUGASSERT(nfds < ufds_len);
+          ufds[nfds].fd = ps.sockets[i];
+          ufds[nfds].events = events;
+          ++nfds;
+        }
+      }
 #ifdef USE_WINSOCK
 #ifdef USE_WINSOCK
+      if(mask) {
         if(WSAEventSelect(ps.sockets[i], multi->wsa_event, mask) != 0) {
         if(WSAEventSelect(ps.sockets[i], multi->wsa_event, mask) != 0) {
           if(ufds_malloc)
           if(ufds_malloc)
             free(ufds);
             free(ufds);
           return CURLM_INTERNAL_ERROR;
           return CURLM_INTERNAL_ERROR;
         }
         }
-#endif
       }
       }
+#endif
     }
     }
   }
   }
 
 
+  curl_nfds = nfds; /* what curl internally used in ufds */
+
   /* Add external file descriptions from poll-like struct curl_waitfd */
   /* Add external file descriptions from poll-like struct curl_waitfd */
   for(i = 0; i < extra_nfds; i++) {
   for(i = 0; i < extra_nfds; i++) {
 #ifdef USE_WINSOCK
 #ifdef USE_WINSOCK
@@ -1357,6 +1430,11 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
       return CURLM_INTERNAL_ERROR;
       return CURLM_INTERNAL_ERROR;
     }
     }
 #endif
 #endif
+    if(nfds >= ufds_len) {
+      if(ufds_increase(&ufds, &ufds_len, 100, &ufds_malloc))
+        return CURLM_OUT_OF_MEMORY;
+    }
+    DEBUGASSERT(nfds < ufds_len);
     ufds[nfds].fd = extra_fds[i].fd;
     ufds[nfds].fd = extra_fds[i].fd;
     ufds[nfds].events = 0;
     ufds[nfds].events = 0;
     if(extra_fds[i].events & CURL_WAIT_POLLIN)
     if(extra_fds[i].events & CURL_WAIT_POLLIN)
@@ -1371,6 +1449,11 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
 #ifdef ENABLE_WAKEUP
 #ifdef ENABLE_WAKEUP
 #ifndef USE_WINSOCK
 #ifndef USE_WINSOCK
   if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
   if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
+    if(nfds >= ufds_len) {
+      if(ufds_increase(&ufds, &ufds_len, 100, &ufds_malloc))
+        return CURLM_OUT_OF_MEMORY;
+    }
+    DEBUGASSERT(nfds < ufds_len);
     ufds[nfds].fd = multi->wakeup_pair[0];
     ufds[nfds].fd = multi->wakeup_pair[0];
     ufds[nfds].events = POLLIN;
     ufds[nfds].events = POLLIN;
     ++nfds;
     ++nfds;
@@ -1410,7 +1493,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
          struct, the bit values of the actual underlying poll() implementation
          struct, the bit values of the actual underlying poll() implementation
          may not be the same as the ones in the public libcurl API! */
          may not be the same as the ones in the public libcurl API! */
       for(i = 0; i < extra_nfds; i++) {
       for(i = 0; i < extra_nfds; i++) {
-        unsigned r = ufds[curlfds + i].revents;
+        unsigned r = ufds[curl_nfds + i].revents;
         unsigned short mask = 0;
         unsigned short mask = 0;
 #ifdef USE_WINSOCK
 #ifdef USE_WINSOCK
         curl_socket_t s = extra_fds[i].fd;
         curl_socket_t s = extra_fds[i].fd;
@@ -1443,7 +1526,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
 #ifdef USE_WINSOCK
 #ifdef USE_WINSOCK
       /* Count up all our own sockets that had activity,
       /* Count up all our own sockets that had activity,
          and remove them from the event. */
          and remove them from the event. */
-      if(curlfds) {
+      if(curl_nfds) {
 
 
         for(data = multi->easyp; data; data = data->next) {
         for(data = multi->easyp; data; data = data->next) {
           multi_getsock(data, &ps);
           multi_getsock(data, &ps);
@@ -1464,7 +1547,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
 #else
 #else
 #ifdef ENABLE_WAKEUP
 #ifdef ENABLE_WAKEUP
       if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
       if(use_wakeup && multi->wakeup_pair[0] != CURL_SOCKET_BAD) {
-        if(ufds[curlfds + extra_nfds].revents & POLLIN) {
+        if(ufds[curl_nfds + extra_nfds].revents & POLLIN) {
           char buf[64];
           char buf[64];
           ssize_t nread;
           ssize_t nread;
           while(1) {
           while(1) {
@@ -1684,47 +1767,47 @@ static bool multi_handle_timeout(struct Curl_easy *data,
                                  CURLcode *result,
                                  CURLcode *result,
                                  bool connect_timeout)
                                  bool connect_timeout)
 {
 {
-  timediff_t timeout_ms;
-  timeout_ms = Curl_timeleft(data, now, connect_timeout);
-
+  timediff_t timeout_ms = Curl_timeleft(data, now, connect_timeout);
   if(timeout_ms < 0) {
   if(timeout_ms < 0) {
     /* Handle timed out */
     /* Handle timed out */
+    struct curltime since;
+    if(connect_timeout)
+      since = data->progress.t_startsingle;
+    else
+      since = data->progress.t_startop;
     if(data->mstate == MSTATE_RESOLVING)
     if(data->mstate == MSTATE_RESOLVING)
       failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T
       failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T
-            " milliseconds",
-            Curl_timediff(*now, data->progress.t_startsingle));
+            " milliseconds", Curl_timediff(*now, since));
     else if(data->mstate == MSTATE_CONNECTING)
     else if(data->mstate == MSTATE_CONNECTING)
       failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T
       failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T
-            " milliseconds",
-            Curl_timediff(*now, data->progress.t_startsingle));
+            " milliseconds", Curl_timediff(*now, since));
     else {
     else {
       struct SingleRequest *k = &data->req;
       struct SingleRequest *k = &data->req;
       if(k->size != -1) {
       if(k->size != -1) {
         failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
         failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
               " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %"
               " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %"
               CURL_FORMAT_CURL_OFF_T " bytes received",
               CURL_FORMAT_CURL_OFF_T " bytes received",
-              Curl_timediff(*now, data->progress.t_startsingle),
-              k->bytecount, k->size);
+              Curl_timediff(*now, since), k->bytecount, k->size);
       }
       }
       else {
       else {
         failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
         failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
               " milliseconds with %" CURL_FORMAT_CURL_OFF_T
               " milliseconds with %" CURL_FORMAT_CURL_OFF_T
-              " bytes received",
-              Curl_timediff(*now, data->progress.t_startsingle),
-              k->bytecount);
+              " bytes received", Curl_timediff(*now, since), k->bytecount);
       }
       }
     }
     }
-
-    /* Force connection closed if the connection has indeed been used */
-    if(data->mstate > MSTATE_DO) {
-      streamclose(data->conn, "Disconnected with pending data");
-      *stream_error = TRUE;
-    }
     *result = CURLE_OPERATION_TIMEDOUT;
     *result = CURLE_OPERATION_TIMEDOUT;
-    (void)multi_done(data, *result, TRUE);
+    if(data->conn) {
+      /* Force connection closed if the connection has indeed been used */
+      if(data->mstate > MSTATE_DO) {
+        streamclose(data->conn, "Disconnect due to timeout");
+        *stream_error = TRUE;
+      }
+      (void)multi_done(data, *result, TRUE);
+    }
+    return TRUE;
   }
   }
 
 
-  return (timeout_ms < 0);
+  return FALSE;
 }
 }
 
 
 /*
 /*
@@ -1816,19 +1899,6 @@ static CURLcode protocol_connect(struct Curl_easy *data,
   return result; /* pass back status */
   return result; /* pass back status */
 }
 }
 
 
-/*
- * Curl_preconnect() is called immediately before a connect starts. When a
- * redirect is followed, this is then called multiple times during a single
- * transfer.
- */
-CURLcode Curl_preconnect(struct Curl_easy *data)
-{
-  /* this used to do data->state.buffer allocation,
-     maybe remove completely now? */
-  (void)data;
-  return CURLE_OK;
-}
-
 static void set_in_callback(struct Curl_multi *multi, bool value)
 static void set_in_callback(struct Curl_multi *multi, bool value)
 {
 {
   multi->in_callback = value;
   multi->in_callback = value;
@@ -1882,51 +1952,44 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         return CURLM_INTERNAL_ERROR;
         return CURLM_INTERNAL_ERROR;
     }
     }
 
 
-    if(data->conn &&
-       (data->mstate >= MSTATE_CONNECT) &&
-       (data->mstate < MSTATE_COMPLETED)) {
-      /* Check for overall operation timeout here but defer handling the
-       * connection timeout to later, to allow for a connection to be set up
-       * in the window since we last checked timeout. This prevents us
-       * tearing down a completed connection in the case where we were slow
-       * to check the timeout (e.g. process descheduled during this loop).
-       * We set connect_timeout=FALSE to do this. */
-
-      /* we need to wait for the connect state as only then is the start time
-         stored, but we must not check already completed handles */
-      if(multi_handle_timeout(data, nowp, &stream_error, &result, FALSE)) {
-        /* Skip the statemachine and go directly to error handling section. */
-        goto statemachine_end;
-      }
-    }
+    /* Wait for the connect state as only then is the start time stored, but
+       we must not check already completed handles */
+    if((data->mstate >= MSTATE_CONNECT) && (data->mstate < MSTATE_COMPLETED) &&
+       multi_handle_timeout(data, nowp, &stream_error, &result, FALSE))
+      /* Skip the statemachine and go directly to error handling section. */
+      goto statemachine_end;
 
 
     switch(data->mstate) {
     switch(data->mstate) {
     case MSTATE_INIT:
     case MSTATE_INIT:
-      /* init this transfer. */
+      /* Transitional state. init this transfer. A handle never comes
+         back to this state. */
       result = Curl_pretransfer(data);
       result = Curl_pretransfer(data);
-
-      if(!result) {
-        /* after init, go CONNECT */
-        multistate(data, MSTATE_CONNECT);
-        *nowp = Curl_pgrsTime(data, TIMER_STARTOP);
-        rc = CURLM_CALL_MULTI_PERFORM;
-      }
-      break;
-
-    case MSTATE_CONNECT:
-      /* Connect. We want to get a connection identifier filled in. */
-      /* init this transfer. */
-      result = Curl_preconnect(data);
       if(result)
       if(result)
         break;
         break;
 
 
+      /* after init, go SETUP */
+      multistate(data, MSTATE_SETUP);
+      (void)Curl_pgrsTime(data, TIMER_STARTOP);
+      FALLTHROUGH();
+
+    case MSTATE_SETUP:
+      /* Transitional state. Setup things for a new transfer. The handle
+         can come back to this state on a redirect. */
       *nowp = Curl_pgrsTime(data, TIMER_STARTSINGLE);
       *nowp = Curl_pgrsTime(data, TIMER_STARTSINGLE);
       if(data->set.timeout)
       if(data->set.timeout)
         Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT);
         Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT);
-
       if(data->set.connecttimeout)
       if(data->set.connecttimeout)
+        /* Since a connection might go to pending and back to CONNECT several
+           times before it actually takes off, we need to set the timeout once
+           in SETUP before we enter CONNECT the first time. */
         Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT);
         Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT);
 
 
+      multistate(data, MSTATE_CONNECT);
+      FALLTHROUGH();
+
+    case MSTATE_CONNECT:
+      /* Connect. We want to get a connection identifier filled in. This state
+         can be entered from SETUP and from PENDING. */
       result = Curl_connect(data, &async, &connected);
       result = Curl_connect(data, &async, &connected);
       if(CURLE_NO_CONNECTION_AVAILABLE == result) {
       if(CURLE_NO_CONNECTION_AVAILABLE == result) {
         /* There was no connection available. We will go to the pending
         /* There was no connection available. We will go to the pending
@@ -1934,18 +1997,14 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         multistate(data, MSTATE_PENDING);
         multistate(data, MSTATE_PENDING);
 
 
         /* add this handle to the list of connect-pending handles */
         /* add this handle to the list of connect-pending handles */
-        Curl_llist_insert_next(&multi->pending, multi->pending.tail, data,
-                               &data->connect_queue);
+        Curl_llist_append(&multi->pending, data, &data->connect_queue);
         /* unlink from the main list */
         /* unlink from the main list */
         unlink_easy(multi, data);
         unlink_easy(multi, data);
         result = CURLE_OK;
         result = CURLE_OK;
         break;
         break;
       }
       }
-      else if(data->state.previouslypending) {
-        /* this transfer comes from the pending queue so try move another */
-        infof(data, "Transfer was pending, now try another");
+      else
         process_pending_handles(data->multi);
         process_pending_handles(data->multi);
-      }
 
 
       if(!result) {
       if(!result) {
         *nowp = Curl_pgrsTime(data, TIMER_POSTQUEUE);
         *nowp = Curl_pgrsTime(data, TIMER_POSTQUEUE);
@@ -2226,7 +2285,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
               follow = FOLLOW_RETRY;
               follow = FOLLOW_RETRY;
               drc = Curl_follow(data, newurl, follow);
               drc = Curl_follow(data, newurl, follow);
               if(!drc) {
               if(!drc) {
-                multistate(data, MSTATE_CONNECT);
+                multistate(data, MSTATE_SETUP);
                 rc = CURLM_CALL_MULTI_PERFORM;
                 rc = CURLM_CALL_MULTI_PERFORM;
                 result = CURLE_OK;
                 result = CURLE_OK;
               }
               }
@@ -2463,7 +2522,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         Curl_posttransfer(data);
         Curl_posttransfer(data);
         multi_done(data, result, TRUE);
         multi_done(data, result, TRUE);
       }
       }
-      else if(data->req.done) {
+      else if(data->req.done && !Curl_cwriter_is_paused(data)) {
 
 
         /* call this even if the readwrite function returned error */
         /* call this even if the readwrite function returned error */
         Curl_posttransfer(data);
         Curl_posttransfer(data);
@@ -2486,10 +2545,9 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
           /* multi_done() might return CURLE_GOT_NOTHING */
           /* multi_done() might return CURLE_GOT_NOTHING */
           result = Curl_follow(data, newurl, follow);
           result = Curl_follow(data, newurl, follow);
           if(!result) {
           if(!result) {
-            multistate(data, MSTATE_CONNECT);
+            multistate(data, MSTATE_SETUP);
             rc = CURLM_CALL_MULTI_PERFORM;
             rc = CURLM_CALL_MULTI_PERFORM;
           }
           }
-          free(newurl);
         }
         }
         else {
         else {
           /* after the transfer is done, go DONE */
           /* after the transfer is done, go DONE */
@@ -2501,7 +2559,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
             newurl = data->req.location;
             newurl = data->req.location;
             data->req.location = NULL;
             data->req.location = NULL;
             result = Curl_follow(data, newurl, FOLLOW_FAKE);
             result = Curl_follow(data, newurl, FOLLOW_FAKE);
-            free(newurl);
             if(result) {
             if(result) {
               stream_error = TRUE;
               stream_error = TRUE;
               result = multi_done(data, result, TRUE);
               result = multi_done(data, result, TRUE);
@@ -2520,6 +2577,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
            transfers */
            transfers */
         Curl_expire(data, 0, EXPIRE_RUN_NOW);
         Curl_expire(data, 0, EXPIRE_RUN_NOW);
       }
       }
+      free(newurl);
       break;
       break;
     }
     }
 
 
@@ -2570,8 +2628,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       return CURLM_INTERNAL_ERROR;
       return CURLM_INTERNAL_ERROR;
     }
     }
 
 
-    if(data->conn &&
-       data->mstate >= MSTATE_CONNECT &&
+    if(data->mstate >= MSTATE_CONNECT &&
        data->mstate < MSTATE_DO &&
        data->mstate < MSTATE_DO &&
        rc != CURLM_CALL_MULTI_PERFORM &&
        rc != CURLM_CALL_MULTI_PERFORM &&
        !multi_ischanged(multi, false)) {
        !multi_ischanged(multi, false)) {
@@ -2581,7 +2638,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
        * (i.e. CURLM_CALL_MULTI_PERFORM == TRUE) then we should do that before
        * (i.e. CURLM_CALL_MULTI_PERFORM == TRUE) then we should do that before
        * declaring the connection timed out as we may almost have a completed
        * declaring the connection timed out as we may almost have a completed
        * connection. */
        * connection. */
-      multi_handle_timeout(data, nowp, &stream_error, &result, TRUE);
+      multi_handle_timeout(data, nowp, &stream_error, &result, FALSE);
     }
     }
 
 
 statemachine_end:
 statemachine_end:
@@ -2658,8 +2715,7 @@ statemachine_end:
       multistate(data, MSTATE_MSGSENT);
       multistate(data, MSTATE_MSGSENT);
 
 
       /* add this handle to the list of msgsent handles */
       /* add this handle to the list of msgsent handles */
-      Curl_llist_insert_next(&multi->msgsent, multi->msgsent.tail, data,
-                             &data->connect_queue);
+      Curl_llist_append(&multi->msgsent, data, &data->connect_queue);
       /* unlink from the main list */
       /* unlink from the main list */
       unlink_easy(multi, data);
       unlink_easy(multi, data);
       return CURLM_OK;
       return CURLM_OK;
@@ -2721,10 +2777,20 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
    */
    */
   do {
   do {
     multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
     multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
-    if(t)
+    if(t) {
       /* the removed may have another timeout in queue */
       /* the removed may have another timeout in queue */
+      data = t->payload;
+      if(data->mstate == MSTATE_PENDING) {
+        bool stream_unused;
+        CURLcode result_unused;
+        if(multi_handle_timeout(data, &now, &stream_unused, &result_unused,
+                                FALSE)) {
+          infof(data, "PENDING handle timeout");
+          move_pending_to_connect(multi, data);
+        }
+      }
       (void)add_next_timeout(now, multi, t->payload);
       (void)add_next_timeout(now, multi, t->payload);
-
+    }
   } while(t);
   } while(t);
 
 
   *running_handles = multi->num_alive;
   *running_handles = multi->num_alive;
@@ -3678,47 +3744,55 @@ void Curl_multiuse_state(struct Curl_easy *data,
   process_pending_handles(data->multi);
   process_pending_handles(data->multi);
 }
 }
 
 
-/* process_pending_handles() moves all handles from PENDING
-   back into the main list and change state to CONNECT */
-static void process_pending_handles(struct Curl_multi *multi)
+static void move_pending_to_connect(struct Curl_multi *multi,
+                                    struct Curl_easy *data)
 {
 {
-  struct Curl_llist_element *e = multi->pending.head;
-  if(e) {
-    struct Curl_easy *data = e->ptr;
+  DEBUGASSERT(data->mstate == MSTATE_PENDING);
+
+  /* put it back into the main list */
+  link_easy(multi, data);
 
 
-    DEBUGASSERT(data->mstate == MSTATE_PENDING);
+  multistate(data, MSTATE_CONNECT);
 
 
-    /* put it back into the main list */
-    link_easy(multi, data);
+  /* Remove this node from the pending list */
+  Curl_llist_remove(&multi->pending, &data->connect_queue, NULL);
 
 
-    multistate(data, MSTATE_CONNECT);
+  /* Make sure that the handle will be processed soonish. */
+  Curl_expire(data, 0, EXPIRE_RUN_NOW);
+}
 
 
-    /* Remove this node from the list */
-    Curl_llist_remove(&multi->pending, e, NULL);
+/* process_pending_handles() moves a handle from PENDING back into the main
+   list and change state to CONNECT.
 
 
-    /* Make sure that the handle will be processed soonish. */
-    Curl_expire(data, 0, EXPIRE_RUN_NOW);
+   We do not move all transfers because that can be a significant amount.
+   Since this is tried every now and then doing too many too often becomes a
+   performance problem.
 
 
-    /* mark this as having been in the pending queue */
-    data->state.previouslypending = TRUE;
+   When there is a change for connection limits like max host connections etc,
+   this likely only allows one new transfer. When there is a pipewait change,
+   it can potentially allow hundreds of new transfers.
+
+   We could consider an improvement where we store the queue reason and allow
+   more pipewait rechecks than others.
+*/
+static void process_pending_handles(struct Curl_multi *multi)
+{
+  struct Curl_llist_element *e = multi->pending.head;
+  if(e) {
+    struct Curl_easy *data = e->ptr;
+    move_pending_to_connect(multi, data);
   }
   }
 }
 }
 
 
 void Curl_set_in_callback(struct Curl_easy *data, bool value)
 void Curl_set_in_callback(struct Curl_easy *data, bool value)
 {
 {
-  /* might get called when there is no data pointer! */
-  if(data) {
-    if(data->multi_easy)
-      data->multi_easy->in_callback = value;
-    else if(data->multi)
-      data->multi->in_callback = value;
-  }
+  if(data && data->multi)
+    data->multi->in_callback = value;
 }
 }
 
 
-bool Curl_is_in_callback(struct Curl_easy *easy)
+bool Curl_is_in_callback(struct Curl_easy *data)
 {
 {
-  return ((easy->multi && easy->multi->in_callback) ||
-          (easy->multi_easy && easy->multi_easy->in_callback));
+  return (data && data->multi && data->multi->in_callback);
 }
 }
 
 
 unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi)
 unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi)

+ 19 - 18
lib/multihandle.h

@@ -44,24 +44,25 @@ struct Curl_message {
 typedef enum {
 typedef enum {
   MSTATE_INIT,         /* 0 - start in this state */
   MSTATE_INIT,         /* 0 - start in this state */
   MSTATE_PENDING,      /* 1 - no connections, waiting for one */
   MSTATE_PENDING,      /* 1 - no connections, waiting for one */
-  MSTATE_CONNECT,      /* 2 - resolve/connect has been sent off */
-  MSTATE_RESOLVING,    /* 3 - awaiting the resolve to finalize */
-  MSTATE_CONNECTING,   /* 4 - awaiting the TCP connect to finalize */
-  MSTATE_TUNNELING,    /* 5 - awaiting HTTPS proxy SSL initialization to
+  MSTATE_SETUP,        /* 2 - start a new transfer */
+  MSTATE_CONNECT,      /* 3 - resolve/connect has been sent off */
+  MSTATE_RESOLVING,    /* 4 - awaiting the resolve to finalize */
+  MSTATE_CONNECTING,   /* 5 - awaiting the TCP connect to finalize */
+  MSTATE_TUNNELING,    /* 6 - awaiting HTTPS proxy SSL initialization to
                           complete and/or proxy CONNECT to finalize */
                           complete and/or proxy CONNECT to finalize */
-  MSTATE_PROTOCONNECT, /* 6 - initiate protocol connect procedure */
-  MSTATE_PROTOCONNECTING, /* 7 - completing the protocol-specific connect
+  MSTATE_PROTOCONNECT, /* 7 - initiate protocol connect procedure */
+  MSTATE_PROTOCONNECTING, /* 8 - completing the protocol-specific connect
                              phase */
                              phase */
-  MSTATE_DO,           /* 8 - start send off the request (part 1) */
-  MSTATE_DOING,        /* 9 - sending off the request (part 1) */
-  MSTATE_DOING_MORE,   /* 10 - send off the request (part 2) */
-  MSTATE_DID,          /* 11 - done sending off request */
-  MSTATE_PERFORMING,   /* 12 - transfer data */
-  MSTATE_RATELIMITING, /* 13 - wait because limit-rate exceeded */
-  MSTATE_DONE,         /* 14 - post data transfer operation */
-  MSTATE_COMPLETED,    /* 15 - operation complete */
-  MSTATE_MSGSENT,      /* 16 - the operation complete message is sent */
-  MSTATE_LAST          /* 17 - not a true state, never use this */
+  MSTATE_DO,           /* 9 - start send off the request (part 1) */
+  MSTATE_DOING,        /* 10 - sending off the request (part 1) */
+  MSTATE_DOING_MORE,   /* 11 - send off the request (part 2) */
+  MSTATE_DID,          /* 12 - done sending off request */
+  MSTATE_PERFORMING,   /* 13 - transfer data */
+  MSTATE_RATELIMITING, /* 14 - wait because limit-rate exceeded */
+  MSTATE_DONE,         /* 15 - post data transfer operation */
+  MSTATE_COMPLETED,    /* 16 - operation complete */
+  MSTATE_MSGSENT,      /* 17 - the operation complete message is sent */
+  MSTATE_LAST          /* 18 - not a true state, never use this */
 } CURLMstate;
 } CURLMstate;
 
 
 /* we support N sockets per easy handle. Set the corresponding bit to what
 /* we support N sockets per easy handle. Set the corresponding bit to what
@@ -158,7 +159,7 @@ struct Curl_multi {
   WSAEVENT wsa_event; /* winsock event used for waits */
   WSAEVENT wsa_event; /* winsock event used for waits */
 #else
 #else
 #ifdef ENABLE_WAKEUP
 #ifdef ENABLE_WAKEUP
-  curl_socket_t wakeup_pair[2]; /* socketpair() used for wakeup
+  curl_socket_t wakeup_pair[2]; /* pipe()/socketpair() used for wakeup
                                    0 is used for read, 1 is used for write */
                                    0 is used for read, 1 is used for write */
 #endif
 #endif
 #endif
 #endif
@@ -179,7 +180,7 @@ struct Curl_multi {
   BIT(dead); /* a callback returned error, everything needs to crash and
   BIT(dead); /* a callback returned error, everything needs to crash and
                 burn */
                 burn */
   BIT(xfer_buf_borrowed);      /* xfer_buf is currently being borrowed */
   BIT(xfer_buf_borrowed);      /* xfer_buf is currently being borrowed */
-  BIT(xfer_ulbuf_borrowed);      /* xfer_buf is currently being borrowed */
+  BIT(xfer_ulbuf_borrowed);    /* xfer_ulbuf is currently being borrowed */
 #ifdef DEBUGBUILD
 #ifdef DEBUGBUILD
   BIT(warned);                 /* true after user warned of DEBUGBUILD */
   BIT(warned);                 /* true after user warned of DEBUGBUILD */
 #endif
 #endif

+ 4 - 3
lib/multiif.h

@@ -38,15 +38,16 @@ void Curl_attach_connection(struct Curl_easy *data,
 void Curl_detach_connection(struct Curl_easy *data);
 void Curl_detach_connection(struct Curl_easy *data);
 bool Curl_multiplex_wanted(const struct Curl_multi *multi);
 bool Curl_multiplex_wanted(const struct Curl_multi *multi);
 void Curl_set_in_callback(struct Curl_easy *data, bool value);
 void Curl_set_in_callback(struct Curl_easy *data, bool value);
-bool Curl_is_in_callback(struct Curl_easy *easy);
+bool Curl_is_in_callback(struct Curl_easy *data);
 CURLcode Curl_preconnect(struct Curl_easy *data);
 CURLcode Curl_preconnect(struct Curl_easy *data);
 
 
 void Curl_multi_connchanged(struct Curl_multi *multi);
 void Curl_multi_connchanged(struct Curl_multi *multi);
 
 
 /* Internal version of curl_multi_init() accepts size parameters for the
 /* Internal version of curl_multi_init() accepts size parameters for the
    socket, connection and dns hashes */
    socket, connection and dns hashes */
-struct Curl_multi *Curl_multi_handle(int hashsize, int chashsize,
-                                     int dnssize);
+struct Curl_multi *Curl_multi_handle(size_t hashsize,
+                                     size_t chashsize,
+                                     size_t dnssize);
 
 
 /* the write bits start at bit 16 for the *getsock() bitmap */
 /* the write bits start at bit 16 for the *getsock() bitmap */
 #define GETSOCK_WRITEBITSTART 16
 #define GETSOCK_WRITEBITSTART 16

+ 1 - 1
lib/noproxy.c

@@ -78,7 +78,7 @@ UNITTEST bool Curl_cidr6_match(const char *ipv6,
                                const char *network,
                                const char *network,
                                unsigned int bits)
                                unsigned int bits)
 {
 {
-#ifdef ENABLE_IPV6
+#ifdef USE_IPV6
   int bytes;
   int bytes;
   int rest;
   int rest;
   unsigned char address[16];
   unsigned char address[16];

+ 10 - 5
lib/openldap.c

@@ -117,7 +117,7 @@ static Curl_recv oldap_recv;
  */
  */
 
 
 const struct Curl_handler Curl_handler_ldap = {
 const struct Curl_handler Curl_handler_ldap = {
-  "LDAP",                               /* scheme */
+  "ldap",                               /* scheme */
   oldap_setup_connection,               /* setup_connection */
   oldap_setup_connection,               /* setup_connection */
   oldap_do,                             /* do_it */
   oldap_do,                             /* do_it */
   oldap_done,                           /* done */
   oldap_done,                           /* done */
@@ -131,6 +131,7 @@ const struct Curl_handler Curl_handler_ldap = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* perform_getsock */
   oldap_disconnect,                     /* disconnect */
   oldap_disconnect,                     /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* attach connection */
   PORT_LDAP,                            /* defport */
   PORT_LDAP,                            /* defport */
@@ -145,7 +146,7 @@ const struct Curl_handler Curl_handler_ldap = {
  */
  */
 
 
 const struct Curl_handler Curl_handler_ldaps = {
 const struct Curl_handler Curl_handler_ldaps = {
-  "LDAPS",                              /* scheme */
+  "ldaps",                              /* scheme */
   oldap_setup_connection,               /* setup_connection */
   oldap_setup_connection,               /* setup_connection */
   oldap_do,                             /* do_it */
   oldap_do,                             /* do_it */
   oldap_done,                           /* done */
   oldap_done,                           /* done */
@@ -159,6 +160,7 @@ const struct Curl_handler Curl_handler_ldaps = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* perform_getsock */
   oldap_disconnect,                     /* disconnect */
   oldap_disconnect,                     /* disconnect */
   ZERO_NULL,                            /* write_resp */
   ZERO_NULL,                            /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* connection_check */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* attach connection */
   PORT_LDAPS,                           /* defport */
   PORT_LDAPS,                           /* defport */
@@ -548,9 +550,12 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
       return result;
       return result;
   }
   }
 
 
-  hosturl = aprintf("ldap%s://%s:%d",
-                    conn->handler->flags & PROTOPT_SSL? "s": "",
-                    conn->host.name, conn->remote_port);
+  hosturl = aprintf("%s://%s%s%s:%d",
+                    conn->handler->scheme,
+                    conn->bits.ipv6_ip? "[": "",
+                    conn->host.name,
+                    conn->bits.ipv6_ip? "]": "",
+                    conn->remote_port);
   if(!hosturl)
   if(!hosturl)
     return CURLE_OUT_OF_MEMORY;
     return CURLE_OUT_OF_MEMORY;
 
 

+ 5 - 3
lib/pop3.c

@@ -112,7 +112,7 @@ static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out);
  */
  */
 
 
 const struct Curl_handler Curl_handler_pop3 = {
 const struct Curl_handler Curl_handler_pop3 = {
-  "POP3",                           /* scheme */
+  "pop3",                           /* scheme */
   pop3_setup_connection,            /* setup_connection */
   pop3_setup_connection,            /* setup_connection */
   pop3_do,                          /* do_it */
   pop3_do,                          /* do_it */
   pop3_done,                        /* done */
   pop3_done,                        /* done */
@@ -126,6 +126,7 @@ const struct Curl_handler Curl_handler_pop3 = {
   ZERO_NULL,                        /* perform_getsock */
   ZERO_NULL,                        /* perform_getsock */
   pop3_disconnect,                  /* disconnect */
   pop3_disconnect,                  /* disconnect */
   ZERO_NULL,                        /* write_resp */
   ZERO_NULL,                        /* write_resp */
+  ZERO_NULL,                        /* write_resp_hd */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* attach connection */
   ZERO_NULL,                        /* attach connection */
   PORT_POP3,                        /* defport */
   PORT_POP3,                        /* defport */
@@ -141,7 +142,7 @@ const struct Curl_handler Curl_handler_pop3 = {
  */
  */
 
 
 const struct Curl_handler Curl_handler_pop3s = {
 const struct Curl_handler Curl_handler_pop3s = {
-  "POP3S",                          /* scheme */
+  "pop3s",                          /* scheme */
   pop3_setup_connection,            /* setup_connection */
   pop3_setup_connection,            /* setup_connection */
   pop3_do,                          /* do_it */
   pop3_do,                          /* do_it */
   pop3_done,                        /* done */
   pop3_done,                        /* done */
@@ -155,6 +156,7 @@ const struct Curl_handler Curl_handler_pop3s = {
   ZERO_NULL,                        /* perform_getsock */
   ZERO_NULL,                        /* perform_getsock */
   pop3_disconnect,                  /* disconnect */
   pop3_disconnect,                  /* disconnect */
   ZERO_NULL,                        /* write_resp */
   ZERO_NULL,                        /* write_resp */
+  ZERO_NULL,                        /* write_resp_hd */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* attach connection */
   ZERO_NULL,                        /* attach connection */
   PORT_POP3S,                       /* defport */
   PORT_POP3S,                       /* defport */
@@ -1450,7 +1452,7 @@ static CURLcode pop3_parse_custom_request(struct Curl_easy *data)
  * This function scans the body after the end-of-body and writes everything
  * This function scans the body after the end-of-body and writes everything
  * until the end is found.
  * until the end is found.
  */
  */
-CURLcode Curl_pop3_write(struct Curl_easy *data, char *str, size_t nread)
+CURLcode Curl_pop3_write(struct Curl_easy *data, const char *str, size_t nread)
 {
 {
   /* This code could be made into a special function in the handler struct */
   /* This code could be made into a special function in the handler struct */
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;

+ 2 - 1
lib/pop3.h

@@ -92,6 +92,7 @@ extern const struct Curl_handler Curl_handler_pop3s;
 
 
 /* This function scans the body after the end-of-body and writes everything
 /* This function scans the body after the end-of-body and writes everything
  * until the end is found */
  * until the end is found */
-CURLcode Curl_pop3_write(struct Curl_easy *data, char *str, size_t nread);
+CURLcode Curl_pop3_write(struct Curl_easy *data,
+                         const char *str, size_t nread);
 
 
 #endif /* HEADER_CURL_POP3_H */
 #endif /* HEADER_CURL_POP3_H */

+ 3 - 3
lib/request.c

@@ -40,10 +40,9 @@
 #include "curl_memory.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 #include "memdebug.h"
 
 
-CURLcode Curl_req_init(struct SingleRequest *req)
+void Curl_req_init(struct SingleRequest *req)
 {
 {
   memset(req, 0, sizeof(*req));
   memset(req, 0, sizeof(*req));
-  return CURLE_OK;
 }
 }
 
 
 CURLcode Curl_req_soft_reset(struct SingleRequest *req,
 CURLcode Curl_req_soft_reset(struct SingleRequest *req,
@@ -266,7 +265,7 @@ static CURLcode req_set_upload_done(struct Curl_easy *data)
   else if(data->req.writebytecount)
   else if(data->req.writebytecount)
     infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T
     infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T
           " bytes", data->req.writebytecount);
           " bytes", data->req.writebytecount);
-  else
+  else if(!data->req.download_done)
     infof(data, Curl_creader_total_length(data)?
     infof(data, Curl_creader_total_length(data)?
                 "We are completely uploaded and fine" :
                 "We are completely uploaded and fine" :
                 "Request completely sent off");
                 "Request completely sent off");
@@ -395,6 +394,7 @@ CURLcode Curl_req_send_more(struct Curl_easy *data)
   result = req_flush(data);
   result = req_flush(data);
   if(result == CURLE_AGAIN)
   if(result == CURLE_AGAIN)
     result = CURLE_OK;
     result = CURLE_OK;
+
   return result;
   return result;
 }
 }
 
 

+ 1 - 1
lib/request.h

@@ -152,7 +152,7 @@ struct SingleRequest {
 /**
 /**
  * Initialize the state of the request for first use.
  * Initialize the state of the request for first use.
  */
  */
-CURLcode Curl_req_init(struct SingleRequest *req);
+void Curl_req_init(struct SingleRequest *req);
 
 
 /**
 /**
  * The request is about to start. Record time and do a soft reset.
  * The request is about to start. Record time and do a soft reset.

+ 12 - 11
lib/rtsp.c

@@ -93,14 +93,14 @@ static int rtsp_getsock_do(struct Curl_easy *data, struct connectdata *conn,
 static
 static
 CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len);
 CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len);
 static
 static
-CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport);
+CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport);
 
 
 
 
 /*
 /*
  * RTSP handler interface.
  * RTSP handler interface.
  */
  */
 const struct Curl_handler Curl_handler_rtsp = {
 const struct Curl_handler Curl_handler_rtsp = {
-  "RTSP",                               /* scheme */
+  "rtsp",                               /* scheme */
   rtsp_setup_connection,                /* setup_connection */
   rtsp_setup_connection,                /* setup_connection */
   rtsp_do,                              /* do_it */
   rtsp_do,                              /* do_it */
   rtsp_done,                            /* done */
   rtsp_done,                            /* done */
@@ -114,6 +114,7 @@ const struct Curl_handler Curl_handler_rtsp = {
   ZERO_NULL,                            /* perform_getsock */
   ZERO_NULL,                            /* perform_getsock */
   rtsp_disconnect,                      /* disconnect */
   rtsp_disconnect,                      /* disconnect */
   rtsp_rtp_write_resp,                  /* write_resp */
   rtsp_rtp_write_resp,                  /* write_resp */
+  ZERO_NULL,                            /* write_resp_hd */
   rtsp_conncheck,                       /* connection_check */
   rtsp_conncheck,                       /* connection_check */
   ZERO_NULL,                            /* attach connection */
   ZERO_NULL,                            /* attach connection */
   PORT_RTSP,                            /* defport */
   PORT_RTSP,                            /* defport */
@@ -393,7 +394,9 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
   if(result)
   if(result)
     goto out;
     goto out;
 
 
+#ifndef CURL_DISABLE_PROXY
   p_proxyuserpwd = data->state.aptr.proxyuserpwd;
   p_proxyuserpwd = data->state.aptr.proxyuserpwd;
+#endif
   p_userpwd = data->state.aptr.userpwd;
   p_userpwd = data->state.aptr.userpwd;
 
 
   /* Referrer */
   /* Referrer */
@@ -802,7 +805,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
   DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, eos=%d)",
   DEBUGF(infof(data, "rtsp_rtp_write_resp(len=%zu, in_header=%d, eos=%d)",
                blen, rtspc->in_header, is_eos));
                blen, rtspc->in_header, is_eos));
 
 
-  /* If header parsing is not onging, extract RTP messages */
+  /* If header parsing is not ongoing, extract RTP messages */
   if(!rtspc->in_header) {
   if(!rtspc->in_header) {
     result = rtsp_filter_rtp(data, buf, blen, &consumed);
     result = rtsp_filter_rtp(data, buf, blen, &consumed);
     if(result)
     if(result)
@@ -911,12 +914,12 @@ CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len)
   return CURLE_OK;
   return CURLE_OK;
 }
 }
 
 
-CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header)
+CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header)
 {
 {
   if(checkprefix("CSeq:", header)) {
   if(checkprefix("CSeq:", header)) {
     long CSeq = 0;
     long CSeq = 0;
     char *endp;
     char *endp;
-    char *p = &header[5];
+    const char *p = &header[5];
     while(ISBLANK(*p))
     while(ISBLANK(*p))
       p++;
       p++;
     CSeq = strtol(p, &endp, 10);
     CSeq = strtol(p, &endp, 10);
@@ -931,8 +934,7 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header)
     }
     }
   }
   }
   else if(checkprefix("Session:", header)) {
   else if(checkprefix("Session:", header)) {
-    char *start;
-    char *end;
+    const char *start, *end;
     size_t idlen;
     size_t idlen;
 
 
     /* Find the first non-space letter */
     /* Find the first non-space letter */
@@ -987,14 +989,13 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header)
 }
 }
 
 
 static
 static
-CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport)
+CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport)
 {
 {
   /* If we receive multiple Transport response-headers, the linterleaved
   /* If we receive multiple Transport response-headers, the linterleaved
      channels of each response header is recorded and used together for
      channels of each response header is recorded and used together for
      subsequent data validity checks.*/
      subsequent data validity checks.*/
   /* e.g.: ' RTP/AVP/TCP;unicast;interleaved=5-6' */
   /* e.g.: ' RTP/AVP/TCP;unicast;interleaved=5-6' */
-  char *start;
-  char *end;
+  const char *start, *end;
   start = transport;
   start = transport;
   while(start && *start) {
   while(start && *start) {
     while(*start && ISBLANK(*start) )
     while(*start && ISBLANK(*start) )
@@ -1003,7 +1004,7 @@ CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport)
     if(checkprefix("interleaved=", start)) {
     if(checkprefix("interleaved=", start)) {
       long chan1, chan2, chan;
       long chan1, chan2, chan;
       char *endp;
       char *endp;
-      char *p = start + 12;
+      const char *p = start + 12;
       chan1 = strtol(p, &endp, 10);
       chan1 = strtol(p, &endp, 10);
       if(p != endp && chan1 >= 0 && chan1 <= 255) {
       if(p != endp && chan1 >= 0 && chan1 <= 255) {
         unsigned char *rtp_channel_mask = data->state.rtp_channel_mask;
         unsigned char *rtp_channel_mask = data->state.rtp_channel_mask;

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