Sfoglia il codice sorgente

curl 2023-12-06 (7161cb17)

Code extracted from:

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

at commit 7161cb17c01dcff1dc5bf89a18437d9d729f1ecd (curl-8_5_0).
Curl Upstream 1 anno fa
parent
commit
fe5ffe06a9
100 ha cambiato i file con 3038 aggiunte e 2530 eliminazioni
  1. 88 158
      CMake/CurlTests.c
  2. 7 0
      CMake/FindZstd.cmake
  3. 0 13
      CMake/Macros.cmake
  4. 7 7
      CMake/OtherTests.cmake
  5. 28 9
      CMake/PickyWarnings.cmake
  6. 154 100
      CMake/Platforms/WindowsCache.cmake
  7. 3 1
      CMake/curl-config.cmake.in
  8. 346 313
      CMakeLists.txt
  9. 9 17
      include/curl/curl.h
  10. 3 3
      include/curl/curlver.h
  11. 11 26
      include/curl/system.h
  12. 13 8
      lib/CMakeLists.txt
  13. 4 6
      lib/altsvc.c
  14. 8 1
      lib/arpa_telnet.h
  15. 89 60
      lib/asyn-ares.c
  16. 25 25
      lib/asyn-thread.c
  17. 1 0
      lib/base64.c
  18. 38 68
      lib/c-hyper.c
  19. 25 30
      lib/cf-h1-proxy.c
  20. 27 40
      lib/cf-h2-proxy.c
  21. 7 13
      lib/cf-haproxy.c
  22. 17 37
      lib/cf-https-connect.c
  23. 39 54
      lib/cf-socket.c
  24. 179 25
      lib/cfilters.c
  25. 95 18
      lib/cfilters.h
  26. 15 10
      lib/conncache.c
  27. 50 43
      lib/connect.c
  28. 134 147
      lib/content_encoding.c
  29. 2 7
      lib/content_encoding.h
  30. 19 13
      lib/cookie.c
  31. 12 15
      lib/curl_config.h.cmake
  32. 2 1
      lib/curl_hmac.h
  33. 3 3
      lib/curl_memory.h
  34. 2 2
      lib/curl_multibyte.c
  35. 4 4
      lib/curl_multibyte.h
  36. 9 0
      lib/curl_ntlm_core.c
  37. 8 6
      lib/curl_ntlm_wb.c
  38. 1 1
      lib/curl_path.h
  39. 1 1
      lib/curl_rtmp.c
  40. 3 0
      lib/curl_sasl.c
  41. 48 93
      lib/curl_setup.h
  42. 2 6
      lib/curl_setup_once.h
  43. 16 0
      lib/curl_sspi.h
  44. 6 20
      lib/curl_trc.c
  45. 52 56
      lib/curl_trc.h
  46. 26 17
      lib/doh.c
  47. 5 1
      lib/dynbuf.c
  48. 29 0
      lib/dynhds.c
  49. 9 0
      lib/dynhds.h
  50. 25 59
      lib/easy.c
  51. 9 0
      lib/easy_lock.h
  52. 1 7
      lib/file.c
  53. 59 18
      lib/fopen.c
  54. 4 4
      lib/formdata.c
  55. 3 3
      lib/ftp.c
  56. 0 3
      lib/ftplistparser.c
  57. 1 1
      lib/functypes.h
  58. 3 2
      lib/getenv.c
  59. 6 5
      lib/hostasyn.c
  60. 101 14
      lib/hostip.c
  61. 4 0
      lib/hostip.h
  62. 3 4
      lib/hostip6.c
  63. 18 10
      lib/hsts.c
  64. 107 129
      lib/http.c
  65. 3 3
      lib/http.h
  66. 55 79
      lib/http2.c
  67. 10 1
      lib/http_aws_sigv4.c
  68. 41 42
      lib/http_chunks.c
  69. 2 2
      lib/http_chunks.h
  70. 1 1
      lib/http_proxy.c
  71. 1 1
      lib/idn.c
  72. 1 3
      lib/imap.c
  73. 3 17
      lib/ldap.c
  74. 2 3
      lib/md4.c
  75. 1 1
      lib/memdebug.c
  76. 2 2
      lib/memdebug.h
  77. 2 3
      lib/mime.c
  78. 1 6
      lib/mprintf.c
  79. 0 9
      lib/mqtt.c
  80. 121 174
      lib/multi.c
  81. 7 8
      lib/multihandle.h
  82. 1 1
      lib/netrc.c
  83. 27 27
      lib/openldap.c
  84. 1 1
      lib/pop3.c
  85. 1 7
      lib/progress.c
  86. 14 14
      lib/rand.c
  87. 1 1
      lib/rand.h
  88. 1 1
      lib/rename.c
  89. 230 129
      lib/rtsp.c
  90. 9 0
      lib/rtsp.h
  91. 1 1
      lib/select.c
  92. 255 71
      lib/sendf.c
  93. 103 24
      lib/sendf.h
  94. 91 129
      lib/setopt.c
  95. 2 11
      lib/setup-win32.h
  96. 1 9
      lib/share.h
  97. 1 8
      lib/smb.c
  98. 1 1
      lib/smtp.c
  99. 3 3
      lib/socketpair.c
  100. 17 0
      lib/socketpair.h

+ 88 - 158
CMake/CurlTests.c

@@ -23,7 +23,6 @@
  ***************************************************************************/
 
 #ifdef HAVE_FCNTL_O_NONBLOCK
-
 /* headers for FCNTL_O_NONBLOCK test */
 #include <sys/types.h>
 #include <unistd.h>
@@ -45,14 +44,13 @@
 #error "O_NONBLOCK does not work on this platform"
 #endif
 
-int
-main ()
+int main(void)
 {
-      /* O_NONBLOCK source test */
-      int flags = 0;
-      if(0 != fcntl(0, F_SETFL, flags | O_NONBLOCK))
-          return 1;
-      return 0;
+  /* O_NONBLOCK source test */
+  int flags = 0;
+  if(0 != fcntl(0, F_SETFL, flags | O_NONBLOCK))
+    return 1;
+  return 0;
 }
 #endif
 
@@ -108,36 +106,16 @@ int main(void)
 }
 #endif
 
-#ifdef HAVE_SOCKLEN_T
-#ifdef _WIN32
-#include <ws2tcpip.h>
-#else
-#include <sys/types.h>
-#include <sys/socket.h>
-#endif
-int
-main ()
-{
-if ((socklen_t *) 0)
-  return 0;
-if (sizeof (socklen_t))
-  return 0;
-  ;
-  return 0;
-}
-#endif
 #ifdef HAVE_IN_ADDR_T
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <arpa/inet.h>
-
-int
-main ()
+int main(void)
 {
-if ((in_addr_t *) 0)
-  return 0;
-if (sizeof (in_addr_t))
-  return 0;
+  if((in_addr_t *) 0)
+    return 0;
+  if(sizeof(in_addr_t))
+    return 0;
   ;
   return 0;
 }
@@ -150,11 +128,10 @@ if (sizeof (in_addr_t))
 #ifdef HAVE_STDBOOL_H
 #include <stdbool.h>
 #endif
-int
-main ()
+int main(void)
 {
-if (sizeof (bool *) )
-  return 0;
+  if(sizeof(bool *))
+    return 0;
   ;
   return 0;
 }
@@ -165,8 +142,9 @@ if (sizeof (bool *) )
 #include <stdarg.h>
 #include <string.h>
 #include <float.h>
-int main() { return 0; }
+int main(void) { return 0; }
 #endif
+
 #ifdef HAVE_FILE_OFFSET_BITS
 #ifdef _FILE_OFFSET_BITS
 #undef _FILE_OFFSET_BITS
@@ -181,104 +159,95 @@ int main() { return 0; }
   int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
                        && LARGE_OFF_T % 2147483647 == 1)
                       ? 1 : -1];
-int main () { ; return 0; }
+int main(void) { ; return 0; }
 #endif
+
 #ifdef HAVE_IOCTLSOCKET
 /* includes start */
 #ifdef HAVE_WINDOWS_H
 #  ifndef WIN32_LEAN_AND_MEAN
 #    define WIN32_LEAN_AND_MEAN
 #  endif
-#  include <windows.h>
 #  ifdef HAVE_WINSOCK2_H
 #    include <winsock2.h>
 #  endif
+#  include <windows.h>
 #endif
-
-int
-main ()
+int main(void)
 {
-
-/* ioctlsocket source code */
- int socket;
- unsigned long flags = ioctlsocket(socket, FIONBIO, &flags);
-
+  /* ioctlsocket source code */
+  int socket;
+  unsigned long flags = ioctlsocket(socket, FIONBIO, &flags);
   ;
   return 0;
 }
 
 #endif
+
 #ifdef HAVE_IOCTLSOCKET_CAMEL
 /* includes start */
 #ifdef HAVE_WINDOWS_H
 #  ifndef WIN32_LEAN_AND_MEAN
 #    define WIN32_LEAN_AND_MEAN
 #  endif
-#  include <windows.h>
 #  ifdef HAVE_WINSOCK2_H
 #    include <winsock2.h>
 #  endif
+#  include <windows.h>
 #endif
-
-int
-main ()
+int main(void)
 {
-
-/* IoctlSocket source code */
-    if(0 != IoctlSocket(0, 0, 0))
-      return 1;
+  /* IoctlSocket source code */
+  if(0 != IoctlSocket(0, 0, 0))
+    return 1;
   ;
   return 0;
 }
 #endif
+
 #ifdef HAVE_IOCTLSOCKET_CAMEL_FIONBIO
 /* includes start */
 #ifdef HAVE_WINDOWS_H
 #  ifndef WIN32_LEAN_AND_MEAN
 #    define WIN32_LEAN_AND_MEAN
 #  endif
-#  include <windows.h>
 #  ifdef HAVE_WINSOCK2_H
 #    include <winsock2.h>
 #  endif
+#  include <windows.h>
 #endif
-
-int
-main ()
+int main(void)
 {
-
-/* IoctlSocket source code */
-        long flags = 0;
-        if(0 != IoctlSocket(0, FIONBIO, &flags))
-          return 1;
+  /* IoctlSocket source code */
+  long flags = 0;
+  if(0 != IoctlSocket(0, FIONBIO, &flags))
+    return 1;
   ;
   return 0;
 }
 #endif
+
 #ifdef HAVE_IOCTLSOCKET_FIONBIO
 /* includes start */
 #ifdef HAVE_WINDOWS_H
 #  ifndef WIN32_LEAN_AND_MEAN
 #    define WIN32_LEAN_AND_MEAN
 #  endif
-#  include <windows.h>
 #  ifdef HAVE_WINSOCK2_H
 #    include <winsock2.h>
 #  endif
+#  include <windows.h>
 #endif
-
-int
-main ()
+int main(void)
 {
-
-        int flags = 0;
-        if(0 != ioctlsocket(0, FIONBIO, &flags))
-          return 1;
-
+  int flags = 0;
+  if(0 != ioctlsocket(0, FIONBIO, &flags))
+    return 1;
   ;
   return 0;
 }
 #endif
+
 #ifdef HAVE_IOCTL_FIONBIO
 /* headers for FIONBIO test */
 /* includes start */
@@ -297,19 +266,16 @@ main ()
 #ifdef HAVE_STROPTS_H
 #  include <stropts.h>
 #endif
-
-int
-main ()
+int main(void)
 {
-
-        int flags = 0;
-        if(0 != ioctl(0, FIONBIO, &flags))
-          return 1;
-
+  int flags = 0;
+  if(0 != ioctl(0, FIONBIO, &flags))
+    return 1;
   ;
   return 0;
 }
 #endif
+
 #ifdef HAVE_IOCTL_SIOCGIFADDR
 /* headers for FIONBIO test */
 /* includes start */
@@ -329,28 +295,26 @@ main ()
 #  include <stropts.h>
 #endif
 #include <net/if.h>
-
-int
-main ()
+int main(void)
 {
-        struct ifreq ifr;
-        if(0 != ioctl(0, SIOCGIFADDR, &ifr))
-          return 1;
-
+  struct ifreq ifr;
+  if(0 != ioctl(0, SIOCGIFADDR, &ifr))
+    return 1;
   ;
   return 0;
 }
 #endif
+
 #ifdef HAVE_SETSOCKOPT_SO_NONBLOCK
 /* includes start */
 #ifdef HAVE_WINDOWS_H
 #  ifndef WIN32_LEAN_AND_MEAN
 #    define WIN32_LEAN_AND_MEAN
 #  endif
-#  include <windows.h>
 #  ifdef HAVE_WINSOCK2_H
 #    include <winsock2.h>
 #  endif
+#  include <windows.h>
 #endif
 /* includes start */
 #ifdef HAVE_SYS_TYPES_H
@@ -360,30 +324,30 @@ main ()
 #  include <sys/socket.h>
 #endif
 /* includes end */
-
-int
-main ()
+int main(void)
 {
-        if(0 != setsockopt(0, SOL_SOCKET, SO_NONBLOCK, 0, 0))
-          return 1;
+  if(0 != setsockopt(0, SOL_SOCKET, SO_NONBLOCK, 0, 0))
+    return 1;
   ;
   return 0;
 }
 #endif
+
 #ifdef HAVE_GLIBC_STRERROR_R
 #include <string.h>
 #include <errno.h>
 
 void check(char c) {}
 
-int
-main () {
+int main(void)
+{
   char buffer[1024];
   /* This will not compile if strerror_r does not return a char* */
   check(strerror_r(EACCES, buffer, sizeof(buffer))[0]);
   return 0;
 }
 #endif
+
 #ifdef HAVE_POSIX_STRERROR_R
 #include <string.h>
 #include <errno.h>
@@ -391,92 +355,51 @@ main () {
 /* float, because a pointer can't be implicitly cast to float */
 void check(float f) {}
 
-int
-main () {
+int main(void)
+{
   char buffer[1024];
   /* This will not compile if strerror_r does not return an int */
   check(strerror_r(EACCES, buffer, sizeof(buffer)));
   return 0;
 }
 #endif
+
 #ifdef HAVE_FSETXATTR_6
 #include <sys/xattr.h> /* header from libc, not from libattr */
-int
-main() {
+int main(void)
+{
   fsetxattr(0, 0, 0, 0, 0, 0);
   return 0;
 }
 #endif
+
 #ifdef HAVE_FSETXATTR_5
 #include <sys/xattr.h> /* header from libc, not from libattr */
-int
-main() {
+int main(void)
+{
   fsetxattr(0, 0, 0, 0, 0);
   return 0;
 }
 #endif
+
 #ifdef HAVE_CLOCK_GETTIME_MONOTONIC
 #include <time.h>
-int
-main() {
+int main(void)
+{
   struct timespec ts = {0, 0};
   clock_gettime(CLOCK_MONOTONIC, &ts);
   return 0;
 }
 #endif
+
 #ifdef HAVE_BUILTIN_AVAILABLE
-int
-main() {
+int main(void)
+{
   if(__builtin_available(macOS 10.12, *)) {}
   return 0;
 }
 #endif
-#ifdef HAVE_VARIADIC_MACROS_C99
-#define c99_vmacro3(first, ...) fun3(first, __VA_ARGS__)
-#define c99_vmacro2(first, ...) fun2(first, __VA_ARGS__)
 
-int fun3(int arg1, int arg2, int arg3);
-int fun2(int arg1, int arg2);
-
-int fun3(int arg1, int arg2, int arg3) {
-  return arg1 + arg2 + arg3;
-}
-int fun2(int arg1, int arg2) {
-  return arg1 + arg2;
-}
-
-int
-main() {
-  int res3 = c99_vmacro3(1, 2, 3);
-  int res2 = c99_vmacro2(1, 2);
-  (void)res3;
-  (void)res2;
-  return 0;
-}
-#endif
-#ifdef HAVE_VARIADIC_MACROS_GCC
-#define gcc_vmacro3(first, args...) fun3(first, args)
-#define gcc_vmacro2(first, args...) fun2(first, args)
-
-int fun3(int arg1, int arg2, int arg3);
-int fun2(int arg1, int arg2);
-
-int fun3(int arg1, int arg2, int arg3) {
-  return arg1 + arg2 + arg3;
-}
-int fun2(int arg1, int arg2) {
-  return arg1 + arg2;
-}
-
-int
-main() {
-  int res3 = gcc_vmacro3(1, 2, 3);
-  int res2 = gcc_vmacro2(1, 2);
-  (void)res3;
-  (void)res2;
-  return 0;
-}
-#endif
 #ifdef HAVE_ATOMIC
 /* includes start */
 #ifdef HAVE_SYS_TYPES_H
@@ -490,17 +413,24 @@ main() {
 #endif
 /* includes end */
 
-int
-main() {
+int main(void)
+{
   _Atomic int i = 1;
   i = 0;  /* Force an atomic-write operation. */
   return i;
 }
 #endif
+
 #ifdef HAVE_WIN32_WINNT
 /* includes start */
-#ifdef WIN32
-#  include "../lib/setup-win32.h"
+#ifdef _WIN32
+#  ifndef WIN32_LEAN_AND_MEAN
+#    define WIN32_LEAN_AND_MEAN
+#  endif
+#  ifndef NOGDI
+#    define NOGDI
+#  endif
+#  include <windows.h>
 #endif
 /* includes end */
 
@@ -508,8 +438,8 @@ main() {
 #define expand(x) enquote(x)
 #pragma message("_WIN32_WINNT=" expand(_WIN32_WINNT))
 
-int
-main() {
+int main(void)
+{
   return 0;
 }
 #endif

+ 7 - 0
CMake/FindZstd.cmake

@@ -56,11 +56,18 @@ find_library(Zstd_LIBRARY NAMES zstd
     ${PC_Zstd_LIBRARY_DIRS}
 )
 
+if(Zstd_INCLUDE_DIR)
+  file(READ "${Zstd_INCLUDE_DIR}/zstd.h" _zstd_header)
+  string(REGEX MATCH ".*define ZSTD_VERSION_MAJOR *([0-9]+).*define ZSTD_VERSION_MINOR *([0-9]+).*define ZSTD_VERSION_RELEASE *([0-9]+)" _zstd_ver "${_zstd_header}")
+  set(Zstd_VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}.${CMAKE_MATCH_3}")
+endif()
+
 include(FindPackageHandleStandardArgs)
 find_package_handle_standard_args(Zstd
   REQUIRED_VARS
     Zstd_LIBRARY
     Zstd_INCLUDE_DIR
+  VERSION_VAR Zstd_VERSION
 )
 
 if(Zstd_FOUND)

+ 0 - 13
CMake/Macros.cmake

@@ -23,19 +23,6 @@
 ###########################################################################
 #File defines convenience macros for available feature testing
 
-# This macro checks if the symbol exists in the library and if it
-# does, it prepends library to the list.  It is intended to be called
-# multiple times with a sequence of possibly dependent libraries in
-# order of least-to-most-dependent.  Some libraries depend on others
-# to link correctly.
-macro(check_library_exists_concat LIBRARY SYMBOL VARIABLE)
-  check_library_exists("${LIBRARY};${CURL_LIBS}" ${SYMBOL} "${CMAKE_LIBRARY_PATH}"
-    ${VARIABLE})
-  if(${VARIABLE})
-    set(CURL_LIBS ${LIBRARY} ${CURL_LIBS})
-  endif()
-endmacro()
-
 # Check if header file exists and add it to the list.
 # This macro is intended to be called multiple times with a sequence of
 # possibly dependent header files.  Some headers depend on others to be

+ 7 - 7
CMake/OtherTests.cmake

@@ -35,13 +35,13 @@ endmacro()
 
 set(signature_call_conv)
 if(HAVE_WINDOWS_H)
-  add_header_include(HAVE_WINSOCK2_H "winsock2.h")
-  add_header_include(HAVE_WINDOWS_H "windows.h")
   set(_source_epilogue
       "${_source_epilogue}\n#ifndef WIN32_LEAN_AND_MEAN\n#define WIN32_LEAN_AND_MEAN\n#endif")
+  add_header_include(HAVE_WINSOCK2_H "winsock2.h")
+  add_header_include(HAVE_WINDOWS_H "windows.h")
   set(signature_call_conv "PASCAL")
   if(WIN32)
-    set(CMAKE_REQUIRED_LIBRARIES ws2_32)
+    set(CMAKE_REQUIRED_LIBRARIES "ws2_32")
   endif()
 else()
   add_header_include(HAVE_SYS_TYPES_H "sys/types.h")
@@ -71,11 +71,11 @@ int main(void) {
 }" HAVE_STRUCT_TIMEVAL)
 
 if(HAVE_WINDOWS_H)
-  set(CMAKE_EXTRA_INCLUDE_FILES winsock2.h)
+  set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h")
 else()
   set(CMAKE_EXTRA_INCLUDE_FILES)
   if(HAVE_SYS_SOCKET_H)
-    set(CMAKE_EXTRA_INCLUDE_FILES sys/socket.h)
+    set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h")
   endif()
 endif()
 
@@ -172,7 +172,7 @@ if(NOT DEFINED HAVE_GETADDRINFO_THREADSAFE)
     }" HAVE_H_ERRNO)
 
   if(NOT HAVE_H_ERRNO)
-    check_c_source_runs("${_source_epilogue}
+    check_c_source_compiles("${_source_epilogue}
       int main(void)
       {
         h_errno = 2;
@@ -201,7 +201,7 @@ if(NOT DEFINED HAVE_GETADDRINFO_THREADSAFE)
   set(_source_epilogue "${_save_epilogue}")
 endif()
 
-if(NOT DEFINED HAVE_CLOCK_GETTIME_MONOTONIC_RAW)
+if(NOT WIN32 AND NOT DEFINED HAVE_CLOCK_GETTIME_MONOTONIC_RAW)
   set(_save_epilogue "${_source_epilogue}")
   set(_source_epilogue "#undef inline")
 

+ 28 - 9
CMake/PickyWarnings.cmake

@@ -52,8 +52,8 @@ if(PICKY_COMPILER)
     # Assume these options always exist with both clang and gcc.
     # Require clang 3.0 / gcc 2.95 or later.
     list(APPEND WPICKY_ENABLE
-      -Wbad-function-cast                  # clang  3.0  gcc  2.95
-      -Wconversion                         # clang  3.0  gcc  2.95
+      -Wbad-function-cast                  # clang  2.7  gcc  2.95
+      -Wconversion                         # clang  2.7  gcc  2.95
       -Winline                             # clang  1.0  gcc  1.0
       -Wmissing-declarations               # clang  1.0  gcc  2.7
       -Wmissing-prototypes                 # clang  1.0  gcc  1.0
@@ -70,23 +70,37 @@ if(PICKY_COMPILER)
 
     # Always enable with clang, version dependent with gcc
     set(WPICKY_COMMON_OLD
+      -Waddress                            # clang  2.7  gcc  4.3
+      -Wattributes                         # clang  2.7  gcc  4.1
       -Wcast-align                         # clang  1.0  gcc  4.2
       -Wdeclaration-after-statement        # clang  1.0  gcc  3.4
-      -Wempty-body                         # clang  3.0  gcc  4.3
+      -Wdiv-by-zero                        # clang  2.7  gcc  4.1
+      -Wempty-body                         # clang  2.7  gcc  4.3
       -Wendif-labels                       # clang  1.0  gcc  3.3
       -Wfloat-equal                        # clang  1.0  gcc  2.96 (3.0)
-      -Wignored-qualifiers                 # clang  3.0  gcc  4.3
+      -Wformat-security                    # clang  2.7  gcc  4.1
+      -Wignored-qualifiers                 # clang  2.8  gcc  4.3
+      -Wmissing-field-initializers         # clang  2.7  gcc  4.1
+      -Wmissing-noreturn                   # clang  2.7  gcc  4.1
       -Wno-format-nonliteral               # clang  1.0  gcc  2.96 (3.0)
-      -Wno-sign-conversion                 # clang  3.0  gcc  4.3
+      -Wno-sign-conversion                 # clang  2.9  gcc  4.3
       -Wno-system-headers                  # clang  1.0  gcc  3.0
+    # -Wpadded                             # clang  2.9  gcc  4.1               # Not used because we cannot change public structs
+      -Wredundant-decls                    # clang  2.7  gcc  4.1
+      -Wold-style-definition               # clang  2.7  gcc  3.4
       -Wstrict-prototypes                  # clang  1.0  gcc  3.3
-      -Wtype-limits                        # clang  3.0  gcc  4.3
+    # -Wswitch-enum                        # clang  2.7  gcc  4.1               # Not used because this basically disallows default case
+      -Wtype-limits                        # clang  2.7  gcc  4.3
+      -Wunreachable-code                   # clang  2.7  gcc  4.1
+    # -Wunused-macros                      # clang  2.7  gcc  4.1               # Not practical
+      -Wunused-parameter                   # clang  2.7  gcc  4.1
       -Wvla                                # clang  2.8  gcc  4.3
     )
 
     set(WPICKY_COMMON
       -Wdouble-promotion                   # clang  3.6  gcc  4.6  appleclang  6.3
       -Wenum-conversion                    # clang  3.2  gcc 10.0  appleclang  4.6  g++ 11.0
+      -Wpragmas                            # clang  3.5  gcc  4.1  appleclang  6.0
       -Wunused-const-variable              # clang  3.4  gcc  6.0  appleclang  5.1
     )
 
@@ -95,12 +109,16 @@ if(PICKY_COMPILER)
         ${WPICKY_COMMON_OLD}
         -Wshift-sign-overflow              # clang  2.9
         -Wshorten-64-to-32                 # clang  1.0
+        -Wlanguage-extension-token         # clang  3.0
       )
       # Enable based on compiler version
       if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.6) OR
          (CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 6.3))
         list(APPEND WPICKY_ENABLE
           ${WPICKY_COMMON}
+          -Wunreachable-code-break         # clang  3.5            appleclang  6.0
+          -Wheader-guard                   # clang  3.4            appleclang  5.1
+          -Wsometimes-uninitialized        # clang  3.2            appleclang  4.6
         )
       endif()
       if((CMAKE_C_COMPILER_ID STREQUAL "Clang"      AND NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 3.9) OR
@@ -125,6 +143,7 @@ if(PICKY_COMPILER)
       if(NOT CMAKE_C_COMPILER_VERSION VERSION_LESS 4.3)
         list(APPEND WPICKY_ENABLE
           ${WPICKY_COMMON_OLD}
+          -Wclobbered                      #             gcc  4.3
           -Wmissing-parameter-type         #             gcc  4.3
           -Wold-style-declaration          #             gcc  4.3
           -Wstrict-aliasing=3              #             gcc  4.0
@@ -159,7 +178,7 @@ if(PICKY_COMPILER)
           -Walloc-zero                     #             gcc  7.0
           -Wduplicated-branches            #             gcc  7.0
           -Wformat-overflow=2              #             gcc  7.0
-          -Wformat-truncation=1            #             gcc  7.0
+          -Wformat-truncation=2            #             gcc  7.0
           -Wrestrict                       #             gcc  7.0
         )
       endif()
@@ -174,11 +193,11 @@ if(PICKY_COMPILER)
 
     unset(WPICKY)
 
-    foreach(_CCOPT ${WPICKY_ENABLE})
+    foreach(_CCOPT IN LISTS WPICKY_ENABLE)
       set(WPICKY "${WPICKY} ${_CCOPT}")
     endforeach()
 
-    foreach(_CCOPT ${WPICKY_DETECT})
+    foreach(_CCOPT IN LISTS WPICKY_DETECT)
       # surprisingly, CHECK_C_COMPILER_FLAG needs a new variable to store each new
       # test result in.
       string(MAKE_C_IDENTIFIER "OPT${_CCOPT}" _optvarname)

+ 154 - 100
CMake/Platforms/WindowsCache.cmake

@@ -21,113 +21,167 @@
 # SPDX-License-Identifier: curl
 #
 ###########################################################################
-if(NOT UNIX)
-  if(WIN32)
+if(NOT WIN32)
+  message(FATAL_ERROR "This file should be included on Windows platform only")
+endif()
 
-    set(HAVE_WINDOWS_H 1)
-    set(HAVE_WS2TCPIP_H 1)
-    set(HAVE_WINSOCK2_H 1)
+set(HAVE_LOCALE_H 1)
 
-    if(MINGW)
-      set(HAVE_SNPRINTF 1)
-      set(HAVE_UNISTD_H 1)
-      set(HAVE_INTTYPES_H 1)
+if(MINGW)
+  set(HAVE_SNPRINTF 1)
+  set(HAVE_UNISTD_H 1)
+  set(HAVE_LIBGEN_H 1)
+  set(HAVE_STDDEF_H 1)  # detected by CMake internally in check_type_size()
+  set(HAVE_STDBOOL_H 1)
+  set(HAVE_BOOL_T "${HAVE_STDBOOL_H}")
+  set(HAVE_STRTOLL 1)
+  set(HAVE_BASENAME 1)
+  set(HAVE_STRCASECMP 1)
+  set(HAVE_FTRUNCATE 1)
+  set(HAVE_SYS_PARAM_H 1)
+  set(HAVE_SYS_TIME_H 1)
+  set(HAVE_GETTIMEOFDAY 1)
+else()
+  set(HAVE_LIBGEN_H 0)
+  set(HAVE_STRCASECMP 0)
+  set(HAVE_FTRUNCATE 0)
+  set(HAVE_SYS_PARAM_H 0)
+  set(HAVE_SYS_TIME_H 0)
+  set(HAVE_GETTIMEOFDAY 0)
+  if(MSVC)
+    set(HAVE_UNISTD_H 0)
+    set(HAVE_LOCALE_H 1)
+    set(HAVE_STDDEF_H 1)  # detected by CMake internally in check_type_size()
+    set(HAVE_STDATOMIC_H 0)
+    if(NOT MSVC_VERSION LESS 1800)
+      set(HAVE_STDBOOL_H 1)
       set(HAVE_STRTOLL 1)
-      set(HAVE_BASENAME 1)
-    elseif(MSVC)
-      if(NOT MSVC_VERSION LESS 1800)
-        set(HAVE_INTTYPES_H 1)
-        set(HAVE_STRTOLL 1)
-      else()
-        set(HAVE_INTTYPES_H 0)
-        set(HAVE_STRTOLL 0)
-      endif()
-      if(NOT MSVC_VERSION LESS 1900)
-        set(HAVE_SNPRINTF 1)
-      else()
-        set(HAVE_SNPRINTF 0)
-      endif()
-      set(HAVE_BASENAME 0)
+    else()
+      set(HAVE_STDBOOL_H 0)
+      set(HAVE_STRTOLL 0)
     endif()
+    set(HAVE_BOOL_T "${HAVE_STDBOOL_H}")
+    if(NOT MSVC_VERSION LESS 1900)
+      set(HAVE_SNPRINTF 1)
+    else()
+      set(HAVE_SNPRINTF 0)
+    endif()
+    set(HAVE_BASENAME 0)
+    set(HAVE_STRTOK_R 0)
+    set(HAVE_FILE_OFFSET_BITS 0)
+    set(HAVE_ATOMIC 0)
+  endif()
+endif()
 
-    set(HAVE_LIBSOCKET 0)
-    set(HAVE_GETHOSTNAME 1)
-    set(HAVE_LIBZ 0)
+# Available in Windows XP and newer
+set(HAVE_GETADDRINFO 1)
+set(HAVE_FREEADDRINFO 1)
 
-    set(HAVE_ARC4RANDOM 0)
-    set(HAVE_FNMATCH 0)
-    set(HAVE_SCHED_YIELD 0)
-    set(HAVE_ARPA_INET_H 0)
-    set(HAVE_FCNTL_H 1)
-    set(HAVE_IFADDRS_H 0)
-    set(HAVE_IO_H 1)
-    set(HAVE_NETDB_H 0)
-    set(HAVE_NETINET_IN_H 0)
-    set(HAVE_NETINET_TCP_H 0)
-    set(HAVE_NETINET_UDP_H 0)
-    set(HAVE_NET_IF_H 0)
-    set(HAVE_IOCTL_SIOCGIFADDR 0)
-    set(HAVE_POLL_H 0)
-    set(HAVE_POLL_FINE 0)
-    set(HAVE_PWD_H 0)
-    set(HAVE_STRINGS_H 0)
-    set(HAVE_SYS_FILIO_H 0)
-    set(HAVE_SYS_WAIT_H 0)
-    set(HAVE_SYS_IOCTL_H 0)
-    set(HAVE_SYS_PARAM_H 0)
-    set(HAVE_SYS_POLL_H 0)
-    set(HAVE_SYS_RESOURCE_H 0)
-    set(HAVE_SYS_SELECT_H 0)
-    set(HAVE_SYS_SOCKET_H 0)
-    set(HAVE_SYS_SOCKIO_H 0)
-    set(HAVE_SYS_STAT_H 1)
-    set(HAVE_SYS_TIME_H 0)
-    set(HAVE_SYS_TYPES_H 1)
-    set(HAVE_SYS_UN_H 0)
-    set(HAVE_SYS_UTIME_H 1)
-    set(HAVE_TERMIOS_H 0)
-    set(HAVE_TERMIO_H 0)
-    set(HAVE_UTIME_H 0)
+set(HAVE_FCHMOD 0)
+set(HAVE_SOCKETPAIR 0)
+set(HAVE_SENDMSG 0)
+set(HAVE_ALARM 0)
+set(HAVE_FCNTL 0)
+set(HAVE_GETPPID 0)
+set(HAVE_UTIMES 0)
+set(HAVE_GETPWUID_R 0)
+set(HAVE_STRERROR_R 0)
+set(HAVE_SIGINTERRUPT 0)
+set(HAVE_PIPE 0)
+set(HAVE_IF_NAMETOINDEX 0)
+set(HAVE_GETRLIMIT 0)
+set(HAVE_SETRLIMIT 0)
+set(HAVE_FSETXATTR 0)
+set(HAVE_LIBSOCKET 0)
+set(HAVE_SETLOCALE 1)
+set(HAVE_SETMODE 1)
+set(HAVE_GETPEERNAME 1)
+set(HAVE_GETSOCKNAME 1)
+set(HAVE_GETHOSTNAME 1)
+set(HAVE_LIBZ 0)
 
-    set(HAVE_FSEEKO 0)
-    set(HAVE__FSEEKI64 1)
-    set(HAVE_SOCKET 1)
-    set(HAVE_SELECT 1)
-    set(HAVE_STRDUP 1)
-    set(HAVE_STRICMP 1)
-    set(HAVE_STRCMPI 1)
-    set(HAVE_MEMRCHR 0)
-    set(HAVE_GETTIMEOFDAY 0)
-    set(HAVE_CLOSESOCKET 1)
-    set(HAVE_SIGSETJMP 0)
-    set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1)
-    set(HAVE_GETPASS_R 0)
-    set(HAVE_GETPWUID 0)
-    set(HAVE_GETEUID 0)
-    set(HAVE_UTIME 1)
-    set(HAVE_GMTIME_R 0)
-    set(HAVE_CLOCK_GETTIME_MONOTONIC_RAW 0)
-    set(HAVE_GETHOSTBYNAME_R 0)
-    set(HAVE_SIGNAL 1)
-    set(HAVE_LINUX_TCP_H 0)
-    set(HAVE_GLIBC_STRERROR_R 0)
-    set(HAVE_MACH_ABSOLUTE_TIME 0)
-    set(HAVE_GETIFADDRS 0)
+set(HAVE_RECV 1)
+set(HAVE_SEND 1)
+set(HAVE_STROPTS_H 0)
+set(HAVE_SYS_XATTR_H 0)
+set(HAVE_ARC4RANDOM 0)
+set(HAVE_FNMATCH 0)
+set(HAVE_SCHED_YIELD 0)
+set(HAVE_ARPA_INET_H 0)
+set(HAVE_FCNTL_H 1)
+set(HAVE_IFADDRS_H 0)
+set(HAVE_IO_H 1)
+set(HAVE_NETDB_H 0)
+set(HAVE_NETINET_IN_H 0)
+set(HAVE_NETINET_TCP_H 0)
+set(HAVE_NETINET_UDP_H 0)
+set(HAVE_NET_IF_H 0)
+set(HAVE_IOCTL_SIOCGIFADDR 0)
+set(HAVE_POLL_H 0)
+set(HAVE_POLL_FINE 0)
+set(HAVE_PWD_H 0)
+set(HAVE_STRINGS_H 0)  # mingw-w64 has it (wrapper to string.h)
+set(HAVE_SYS_FILIO_H 0)
+set(HAVE_SYS_WAIT_H 0)
+set(HAVE_SYS_IOCTL_H 0)
+set(HAVE_SYS_POLL_H 0)
+set(HAVE_SYS_RESOURCE_H 0)
+set(HAVE_SYS_SELECT_H 0)
+set(HAVE_SYS_SOCKET_H 0)
+set(HAVE_SYS_SOCKIO_H 0)
+set(HAVE_SYS_STAT_H 1)
+set(HAVE_SYS_TYPES_H 1)
+set(HAVE_SYS_UN_H 0)
+set(HAVE_SYS_UTIME_H 1)
+set(HAVE_TERMIOS_H 0)
+set(HAVE_TERMIO_H 0)
+set(HAVE_UTIME_H 0)  # mingw-w64 has it (wrapper to sys/utime.h)
 
-    set(HAVE_GETHOSTBYNAME_R_3 0)
-    set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0)
-    set(HAVE_GETHOSTBYNAME_R_5 0)
-    set(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0)
-    set(HAVE_GETHOSTBYNAME_R_6 0)
-    set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 0)
+set(HAVE_FSEEKO 0)
+set(HAVE__FSEEKI64 1)
+set(HAVE_SOCKET 1)
+set(HAVE_SELECT 1)
+set(HAVE_STRDUP 1)
+set(HAVE_STRICMP 1)
+set(HAVE_STRCMPI 1)
+set(HAVE_MEMRCHR 0)
+set(HAVE_CLOSESOCKET 1)
+set(HAVE_SIGSETJMP 0)
+set(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1)
+set(HAVE_GETPASS_R 0)
+set(HAVE_GETPWUID 0)
+set(HAVE_GETEUID 0)
+set(HAVE_UTIME 1)
+set(HAVE_GMTIME_R 0)
+set(HAVE_GETHOSTBYNAME_R 0)
+set(HAVE_SIGNAL 1)
+set(HAVE_SIGACTION 0)
+set(HAVE_LINUX_TCP_H 0)
+set(HAVE_GLIBC_STRERROR_R 0)
+set(HAVE_MACH_ABSOLUTE_TIME 0)
+set(HAVE_GETIFADDRS 0)
+set(HAVE_FCNTL_O_NONBLOCK 0)
+set(HAVE_IOCTLSOCKET 1)
+set(HAVE_IOCTLSOCKET_CAMEL 0)
+set(HAVE_IOCTLSOCKET_CAMEL_FIONBIO 0)
+set(HAVE_IOCTLSOCKET_FIONBIO 1)
+set(HAVE_IOCTL_FIONBIO 0)
+set(HAVE_SETSOCKOPT_SO_NONBLOCK 0)
+set(HAVE_POSIX_STRERROR_R 0)
+set(HAVE_BUILTIN_AVAILABLE 0)
+set(HAVE_MSG_NOSIGNAL 0)
+set(HAVE_STRUCT_TIMEVAL 1)
 
-    set(HAVE_O_NONBLOCK 0)
-    set(HAVE_IN_ADDR_T 0)
-    set(STDC_HEADERS 1)
+set(HAVE_GETHOSTBYNAME_R_3 0)
+set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0)
+set(HAVE_GETHOSTBYNAME_R_5 0)
+set(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0)
+set(HAVE_GETHOSTBYNAME_R_6 0)
+set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 0)
 
-    set(HAVE_SIGACTION 0)
-    set(HAVE_MACRO_SIGSETJMP 0)
-  else()
-    message("This file should be included on Windows platform only")
-  endif()
-endif()
+set(HAVE_O_NONBLOCK 0)
+set(HAVE_IN_ADDR_T 0)
+set(STDC_HEADERS 1)
+
+set(HAVE_SIZEOF_SUSECONDS_T 0)
+set(HAVE_SIZEOF_SA_FAMILY_T 0)

+ 3 - 1
CMake/curl-config.cmake.in

@@ -35,4 +35,6 @@ include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
 check_required_components("@PROJECT_NAME@")
 
 # Alias for either shared or static library
-add_library(@PROJECT_NAME@::libcurl ALIAS @PROJECT_NAME@::@LIB_SELECTED@)
+if(NOT TARGET @PROJECT_NAME@::libcurl)
+  add_library(@PROJECT_NAME@::libcurl ALIAS @PROJECT_NAME@::@LIB_SELECTED@)
+endif()

+ 346 - 313
CMakeLists.txt

@@ -54,13 +54,13 @@
 #   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_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in QUICHE
-#   HAVE_ZSTD_CREATEDSTREAM: `ZSTD_createDStream` present in Zstd
 #
 # For each of the above variables, if the variable is DEFINED (either
 # to ON or OFF), the symbol detection will be skipped.  If the
 # variable is NOT DEFINED, the symbol detection will be performed.
 
 cmake_minimum_required(VERSION 3.7...3.16 FATAL_ERROR)
+message(STATUS "Using CMake version ${CMAKE_VERSION}")
 
 set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}")
 include(Utilities)
@@ -105,6 +105,8 @@ option(BUILD_SHARED_LIBS "Build shared libraries" ON)
 option(BUILD_STATIC_LIBS "Build static libraries" OFF)
 option(BUILD_STATIC_CURL "Build curl executable with static libcurl" OFF)
 option(ENABLE_ARES "Set to ON to enable c-ares support" OFF)
+option(CURL_DISABLE_INSTALL "Set to ON to disable instalation targets" OFF)
+
 if(WIN32)
   option(CURL_STATIC_CRT "Set to ON to build libcurl with static CRT on Windows (/MT)." OFF)
   option(ENABLE_UNICODE "Set to ON to use the Unicode version of the Windows API functions" OFF)
@@ -220,6 +222,8 @@ option(CURL_DISABLE_GETOPTIONS "disables curl_easy_options API for existing opti
 mark_as_advanced(CURL_DISABLE_GETOPTIONS)
 option(CURL_DISABLE_GOPHER "disables Gopher" OFF)
 mark_as_advanced(CURL_DISABLE_GOPHER)
+option(CURL_DISABLE_HEADERS_API "disables headers-api support" OFF)
+mark_as_advanced(CURL_DISABLE_HEADERS_API)
 option(CURL_DISABLE_HSTS "disables HSTS support" OFF)
 mark_as_advanced(CURL_DISABLE_HSTS)
 option(CURL_DISABLE_HTTP "disables HTTP" OFF)
@@ -237,6 +241,8 @@ mark_as_advanced(CURL_DISABLE_LIBCURL_OPTION)
 option(CURL_DISABLE_MIME "disables MIME support" OFF)
 mark_as_advanced(CURL_DISABLE_MIME)
 option(CURL_DISABLE_MQTT "disables MQTT" OFF)
+mark_as_advanced(CURL_DISABLE_BINDLOCAL)
+option(CURL_DISABLE_BINDLOCAL "disables local binding support" OFF)
 mark_as_advanced(CURL_DISABLE_MQTT)
 option(CURL_DISABLE_NETRC "disables netrc parser" OFF)
 mark_as_advanced(CURL_DISABLE_NETRC)
@@ -362,28 +368,33 @@ include(CheckCSourceCompiles)
 
 # On windows preload settings
 if(WIN32)
-  list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_WINSOCKAPI_=)
+  set(HAVE_WINDOWS_H 1)
+  set(HAVE_WS2TCPIP_H 1)
+  set(HAVE_WINSOCK2_H 1)
   include(${CMAKE_CURRENT_SOURCE_DIR}/CMake/Platforms/WindowsCache.cmake)
 endif()
 
 if(ENABLE_THREADED_RESOLVER)
-  find_package(Threads REQUIRED)
   if(WIN32)
     set(USE_THREADS_WIN32 ON)
   else()
+    find_package(Threads REQUIRED)
     set(USE_THREADS_POSIX ${CMAKE_USE_PTHREADS_INIT})
     set(HAVE_PTHREAD_H ${CMAKE_USE_PTHREADS_INIT})
+    set(CURL_LIBS ${CURL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
   endif()
-  set(CURL_LIBS ${CURL_LIBS} ${CMAKE_THREAD_LIBS_INIT})
 endif()
 
 # Check for all needed libraries
-check_library_exists_concat("socket" connect      HAVE_LIBSOCKET)
+check_library_exists("socket" "connect" "" HAVE_LIBSOCKET)
+if(HAVE_LIBSOCKET)
+  set(CURL_LIBS "socket;${CURL_LIBS}")
+endif()
 
 check_function_exists(gethostname HAVE_GETHOSTNAME)
 
 if(WIN32)
-  list(APPEND CURL_LIBS "ws2_32")
+  list(APPEND CURL_LIBS "ws2_32" "bcrypt")
   if(USE_LIBRTMP)
     list(APPEND CURL_LIBS "winmm")
   endif()
@@ -413,7 +424,7 @@ set(openssl_default ON)
 if(WIN32 OR CURL_USE_SECTRANSP OR CURL_USE_SCHANNEL OR CURL_USE_MBEDTLS OR CURL_USE_WOLFSSL)
   set(openssl_default OFF)
 endif()
-cmake_dependent_option(CURL_USE_OPENSSL "Use OpenSSL code. Experimental" ${openssl_default} CURL_ENABLE_SSL OFF)
+cmake_dependent_option(CURL_USE_OPENSSL "Enable OpenSSL for SSL/TLS" ${openssl_default} CURL_ENABLE_SSL OFF)
 option(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG "Disable automatic loading of OpenSSL configuration" OFF)
 
 count_true(enabled_ssl_options_count
@@ -487,11 +498,6 @@ if(CURL_USE_OPENSSL)
     include_directories(${OPENSSL_INCLUDE_DIR})
   endif()
 
-  if(WIN32)
-    list(APPEND CURL_LIBS "ws2_32")
-    list(APPEND CURL_LIBS "bcrypt")  # for OpenSSL/LibreSSL
-  endif()
-
   if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "openssl")
     set(valid_default_ssl_backend TRUE)
   endif()
@@ -604,17 +610,12 @@ option(CURL_ZSTD "Set to ON to enable building curl with zstd support." OFF)
 set(HAVE_ZSTD OFF)
 if(CURL_ZSTD)
   find_package(Zstd REQUIRED)
-  if(NOT DEFINED HAVE_ZSTD_CREATEDSTREAM)
-    cmake_push_check_state()
-    set(CMAKE_REQUIRED_INCLUDES ${Zstd_INCLUDE_DIRS})
-    set(CMAKE_REQUIRED_LIBRARIES ${Zstd_LIBRARIES})
-    check_symbol_exists(ZSTD_createDStream "zstd.h" HAVE_ZSTD_CREATEDSTREAM)
-    cmake_pop_check_state()
-  endif()
-  if(Zstd_FOUND AND HAVE_ZSTD_CREATEDSTREAM)
+  if(Zstd_FOUND AND NOT Zstd_VERSION VERSION_LESS "1.0.0")
     set(HAVE_ZSTD ON)
     list(APPEND CURL_LIBS ${Zstd_LIBRARIES})
     include_directories(${Zstd_INCLUDE_DIRS})
+  else()
+    message(WARNING "zstd v1.0.0 or newer is required, disabling zstd support.")
   endif()
 endif()
 
@@ -647,6 +648,20 @@ macro(openssl_check_symbol_exists SYMBOL FILES VARIABLE)
   cmake_pop_check_state()
 endmacro()
 
+# Ensure that the OpenSSL fork actually supports QUIC.
+macro(openssl_check_quic)
+  if(NOT DEFINED HAVE_SSL_CTX_SET_QUIC_METHOD)
+    if(USE_OPENSSL)
+      openssl_check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD)
+    elseif(USE_WOLFSSL)
+      openssl_check_symbol_exists(wolfSSL_set_quic_method "wolfssl/options.h;wolfssl/openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD)
+    endif()
+  endif()
+  if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD)
+    message(FATAL_ERROR "QUIC support is missing in OpenSSL fork. Try setting -DOPENSSL_ROOT_DIR")
+  endif()
+endmacro()
+
 if(USE_OPENSSL OR USE_WOLFSSL)
   if(NOT DEFINED HAVE_SSL_SET0_WBIO)
     openssl_check_symbol_exists(SSL_set0_wbio "openssl/ssl.h" HAVE_SSL_SET0_WBIO)
@@ -673,18 +688,7 @@ if(USE_NGTCP2)
     else()
       find_package(NGTCP2 REQUIRED quictls)
     endif()
-
-    # Be sure that the OpenSSL/wolfSSL library actually supports QUIC.
-    if(NOT DEFINED HAVE_SSL_CTX_SET_QUIC_METHOD)
-      if(USE_OPENSSL)
-        openssl_check_symbol_exists(SSL_CTX_set_quic_method "openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD)
-      elseif(USE_WOLFSSL)
-        openssl_check_symbol_exists(wolfSSL_set_quic_method "wolfssl/options.h;wolfssl/openssl/ssl.h" HAVE_SSL_CTX_SET_QUIC_METHOD)
-      endif()
-    endif()
-    if(NOT HAVE_SSL_CTX_SET_QUIC_METHOD)
-      message(FATAL_ERROR "QUIC support is missing in OpenSSL/LibreSSL/BoringSSL/wolfSSL. Try setting -DOPENSSL_ROOT_DIR")
-    endif()
+    openssl_check_quic()
   elseif(USE_GNUTLS)
     find_package(NGTCP2 REQUIRED GnuTLS)
   else()
@@ -706,7 +710,10 @@ if(USE_QUICHE)
     message(FATAL_ERROR "Only one HTTP/3 backend can be selected!")
   endif()
   find_package(QUICHE REQUIRED)
-  CheckQuicSupportInOpenSSL()
+  if(NOT HAVE_BORINGSSL)
+    message(FATAL_ERROR "quiche requires BoringSSL")
+  endif()
+  openssl_check_quic()
   set(USE_QUICHE ON)
   include_directories(${QUICHE_INCLUDE_DIRS})
   list(APPEND CURL_LIBS ${QUICHE_LIBRARIES})
@@ -751,8 +758,12 @@ if(NOT CURL_DISABLE_LDAP)
   if(NOT USE_WIN32_LDAP)
     # Check for LDAP
     set(CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES})
-    check_library_exists_concat(${CMAKE_LDAP_LIB} ldap_init HAVE_LIBLDAP)
-    check_library_exists_concat(${CMAKE_LBER_LIB} ber_init HAVE_LIBLBER)
+    check_library_exists("${CMAKE_LDAP_LIB}" "ldap_init" "" HAVE_LIBLDAP)
+    if(HAVE_LIBLDAP)
+      check_library_exists("${CMAKE_LDAP_LIB};${CMAKE_LBER_LIB}" "ber_init" "" HAVE_LIBLBER)
+    else()
+      check_library_exists("${CMAKE_LBER_LIB}" "ber_init" "" HAVE_LIBLBER)
+    endif()
 
     set(CMAKE_REQUIRED_INCLUDES_BAK ${CMAKE_REQUIRED_INCLUDES})
     set(CMAKE_LDAP_INCLUDE_DIR "" CACHE STRING "Path to LDAP include directory")
@@ -791,8 +802,10 @@ if(NOT CURL_DISABLE_LDAP)
 
       list(APPEND CMAKE_REQUIRED_DEFINITIONS -DLDAP_DEPRECATED=1)
       list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LDAP_LIB})
+      set(CURL_LIBS "${CMAKE_LDAP_LIB};${CURL_LIBS}")
       if(HAVE_LIBLBER)
         list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_LBER_LIB})
+        set(CURL_LIBS "${CMAKE_LBER_LIB};${CURL_LIBS}")
       endif()
 
       check_c_source_compiles("
@@ -839,7 +852,11 @@ endif()
 # Check for idn2
 option(USE_LIBIDN2 "Use libidn2 for IDN support" ON)
 if(USE_LIBIDN2)
-  check_library_exists_concat("idn2" idn2_lookup_ul HAVE_LIBIDN2)
+  check_library_exists("idn2" "idn2_lookup_ul" "" HAVE_LIBIDN2)
+  if(HAVE_LIBIDN2)
+    set(CURL_LIBS "idn2;${CURL_LIBS}")
+    check_include_file_concat("idn2.h" HAVE_IDN2_H)
+  endif()
 else()
   set(HAVE_LIBIDN2 OFF)
 endif()
@@ -1052,13 +1069,46 @@ if(CURL_CA_PATH_SET AND
 endif()
 
 # Check for header files
-if(NOT UNIX)
-  check_include_file_concat("windows.h"      HAVE_WINDOWS_H)
-  check_include_file_concat("ws2tcpip.h"     HAVE_WS2TCPIP_H)
+if(WIN32)
   check_include_file_concat("winsock2.h"     HAVE_WINSOCK2_H)
+  check_include_file_concat("ws2tcpip.h"     HAVE_WS2TCPIP_H)
+  check_include_file_concat("windows.h"      HAVE_WINDOWS_H)
+endif()
+
+if(WIN32)
+  # detect actual value of _WIN32_WINNT and store as HAVE_WIN32_WINNT
+  curl_internal_test(HAVE_WIN32_WINNT)
+  if(HAVE_WIN32_WINNT)
+    string(REGEX MATCH ".*_WIN32_WINNT=0x[0-9a-fA-F]+" OUTPUT "${OUTPUT}")
+    string(REGEX REPLACE ".*_WIN32_WINNT=" "" OUTPUT "${OUTPUT}")
+    string(REGEX REPLACE "0x([0-9a-f][0-9a-f][0-9a-f])$" "0x0\\1" OUTPUT "${OUTPUT}")  # pad to 4 digits
+    string(TOLOWER "${OUTPUT}" HAVE_WIN32_WINNT)
+    message(STATUS "Found _WIN32_WINNT=${HAVE_WIN32_WINNT}")
+  endif()
+  # avoid storing HAVE_WIN32_WINNT in CMake cache
+  unset(HAVE_WIN32_WINNT CACHE)
+
+  if(HAVE_WIN32_WINNT)
+    if(HAVE_WIN32_WINNT STRLESS "0x0501")
+      # Windows XP is required for freeaddrinfo, getaddrinfo
+      message(FATAL_ERROR "Building for Windows XP or newer is required.")
+    endif()
+
+    # pre-fill detection results based on target OS version
+    if(MINGW OR MSVC)
+      if(HAVE_WIN32_WINNT STRLESS "0x0600")
+        set(HAVE_INET_NTOP 0)
+        set(HAVE_INET_PTON 0)
+      else()  # Windows Vista or newer
+        set(HAVE_INET_NTOP 1)
+        set(HAVE_INET_PTON 1)
+      endif()
+      unset(HAVE_INET_NTOP CACHE)
+      unset(HAVE_INET_PTON CACHE)
+    endif()
+  endif()
 endif()
 
-check_include_file_concat("inttypes.h"       HAVE_INTTYPES_H)
 check_include_file_concat("sys/filio.h"      HAVE_SYS_FILIO_H)
 check_include_file_concat("sys/wait.h"       HAVE_SYS_WAIT_H)
 check_include_file_concat("sys/ioctl.h"      HAVE_SYS_IOCTL_H)
@@ -1076,7 +1126,6 @@ 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("arpa/inet.h"      HAVE_ARPA_INET_H)
 check_include_file_concat("fcntl.h"          HAVE_FCNTL_H)
-check_include_file_concat("idn2.h"           HAVE_IDN2_H)
 check_include_file_concat("ifaddrs.h"        HAVE_IFADDRS_H)
 check_include_file_concat("io.h"             HAVE_IO_H)
 check_include_file_concat("libgen.h"         HAVE_LIBGEN_H)
@@ -1092,7 +1141,6 @@ check_include_file_concat("poll.h"           HAVE_POLL_H)
 check_include_file_concat("pwd.h"            HAVE_PWD_H)
 check_include_file_concat("stdatomic.h"      HAVE_STDATOMIC_H)
 check_include_file_concat("stdbool.h"        HAVE_STDBOOL_H)
-check_include_file_concat("stdint.h"         HAVE_STDINT_H)
 check_include_file_concat("strings.h"        HAVE_STRINGS_H)
 check_include_file_concat("stropts.h"        HAVE_STROPTS_H)
 check_include_file_concat("termio.h"         HAVE_TERMIO_H)
@@ -1137,7 +1185,6 @@ elseif(HAVE_LIBSOCKET)
   set(CMAKE_REQUIRED_LIBRARIES socket)
 endif()
 
-check_symbol_exists(fchmod        "${CURL_INCLUDES}" HAVE_FCHMOD)
 check_symbol_exists(fnmatch       "${CURL_INCLUDES};fnmatch.h" HAVE_FNMATCH)
 check_symbol_exists(basename      "${CURL_INCLUDES};string.h" HAVE_BASENAME)
 check_symbol_exists(socket        "${CURL_INCLUDES}" HAVE_SOCKET)
@@ -1174,6 +1221,7 @@ check_symbol_exists(gethostbyname_r "${CURL_INCLUDES}" HAVE_GETHOSTBYNAME_R)
 check_symbol_exists(signal         "${CURL_INCLUDES};signal.h" HAVE_SIGNAL)
 check_symbol_exists(strtoll        "${CURL_INCLUDES};stdlib.h" HAVE_STRTOLL)
 check_symbol_exists(strerror_r     "${CURL_INCLUDES};stdlib.h;string.h" HAVE_STRERROR_R)
+check_symbol_exists(sigaction      "signal.h" HAVE_SIGACTION)
 check_symbol_exists(siginterrupt   "${CURL_INCLUDES};signal.h" HAVE_SIGINTERRUPT)
 check_symbol_exists(getaddrinfo    "${CURL_INCLUDES};stdlib.h;string.h" HAVE_GETADDRINFO)
 check_symbol_exists(getifaddrs     "${CURL_INCLUDES};stdlib.h" HAVE_GETIFADDRS)
@@ -1190,6 +1238,10 @@ check_symbol_exists(setlocale      "${CURL_INCLUDES}" HAVE_SETLOCALE)
 check_symbol_exists(setmode        "${CURL_INCLUDES}" HAVE_SETMODE)
 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))
   # earlier MSVC compilers had faulty snprintf implementations
   check_symbol_exists(snprintf       "stdio.h" HAVE_SNPRINTF)
@@ -1213,20 +1265,11 @@ check_type_size("sa_family_t"   SIZEOF_SA_FAMILY_T)
 set(HAVE_SA_FAMILY_T            ${HAVE_SIZEOF_SA_FAMILY_T})
 set(CMAKE_EXTRA_INCLUDE_FILES   "")
 
-set(CMAKE_EXTRA_INCLUDE_FILES   "ws2def.h")
-check_type_size("ADDRESS_FAMILY"    SIZEOF_ADDRESS_FAMILY)
-set(HAVE_ADDRESS_FAMILY         ${HAVE_SIZEOF_ADDRESS_FAMILY})
-set(CMAKE_EXTRA_INCLUDE_FILES   "")
-
-# sigaction and sigsetjmp are special. Use special mechanism for
-# detecting those, but only if previous attempt failed.
-check_symbol_exists(sigaction "signal.h" HAVE_SIGACTION)
-
-if(NOT HAVE_SIGSETJMP)
-  check_symbol_exists(sigsetjmp "setjmp.h" HAVE_MACRO_SIGSETJMP)
-  if(HAVE_MACRO_SIGSETJMP)
-    set(HAVE_SIGSETJMP 1)
-  endif()
+if(WIN32)
+  set(CMAKE_EXTRA_INCLUDE_FILES   "ws2def.h")
+  check_type_size("ADDRESS_FAMILY"    SIZEOF_ADDRESS_FAMILY)
+  set(HAVE_ADDRESS_FAMILY         ${HAVE_SIZEOF_ADDRESS_FAMILY})
+  set(CMAKE_EXTRA_INCLUDE_FILES   "")
 endif()
 
 # Do curl specific tests
@@ -1250,8 +1293,6 @@ foreach(CURL_TEST
     HAVE_BOOL_T
     STDC_HEADERS
     HAVE_FILE_OFFSET_BITS
-    HAVE_VARIADIC_MACROS_C99
-    HAVE_VARIADIC_MACROS_GCC
     HAVE_ATOMIC
     )
   curl_internal_test(${CURL_TEST})
@@ -1271,18 +1312,6 @@ set(CMAKE_EXTRA_INCLUDE_FILES "curl/curl.h")
 check_type_size("curl_socket_t"  SIZEOF_CURL_SOCKET_T)
 set(CMAKE_EXTRA_INCLUDE_FILES "")
 
-if(WIN32)
-  # detect actual value of _WIN32_WINNT and store as HAVE_WIN32_WINNT
-  curl_internal_test(HAVE_WIN32_WINNT)
-  if(HAVE_WIN32_WINNT)
-    string(REGEX MATCH ".*_WIN32_WINNT=0x[0-9a-fA-F]+" OUTPUT "${OUTPUT}")
-    string(REGEX REPLACE ".*_WIN32_WINNT=" "" HAVE_WIN32_WINNT "${OUTPUT}")
-    message(STATUS "Found _WIN32_WINNT=${HAVE_WIN32_WINNT}")
-  endif()
-  # avoid storing HAVE_WIN32_WINNT in CMake cache
-  unset(HAVE_WIN32_WINNT CACHE)
-endif()
-
 if(NOT WIN32 AND NOT CMAKE_CROSSCOMPILING)
   # on not-Windows and not-crosscompiling, check for writable argv[]
   include(CheckCSourceRuns)
@@ -1338,8 +1367,10 @@ if(NEED_REENTRANT)
   endforeach()
 endif()
 
-# Check clock_gettime(CLOCK_MONOTONIC, x) support
-curl_internal_test(HAVE_CLOCK_GETTIME_MONOTONIC)
+if(NOT WIN32)
+  # Check clock_gettime(CLOCK_MONOTONIC, x) support
+  curl_internal_test(HAVE_CLOCK_GETTIME_MONOTONIC)
+endif()
 
 # Check compiler support of __builtin_available()
 curl_internal_test(HAVE_BUILTIN_AVAILABLE)
@@ -1404,8 +1435,6 @@ if(WIN32)
   if(USE_WIN32_CRYPTO OR USE_SCHANNEL)
     list(APPEND CURL_LIBS "advapi32" "crypt32")
   endif()
-
-  list(APPEND CURL_LIBS "bcrypt")
 endif()
 
 if(MSVC)
@@ -1492,258 +1521,262 @@ if(BUILD_TESTING)
   add_subdirectory(tests)
 endif()
 
-# Helper to populate a list (_items) with a label when conditions (the remaining
-# args) are satisfied
-macro(_add_if label)
-  # needs to be a macro to allow this indirection
-  if(${ARGN})
-    set(_items ${_items} "${label}")
+if(NOT CURL_DISABLE_INSTALL)
+
+  # Helper to populate a list (_items) with a label when conditions (the remaining
+  # args) are satisfied
+  macro(_add_if label)
+    # needs to be a macro to allow this indirection
+    if(${ARGN})
+      set(_items ${_items} "${label}")
+    endif()
+  endmacro()
+
+  # NTLM support requires crypto function adaptions from various SSL libs
+  # TODO alternative SSL libs tests for SSP1, GnuTLS, NSS
+  if(NOT (CURL_DISABLE_NTLM) AND
+      (USE_OPENSSL OR USE_MBEDTLS OR USE_DARWINSSL OR USE_WIN32_CRYPTO OR USE_GNUTLS))
+    set(use_curl_ntlm_core ON)
   endif()
-endmacro()
 
-# NTLM support requires crypto function adaptions from various SSL libs
-# TODO alternative SSL libs tests for SSP1, GnuTLS, NSS
-if(NOT (CURL_DISABLE_NTLM) AND
-    (USE_OPENSSL OR USE_MBEDTLS OR USE_DARWINSSL OR USE_WIN32_CRYPTO OR USE_GNUTLS))
-  set(use_curl_ntlm_core ON)
-endif()
-
-# Clear list and try to detect available features
-set(_items)
-_add_if("SSL"           SSL_ENABLED)
-_add_if("IPv6"          ENABLE_IPV6)
-_add_if("unixsockets"   USE_UNIX_SOCKETS)
-_add_if("libz"          HAVE_LIBZ)
-_add_if("brotli"        HAVE_BROTLI)
-_add_if("zstd"          HAVE_ZSTD)
-_add_if("AsynchDNS"     USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32)
-_add_if("IDN"           HAVE_LIBIDN2 OR USE_WIN32_IDN)
-_add_if("Largefile"     (SIZEOF_CURL_OFF_T GREATER 4) AND
-                        ((SIZEOF_OFF_T GREATER 4) OR USE_WIN32_LARGE_FILES))
-# TODO SSP1 (Schannel) check is missing
-_add_if("SSPI"          USE_WINDOWS_SSPI)
-_add_if("GSS-API"       HAVE_GSSAPI)
-_add_if("alt-svc"       NOT CURL_DISABLE_ALTSVC)
-_add_if("HSTS"          NOT CURL_DISABLE_HSTS)
-# TODO SSP1 missing for SPNEGO
-_add_if("SPNEGO"        NOT CURL_DISABLE_NEGOTIATE_AUTH AND
-                        (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
-_add_if("Kerberos"      NOT CURL_DISABLE_KERBEROS_AUTH AND
-                        (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
-# NTLM support requires crypto function adaptions from various SSL libs
-# TODO alternative SSL libs tests for SSP1, GnuTLS, NSS
-_add_if("NTLM"          NOT (CURL_DISABLE_NTLM) AND
-                        (use_curl_ntlm_core OR USE_WINDOWS_SSPI))
-# TODO missing option (autoconf: --enable-ntlm-wb)
-_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)
-# TODO option --with-nghttp2 tests for nghttp2 lib and nghttp2/nghttp2.h header
-_add_if("HTTP2"         USE_NGHTTP2)
-_add_if("HTTP3"         USE_NGTCP2 OR USE_QUICHE)
-_add_if("MultiSSL"      CURL_WITH_MULTI_SSL)
-# TODO wolfSSL only support this from v5.0.0 onwards
-_add_if("HTTPS-proxy"   SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS
-                        OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR
-                        USE_MBEDTLS OR USE_SECTRANSP))
-_add_if("unicode"       ENABLE_UNICODE)
-_add_if("threadsafe"    HAVE_ATOMIC OR (WIN32 AND
-                        HAVE_WIN32_WINNT GREATER_EQUAL 0x600))
-_add_if("PSL"           USE_LIBPSL)
-string(REPLACE ";" " " SUPPORT_FEATURES "${_items}")
-message(STATUS "Enabled features: ${SUPPORT_FEATURES}")
-
-# Clear list and try to detect available protocols
-set(_items)
-_add_if("HTTP"          NOT CURL_DISABLE_HTTP)
-_add_if("HTTPS"         NOT CURL_DISABLE_HTTP AND SSL_ENABLED)
-_add_if("FTP"           NOT CURL_DISABLE_FTP)
-_add_if("FTPS"          NOT CURL_DISABLE_FTP AND SSL_ENABLED)
-_add_if("FILE"          NOT CURL_DISABLE_FILE)
-_add_if("TELNET"        NOT CURL_DISABLE_TELNET)
-_add_if("LDAP"          NOT CURL_DISABLE_LDAP)
-# CURL_DISABLE_LDAP implies CURL_DISABLE_LDAPS
-_add_if("LDAPS"         NOT CURL_DISABLE_LDAPS AND
-                        ((USE_OPENLDAP AND SSL_ENABLED) OR
-                        (NOT USE_OPENLDAP AND HAVE_LDAP_SSL)))
-_add_if("DICT"          NOT CURL_DISABLE_DICT)
-_add_if("TFTP"          NOT CURL_DISABLE_TFTP)
-_add_if("GOPHER"        NOT CURL_DISABLE_GOPHER)
-_add_if("GOPHERS"       NOT CURL_DISABLE_GOPHER AND SSL_ENABLED)
-_add_if("POP3"          NOT CURL_DISABLE_POP3)
-_add_if("POP3S"         NOT CURL_DISABLE_POP3 AND SSL_ENABLED)
-_add_if("IMAP"          NOT CURL_DISABLE_IMAP)
-_add_if("IMAPS"         NOT CURL_DISABLE_IMAP AND SSL_ENABLED)
-_add_if("SMB"           NOT CURL_DISABLE_SMB AND
-                        use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4))
-_add_if("SMBS"          NOT CURL_DISABLE_SMB AND SSL_ENABLED AND
-                        use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4))
-_add_if("SMTP"          NOT CURL_DISABLE_SMTP)
-_add_if("SMTPS"         NOT CURL_DISABLE_SMTP AND SSL_ENABLED)
-_add_if("SCP"           USE_LIBSSH2 OR USE_LIBSSH)
-_add_if("SFTP"          USE_LIBSSH2 OR USE_LIBSSH)
-_add_if("RTSP"          NOT CURL_DISABLE_RTSP)
-_add_if("RTMP"          USE_LIBRTMP)
-_add_if("MQTT"          NOT CURL_DISABLE_MQTT)
-_add_if("WS"            USE_WEBSOCKETS)
-_add_if("WSS"           USE_WEBSOCKETS)
-if(_items)
-  list(SORT _items)
-endif()
-string(REPLACE ";" " " SUPPORT_PROTOCOLS "${_items}")
-message(STATUS "Enabled protocols: ${SUPPORT_PROTOCOLS}")
-
-# Clear list and collect SSL backends
-set(_items)
-_add_if("Schannel"         SSL_ENABLED AND USE_SCHANNEL)
-_add_if("OpenSSL"          SSL_ENABLED AND USE_OPENSSL)
-_add_if("Secure Transport" SSL_ENABLED AND USE_SECTRANSP)
-_add_if("mbedTLS"          SSL_ENABLED AND USE_MBEDTLS)
-_add_if("BearSSL"          SSL_ENABLED AND USE_BEARSSL)
-_add_if("wolfSSL"          SSL_ENABLED AND USE_WOLFSSL)
-_add_if("GnuTLS"           SSL_ENABLED AND USE_GNUTLS)
-
-if(_items)
-  list(SORT _items)
-endif()
-string(REPLACE ";" " " SSL_BACKENDS "${_items}")
-message(STATUS "Enabled SSL backends: ${SSL_BACKENDS}")
-if(CURL_DEFAULT_SSL_BACKEND)
-  message(STATUS "Default SSL backend: ${CURL_DEFAULT_SSL_BACKEND}")
-endif()
-
-# curl-config needs the following options to be set.
-set(CC                      "${CMAKE_C_COMPILER}")
-# TODO probably put a -D... options here?
-set(CONFIGURE_OPTIONS       "")
-set(CURLVERSION             "${CURL_VERSION}")
-set(exec_prefix             "\${prefix}")
-set(includedir              "\${prefix}/include")
-set(LDFLAGS                 "${CMAKE_SHARED_LINKER_FLAGS}")
-set(LIBCURL_LIBS            "")
-set(libdir                  "${CMAKE_INSTALL_PREFIX}/lib")
-foreach(_lib ${CMAKE_C_IMPLICIT_LINK_LIBRARIES} ${CURL_LIBS})
-  if(TARGET "${_lib}")
-    set(_libname "${_lib}")
-    get_target_property(_imported "${_libname}" IMPORTED)
-    if(NOT _imported)
-      # Reading the LOCATION property on non-imported target will error out.
-      # Assume the user won't need this information in the .pc file.
-      continue()
+  # Clear list and try to detect available features
+  set(_items)
+  _add_if("SSL"           SSL_ENABLED)
+  _add_if("IPv6"          ENABLE_IPV6)
+  _add_if("UnixSockets"   USE_UNIX_SOCKETS)
+  _add_if("libz"          HAVE_LIBZ)
+  _add_if("brotli"        HAVE_BROTLI)
+  _add_if("zstd"          HAVE_ZSTD)
+  _add_if("AsynchDNS"     USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32)
+  _add_if("IDN"           HAVE_LIBIDN2 OR USE_WIN32_IDN)
+  _add_if("Largefile"     (SIZEOF_CURL_OFF_T GREATER 4) AND
+                          ((SIZEOF_OFF_T GREATER 4) OR USE_WIN32_LARGE_FILES))
+  # TODO SSP1 (Schannel) check is missing
+  _add_if("SSPI"          USE_WINDOWS_SSPI)
+  _add_if("GSS-API"       HAVE_GSSAPI)
+  _add_if("alt-svc"       NOT CURL_DISABLE_ALTSVC)
+  _add_if("HSTS"          NOT CURL_DISABLE_HSTS)
+  # TODO SSP1 missing for SPNEGO
+  _add_if("SPNEGO"        NOT CURL_DISABLE_NEGOTIATE_AUTH AND
+                          (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
+  _add_if("Kerberos"      NOT CURL_DISABLE_KERBEROS_AUTH AND
+                          (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
+  # NTLM support requires crypto function adaptions from various SSL libs
+  # TODO alternative SSL libs tests for SSP1, GnuTLS, NSS
+  _add_if("NTLM"          NOT (CURL_DISABLE_NTLM) AND
+                          (use_curl_ntlm_core OR USE_WINDOWS_SSPI))
+  # TODO missing option (autoconf: --enable-ntlm-wb)
+  _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)
+  # TODO option --with-nghttp2 tests for nghttp2 lib and nghttp2/nghttp2.h header
+  _add_if("HTTP2"         USE_NGHTTP2)
+  _add_if("HTTP3"         USE_NGTCP2 OR USE_QUICHE)
+  _add_if("MultiSSL"      CURL_WITH_MULTI_SSL)
+  # TODO wolfSSL only support this from v5.0.0 onwards
+  _add_if("HTTPS-proxy"   SSL_ENABLED AND (USE_OPENSSL OR USE_GNUTLS
+                          OR USE_SCHANNEL OR USE_RUSTLS OR USE_BEARSSL OR
+                          USE_MBEDTLS OR USE_SECTRANSP))
+  _add_if("unicode"       ENABLE_UNICODE)
+  _add_if("threadsafe"    HAVE_ATOMIC OR
+                          (USE_THREADS_POSIX AND HAVE_PTHREAD_H) OR
+                          (WIN32 AND HAVE_WIN32_WINNT GREATER_EQUAL 0x600))
+  _add_if("PSL"           USE_LIBPSL)
+  string(REPLACE ";" " " SUPPORT_FEATURES "${_items}")
+  message(STATUS "Enabled features: ${SUPPORT_FEATURES}")
+
+  # Clear list and try to detect available protocols
+  set(_items)
+  _add_if("HTTP"          NOT CURL_DISABLE_HTTP)
+  _add_if("HTTPS"         NOT CURL_DISABLE_HTTP AND SSL_ENABLED)
+  _add_if("FTP"           NOT CURL_DISABLE_FTP)
+  _add_if("FTPS"          NOT CURL_DISABLE_FTP AND SSL_ENABLED)
+  _add_if("FILE"          NOT CURL_DISABLE_FILE)
+  _add_if("TELNET"        NOT CURL_DISABLE_TELNET)
+  _add_if("LDAP"          NOT CURL_DISABLE_LDAP)
+  # CURL_DISABLE_LDAP implies CURL_DISABLE_LDAPS
+  _add_if("LDAPS"         NOT CURL_DISABLE_LDAPS AND
+                          ((USE_OPENLDAP AND SSL_ENABLED) OR
+                          (NOT USE_OPENLDAP AND HAVE_LDAP_SSL)))
+  _add_if("DICT"          NOT CURL_DISABLE_DICT)
+  _add_if("TFTP"          NOT CURL_DISABLE_TFTP)
+  _add_if("GOPHER"        NOT CURL_DISABLE_GOPHER)
+  _add_if("GOPHERS"       NOT CURL_DISABLE_GOPHER AND SSL_ENABLED)
+  _add_if("POP3"          NOT CURL_DISABLE_POP3)
+  _add_if("POP3S"         NOT CURL_DISABLE_POP3 AND SSL_ENABLED)
+  _add_if("IMAP"          NOT CURL_DISABLE_IMAP)
+  _add_if("IMAPS"         NOT CURL_DISABLE_IMAP AND SSL_ENABLED)
+  _add_if("SMB"           NOT CURL_DISABLE_SMB AND
+                          use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4))
+  _add_if("SMBS"          NOT CURL_DISABLE_SMB AND SSL_ENABLED AND
+                          use_curl_ntlm_core AND (SIZEOF_CURL_OFF_T GREATER 4))
+  _add_if("SMTP"          NOT CURL_DISABLE_SMTP)
+  _add_if("SMTPS"         NOT CURL_DISABLE_SMTP AND SSL_ENABLED)
+  _add_if("SCP"           USE_LIBSSH2 OR USE_LIBSSH)
+  _add_if("SFTP"          USE_LIBSSH2 OR USE_LIBSSH)
+  _add_if("RTSP"          NOT CURL_DISABLE_RTSP)
+  _add_if("RTMP"          USE_LIBRTMP)
+  _add_if("MQTT"          NOT CURL_DISABLE_MQTT)
+  _add_if("WS"            USE_WEBSOCKETS)
+  _add_if("WSS"           USE_WEBSOCKETS)
+  if(_items)
+    list(SORT _items)
+  endif()
+  string(REPLACE ";" " " SUPPORT_PROTOCOLS "${_items}")
+  message(STATUS "Enabled protocols: ${SUPPORT_PROTOCOLS}")
+
+  # Clear list and collect SSL backends
+  set(_items)
+  _add_if("Schannel"         SSL_ENABLED AND USE_SCHANNEL)
+  _add_if("OpenSSL"          SSL_ENABLED AND USE_OPENSSL)
+  _add_if("Secure Transport" SSL_ENABLED AND USE_SECTRANSP)
+  _add_if("mbedTLS"          SSL_ENABLED AND USE_MBEDTLS)
+  _add_if("BearSSL"          SSL_ENABLED AND USE_BEARSSL)
+  _add_if("wolfSSL"          SSL_ENABLED AND USE_WOLFSSL)
+  _add_if("GnuTLS"           SSL_ENABLED AND USE_GNUTLS)
+
+  if(_items)
+    list(SORT _items)
+  endif()
+  string(REPLACE ";" " " SSL_BACKENDS "${_items}")
+  message(STATUS "Enabled SSL backends: ${SSL_BACKENDS}")
+  if(CURL_DEFAULT_SSL_BACKEND)
+    message(STATUS "Default SSL backend: ${CURL_DEFAULT_SSL_BACKEND}")
+  endif()
+
+  # curl-config needs the following options to be set.
+  set(CC                      "${CMAKE_C_COMPILER}")
+  # TODO probably put a -D... options here?
+  set(CONFIGURE_OPTIONS       "")
+  set(CURLVERSION             "${CURL_VERSION}")
+  set(exec_prefix             "\${prefix}")
+  set(includedir              "\${prefix}/include")
+  set(LDFLAGS                 "${CMAKE_SHARED_LINKER_FLAGS}")
+  set(LIBCURL_LIBS            "")
+  set(libdir                  "${CMAKE_INSTALL_PREFIX}/lib")
+  foreach(_lib ${CMAKE_C_IMPLICIT_LINK_LIBRARIES} ${CURL_LIBS})
+    if(TARGET "${_lib}")
+      set(_libname "${_lib}")
+      get_target_property(_imported "${_libname}" IMPORTED)
+      if(NOT _imported)
+        # Reading the LOCATION property on non-imported target will error out.
+        # Assume the user won't need this information in the .pc file.
+        continue()
+      endif()
+      get_target_property(_lib "${_libname}" LOCATION)
+      if(NOT _lib)
+        message(WARNING "Bad lib in library list: ${_libname}")
+        continue()
+      endif()
     endif()
-    get_target_property(_lib "${_libname}" LOCATION)
-    if(NOT _lib)
-      message(WARNING "Bad lib in library list: ${_libname}")
-      continue()
+    if(_lib MATCHES ".*/.*" OR _lib MATCHES "^-")
+      set(LIBCURL_LIBS          "${LIBCURL_LIBS} ${_lib}")
+    else()
+      set(LIBCURL_LIBS          "${LIBCURL_LIBS} -l${_lib}")
     endif()
+  endforeach()
+  if(BUILD_SHARED_LIBS)
+    set(ENABLE_SHARED         "yes")
+    set(LIBCURL_NO_SHARED     "")
+    set(CPPFLAG_CURL_STATICLIB "")
+  else()
+    set(ENABLE_SHARED         "no")
+    set(LIBCURL_NO_SHARED     "${LIBCURL_LIBS}")
+    set(CPPFLAG_CURL_STATICLIB "-DCURL_STATICLIB")
   endif()
-  if(_lib MATCHES ".*/.*" OR _lib MATCHES "^-")
-    set(LIBCURL_LIBS          "${LIBCURL_LIBS} ${_lib}")
+  if(BUILD_STATIC_LIBS)
+    set(ENABLE_STATIC         "yes")
   else()
-    set(LIBCURL_LIBS          "${LIBCURL_LIBS} -l${_lib}")
+    set(ENABLE_STATIC         "no")
   endif()
-endforeach()
-if(BUILD_SHARED_LIBS)
-  set(ENABLE_SHARED         "yes")
-  set(LIBCURL_NO_SHARED     "")
-  set(CPPFLAG_CURL_STATICLIB "")
-else()
-  set(ENABLE_SHARED         "no")
-  set(LIBCURL_NO_SHARED     "${LIBCURL_LIBS}")
-  set(CPPFLAG_CURL_STATICLIB "-DCURL_STATICLIB")
-endif()
-if(BUILD_STATIC_LIBS)
-  set(ENABLE_STATIC         "yes")
-else()
-  set(ENABLE_STATIC         "no")
-endif()
-# "a" (Linux) or "lib" (Windows)
-string(REPLACE "." "" libext "${CMAKE_STATIC_LIBRARY_SUFFIX}")
-set(prefix                  "${CMAKE_INSTALL_PREFIX}")
-# Set this to "yes" to append all libraries on which -lcurl is dependent
-set(REQUIRE_LIB_DEPS        "no")
-# SUPPORT_FEATURES
-# SUPPORT_PROTOCOLS
-set(VERSIONNUM              "${CURL_VERSION_NUM}")
-
-# Finally generate a "curl-config" matching this config
-# Use:
-# * ENABLE_SHARED
-# * ENABLE_STATIC
-configure_file("${CURL_SOURCE_DIR}/curl-config.in"
-               "${CURL_BINARY_DIR}/curl-config" @ONLY)
-install(FILES "${CURL_BINARY_DIR}/curl-config"
-        DESTINATION ${CMAKE_INSTALL_BINDIR}
-        PERMISSIONS
-          OWNER_READ OWNER_WRITE OWNER_EXECUTE
-          GROUP_READ GROUP_EXECUTE
-          WORLD_READ WORLD_EXECUTE)
-
-# Finally generate a pkg-config file matching this config
-configure_file("${CURL_SOURCE_DIR}/libcurl.pc.in"
-               "${CURL_BINARY_DIR}/libcurl.pc" @ONLY)
-install(FILES "${CURL_BINARY_DIR}/libcurl.pc"
-        DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
-
-# install headers
-install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/curl"
-    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
-    FILES_MATCHING PATTERN "*.h")
-
-include(CMakePackageConfigHelpers)
-write_basic_package_version_file(
-    "${version_config}"
-    VERSION ${CURL_VERSION}
-    COMPATIBILITY SameMajorVersion
-)
-file(READ "${version_config}" generated_version_config)
-file(WRITE "${version_config}"
-"if(NOT PACKAGE_FIND_VERSION_RANGE AND PACKAGE_FIND_VERSION_MAJOR STREQUAL \"7\")
-    # Version 8 satisfies version 7... requirements
-    set(PACKAGE_FIND_VERSION_MAJOR 8)
-    set(PACKAGE_FIND_VERSION_COUNT 1)
-endif()
-${generated_version_config}"
-)
+  # "a" (Linux) or "lib" (Windows)
+  string(REPLACE "." "" libext "${CMAKE_STATIC_LIBRARY_SUFFIX}")
+  set(prefix                  "${CMAKE_INSTALL_PREFIX}")
+  # Set this to "yes" to append all libraries on which -lcurl is dependent
+  set(REQUIRE_LIB_DEPS        "no")
+  # SUPPORT_FEATURES
+  # SUPPORT_PROTOCOLS
+  set(VERSIONNUM              "${CURL_VERSION_NUM}")
+
+  # Finally generate a "curl-config" matching this config
+  # Use:
+  # * ENABLE_SHARED
+  # * ENABLE_STATIC
+  configure_file("${CURL_SOURCE_DIR}/curl-config.in"
+                "${CURL_BINARY_DIR}/curl-config" @ONLY)
+  install(FILES "${CURL_BINARY_DIR}/curl-config"
+          DESTINATION ${CMAKE_INSTALL_BINDIR}
+          PERMISSIONS
+            OWNER_READ OWNER_WRITE OWNER_EXECUTE
+            GROUP_READ GROUP_EXECUTE
+            WORLD_READ WORLD_EXECUTE)
+
+  # Finally generate a pkg-config file matching this config
+  configure_file("${CURL_SOURCE_DIR}/libcurl.pc.in"
+                "${CURL_BINARY_DIR}/libcurl.pc" @ONLY)
+  install(FILES "${CURL_BINARY_DIR}/libcurl.pc"
+          DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
+
+  # install headers
+  install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/curl"
+      DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
+      FILES_MATCHING PATTERN "*.h")
+
+  include(CMakePackageConfigHelpers)
+  write_basic_package_version_file(
+      "${version_config}"
+      VERSION ${CURL_VERSION}
+      COMPATIBILITY SameMajorVersion
+  )
+  file(READ "${version_config}" generated_version_config)
+  file(WRITE "${version_config}"
+  "if(NOT PACKAGE_FIND_VERSION_RANGE AND PACKAGE_FIND_VERSION_MAJOR STREQUAL \"7\")
+      # Version 8 satisfies version 7... requirements
+      set(PACKAGE_FIND_VERSION_MAJOR 8)
+      set(PACKAGE_FIND_VERSION_COUNT 1)
+  endif()
+  ${generated_version_config}"
+  )
 
-# Use:
-# * TARGETS_EXPORT_NAME
-# * PROJECT_NAME
-configure_package_config_file(CMake/curl-config.cmake.in
-        "${project_config}"
-        INSTALL_DESTINATION ${CURL_INSTALL_CMAKE_DIR}
-)
+  # Use:
+  # * TARGETS_EXPORT_NAME
+  # * PROJECT_NAME
+  configure_package_config_file(CMake/curl-config.cmake.in
+          "${project_config}"
+          INSTALL_DESTINATION ${CURL_INSTALL_CMAKE_DIR}
+  )
+
+  if(CURL_ENABLE_EXPORT_TARGET)
+    install(
+            EXPORT "${TARGETS_EXPORT_NAME}"
+            NAMESPACE "${PROJECT_NAME}::"
+            DESTINATION ${CURL_INSTALL_CMAKE_DIR}
+    )
+  endif()
 
-if(CURL_ENABLE_EXPORT_TARGET)
   install(
-          EXPORT "${TARGETS_EXPORT_NAME}"
-          NAMESPACE "${PROJECT_NAME}::"
+          FILES ${version_config} ${project_config}
           DESTINATION ${CURL_INSTALL_CMAKE_DIR}
   )
-endif()
-
-install(
-        FILES ${version_config} ${project_config}
-        DESTINATION ${CURL_INSTALL_CMAKE_DIR}
-)
 
-# Workaround for MSVS10 to avoid the Dialog Hell
-# FIXME: This could be removed with future version of CMake.
-if(MSVC_VERSION EQUAL 1600)
-  set(CURL_SLN_FILENAME "${CMAKE_CURRENT_BINARY_DIR}/CURL.sln")
-  if(EXISTS "${CURL_SLN_FILENAME}")
-    file(APPEND "${CURL_SLN_FILENAME}" "\n# This should be regenerated!\n")
+  # Workaround for MSVS10 to avoid the Dialog Hell
+  # FIXME: This could be removed with future version of CMake.
+  if(MSVC_VERSION EQUAL 1600)
+    set(CURL_SLN_FILENAME "${CMAKE_CURRENT_BINARY_DIR}/CURL.sln")
+    if(EXISTS "${CURL_SLN_FILENAME}")
+      file(APPEND "${CURL_SLN_FILENAME}" "\n# This should be regenerated!\n")
+    endif()
   endif()
-endif()
 
-if(NOT TARGET curl_uninstall)
-  configure_file(
-      ${CMAKE_CURRENT_SOURCE_DIR}/CMake/cmake_uninstall.cmake.in
-      ${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake
-      IMMEDIATE @ONLY)
+  if(NOT TARGET curl_uninstall)
+    configure_file(
+        ${CMAKE_CURRENT_SOURCE_DIR}/CMake/cmake_uninstall.cmake.in
+        ${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake
+        IMMEDIATE @ONLY)
 
-  add_custom_target(curl_uninstall
-      COMMAND ${CMAKE_COMMAND} -P
-      ${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake)
+    add_custom_target(curl_uninstall
+        COMMAND ${CMAKE_COMMAND} -P
+        ${CMAKE_CURRENT_BINARY_DIR}/CMake/cmake_uninstall.cmake)
+  endif()
 endif()

+ 9 - 17
include/curl/curl.h

@@ -53,28 +53,19 @@
 #include "curlver.h"         /* libcurl version defines   */
 #include "system.h"          /* determine things run-time */
 
-/*
- * Define CURL_WIN32 when build target is Win32 API
- */
-
-#if (defined(_WIN32) || defined(__WIN32__) || defined(WIN32)) &&        \
-  !defined(__SYMBIAN32__)
-#define CURL_WIN32
-#endif
-
 #include <stdio.h>
 #include <limits.h>
 
-#if (defined(__FreeBSD__) && (__FreeBSD__ >= 2)) || defined(__MidnightBSD__)
+#if defined(__FreeBSD__) || defined(__MidnightBSD__)
 /* Needed for __FreeBSD_version or __MidnightBSD_version symbol definition */
-#include <osreldate.h>
+#include <sys/param.h>
 #endif
 
 /* The include stuff here below is mainly for time_t! */
 #include <sys/types.h>
 #include <time.h>
 
-#if defined(CURL_WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__)
+#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__CYGWIN__)
 #if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H) || \
       defined(__LWIP_OPT_H__) || defined(LWIP_HDR_OPT_H))
 /* The check above prevents the winsock2 inclusion if winsock.h already was
@@ -88,7 +79,7 @@
    libc5-based Linux systems. Only include it on systems that are known to
    require it! */
 #if defined(_AIX) || defined(__NOVELL_LIBC__) || defined(__NetBSD__) || \
-    defined(__minix) || defined(__SYMBIAN32__) || defined(__INTEGRITY) || \
+    defined(__minix) || defined(__INTEGRITY) || \
     defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \
     defined(__CYGWIN__) || defined(AMIGA) || defined(__NuttX__) || \
    (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) || \
@@ -97,11 +88,11 @@
 #include <sys/select.h>
 #endif
 
-#if !defined(CURL_WIN32) && !defined(_WIN32_WCE)
+#if !defined(_WIN32) && !defined(_WIN32_WCE)
 #include <sys/socket.h>
 #endif
 
-#if !defined(CURL_WIN32)
+#if !defined(_WIN32)
 #include <sys/time.h>
 #endif
 
@@ -128,7 +119,7 @@ typedef void CURLSH;
 
 #ifdef CURL_STATICLIB
 #  define CURL_EXTERN
-#elif defined(CURL_WIN32) || defined(__SYMBIAN32__) || \
+#elif defined(_WIN32) || \
      (__has_declspec_attribute(dllexport) && \
       __has_declspec_attribute(dllimport))
 #  if defined(BUILDING_LIBCURL)
@@ -144,7 +135,7 @@ typedef void CURLSH;
 
 #ifndef curl_socket_typedef
 /* socket typedef */
-#if defined(CURL_WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H)
+#if defined(_WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H)
 typedef SOCKET curl_socket_t;
 #define CURL_SOCKET_BAD INVALID_SOCKET
 #else
@@ -3220,6 +3211,7 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask);
 #include "options.h"
 #include "header.h"
 #include "websockets.h"
+#include "mprintf.h"
 
 /* the typechecker doesn't work in C++ (yet) */
 #if defined(__GNUC__) && defined(__GNUC_MINOR__) && \

+ 3 - 3
include/curl/curlver.h

@@ -32,12 +32,12 @@
 
 /* This is the version number of the libcurl package from which this header
    file origins: */
-#define LIBCURL_VERSION "8.4.0-DEV"
+#define LIBCURL_VERSION "8.5.0-DEV"
 
 /* The numeric version number is also available "in parts" by using these
    defines: */
 #define LIBCURL_VERSION_MAJOR 8
-#define LIBCURL_VERSION_MINOR 4
+#define LIBCURL_VERSION_MINOR 5
 #define LIBCURL_VERSION_PATCH 0
 
 /* 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
    and needs it to contain the full number.
 */
-#define LIBCURL_VERSION_NUM 0x080400
+#define LIBCURL_VERSION_NUM 0x080500
 
 /*
  * This is the date and time when the full source package was created. The

+ 11 - 26
include/curl/system.h

@@ -141,29 +141,6 @@
 #    define CURL_TYPEOF_CURL_SOCKLEN_T int
 #  endif
 
-#elif defined(__SYMBIAN32__)
-#  if defined(__EABI__) /* Treat all ARM compilers equally */
-#    define CURL_TYPEOF_CURL_OFF_T     long long
-#    define CURL_FORMAT_CURL_OFF_T     "lld"
-#    define CURL_FORMAT_CURL_OFF_TU    "llu"
-#    define CURL_SUFFIX_CURL_OFF_T     LL
-#    define CURL_SUFFIX_CURL_OFF_TU    ULL
-#  elif defined(__CW32__)
-#    pragma longlong on
-#    define CURL_TYPEOF_CURL_OFF_T     long long
-#    define CURL_FORMAT_CURL_OFF_T     "lld"
-#    define CURL_FORMAT_CURL_OFF_TU    "llu"
-#    define CURL_SUFFIX_CURL_OFF_T     LL
-#    define CURL_SUFFIX_CURL_OFF_TU    ULL
-#  elif defined(__VC32__)
-#    define CURL_TYPEOF_CURL_OFF_T     __int64
-#    define CURL_FORMAT_CURL_OFF_T     "lld"
-#    define CURL_FORMAT_CURL_OFF_TU    "llu"
-#    define CURL_SUFFIX_CURL_OFF_T     LL
-#    define CURL_SUFFIX_CURL_OFF_TU    ULL
-#  endif
-#  define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int
-
 #elif defined(macintosh)
 #  include <ConditionalMacros.h>
 #  if TYPE_LONGLONG
@@ -201,9 +178,10 @@
 #  define CURL_TYPEOF_CURL_SOCKLEN_T int
 
 #elif defined(__MINGW32__)
+#  include <inttypes.h>
 #  define CURL_TYPEOF_CURL_OFF_T     long long
-#  define CURL_FORMAT_CURL_OFF_T     "I64d"
-#  define CURL_FORMAT_CURL_OFF_TU    "I64u"
+#  define CURL_FORMAT_CURL_OFF_T     PRId64
+#  define CURL_FORMAT_CURL_OFF_TU    PRIu64
 #  define CURL_SUFFIX_CURL_OFF_T     LL
 #  define CURL_SUFFIX_CURL_OFF_TU    ULL
 #  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
@@ -370,7 +348,14 @@
 /* ===================================== */
 
 #elif defined(_MSC_VER)
-#  if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
+#  if (_MSC_VER >= 1800)
+#    include <inttypes.h>
+#    define CURL_TYPEOF_CURL_OFF_T     __int64
+#    define CURL_FORMAT_CURL_OFF_T     PRId64
+#    define CURL_FORMAT_CURL_OFF_TU    PRIu64
+#    define CURL_SUFFIX_CURL_OFF_T     i64
+#    define CURL_SUFFIX_CURL_OFF_TU    ui64
+#  elif (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
 #    define CURL_TYPEOF_CURL_OFF_T     __int64
 #    define CURL_FORMAT_CURL_OFF_T     "I64d"
 #    define CURL_FORMAT_CURL_OFF_TU    "I64u"

+ 13 - 8
lib/CMakeLists.txt

@@ -47,20 +47,25 @@ if(USE_ARES)
   include_directories(${CARES_INCLUDE_DIR})
 endif()
 
-add_library(
-  curlu # special libcurlu library just for unittests
-  STATIC
-  EXCLUDE_FROM_ALL
-  ${HHEADERS} ${CSOURCES}
-)
-target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB)
+if(BUILD_TESTING)
+  add_library(
+    curlu # special libcurlu library just for unittests
+    STATIC
+    EXCLUDE_FROM_ALL
+    ${HHEADERS} ${CSOURCES}
+  )
+  target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB)
+endif()
 
 if(ENABLE_CURLDEBUG)
   # We must compile these sources separately to avoid memdebug.h redefinitions
   # applying to them.
   set_source_files_properties(memdebug.c curl_multibyte.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
 endif()
-target_link_libraries(curlu PRIVATE ${CURL_LIBS})
+
+if(BUILD_TESTING)
+  target_link_libraries(curlu PRIVATE ${CURL_LIBS})
+endif()
 
 transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")
 include(${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake)

+ 4 - 6
lib/altsvc.c

@@ -97,7 +97,7 @@ static struct altsvc *altsvc_createid(const char *srchost,
                                       unsigned int srcport,
                                       unsigned int dstport)
 {
-  struct altsvc *as = calloc(sizeof(struct altsvc), 1);
+  struct altsvc *as = calloc(1, sizeof(struct altsvc));
   size_t hlen;
   size_t dlen;
   if(!as)
@@ -123,15 +123,13 @@ static struct altsvc *altsvc_createid(const char *srchost,
     dlen -= 2;
   }
 
-  as->src.host = Curl_memdup(srchost, hlen + 1);
+  as->src.host = Curl_strndup(srchost, hlen);
   if(!as->src.host)
     goto error;
-  as->src.host[hlen] = 0;
 
-  as->dst.host = Curl_memdup(dsthost, dlen + 1);
+  as->dst.host = Curl_strndup(dsthost, dlen);
   if(!as->dst.host)
     goto error;
-  as->dst.host[dlen] = 0;
 
   as->src.alpnid = srcalpnid;
   as->dst.alpnid = dstalpnid;
@@ -301,7 +299,7 @@ static CURLcode altsvc_out(struct altsvc *as, FILE *fp)
  */
 struct altsvcinfo *Curl_altsvc_init(void)
 {
-  struct altsvcinfo *asi = calloc(sizeof(struct altsvcinfo), 1);
+  struct altsvcinfo *asi = calloc(1, sizeof(struct altsvcinfo));
   if(!asi)
     return NULL;
   Curl_llist_init(&asi->list, NULL);

+ 8 - 1
lib/arpa_telnet.h

@@ -56,12 +56,14 @@ static const char * const telnetoptions[]=
   "TERM SPEED",  "LFLOW",          "LINEMODE",      "XDISPLOC",
   "OLD-ENVIRON", "AUTHENTICATION", "ENCRYPT",       "NEW-ENVIRON"
 };
+#define CURL_TELOPT(x)    telnetoptions[x]
+#else
+#define CURL_TELOPT(x)    ""
 #endif
 
 #define CURL_TELOPT_MAXIMUM CURL_TELOPT_NEW_ENVIRON
 
 #define CURL_TELOPT_OK(x) ((x) <= CURL_TELOPT_MAXIMUM)
-#define CURL_TELOPT(x)    telnetoptions[x]
 
 #define CURL_NTELOPTS 40
 
@@ -103,7 +105,12 @@ static const char * const telnetcmds[]=
 
 #define CURL_TELCMD_OK(x) ( ((unsigned int)(x) >= CURL_TELCMD_MINIMUM) && \
                        ((unsigned int)(x) <= CURL_TELCMD_MAXIMUM) )
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
 #define CURL_TELCMD(x)    telnetcmds[(x)-CURL_TELCMD_MINIMUM]
+#else
+#define CURL_TELCMD(x)    ""
+#endif
 
 #endif /* CURL_DISABLE_TELNET */
 

+ 89 - 60
lib/asyn-ares.c

@@ -60,13 +60,13 @@
 #include "progress.h"
 #include "timediff.h"
 
-#  if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) &&   \
-  defined(WIN32)
-#    define CARES_STATICLIB
-#  endif
-#  include <ares.h>
-#  include <ares_version.h> /* really old c-ares didn't include this by
-                               itself */
+#if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) &&   \
+  defined(_WIN32)
+#  define CARES_STATICLIB
+#endif
+#include <ares.h>
+#include <ares_version.h> /* really old c-ares didn't include this by
+                             itself */
 
 #if ARES_VERSION >= 0x010500
 /* c-ares 1.5.0 or later, the callback proto is modified */
@@ -228,9 +228,9 @@ static void destroy_async_data(struct Curl_async *async);
 void Curl_resolver_cancel(struct Curl_easy *data)
 {
   DEBUGASSERT(data);
-  if(data->state.async.resolver)
-    ares_cancel((ares_channel)data->state.async.resolver);
-  destroy_async_data(&data->state.async);
+  if(data->conn->resolve_async.resolver)
+    ares_cancel((ares_channel)data->conn->resolve_async.resolver);
+  destroy_async_data(&data->conn->resolve_async);
 }
 
 /*
@@ -278,14 +278,14 @@ int Curl_resolver_getsock(struct Curl_easy *data,
   struct timeval timebuf;
   struct timeval *timeout;
   long milli;
-  int max = ares_getsock((ares_channel)data->state.async.resolver,
+  int max = ares_getsock((ares_channel)data->conn->resolve_async.resolver,
                          (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
 
   maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
   maxtime.tv_usec = 0;
 
-  timeout = ares_timeout((ares_channel)data->state.async.resolver, &maxtime,
-                         &timebuf);
+  timeout = ares_timeout((ares_channel)data->conn->resolve_async.resolver,
+                         &maxtime, &timebuf);
   milli = (long)curlx_tvtoms(timeout);
   if(milli == 0)
     milli += 10;
@@ -313,8 +313,8 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
   int i;
   int num = 0;
 
-  bitmask = ares_getsock((ares_channel)data->state.async.resolver, socks,
-                         ARES_GETSOCK_MAXNUM);
+  bitmask = ares_getsock((ares_channel)data->conn->resolve_async.resolver,
+                         socks, ARES_GETSOCK_MAXNUM);
 
   for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
     pfd[i].events = 0;
@@ -344,12 +344,12 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
   if(!nfds)
     /* Call ares_process() unconditionally here, even if we simply timed out
        above, as otherwise the ares name resolve won't timeout! */
-    ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD,
-                    ARES_SOCKET_BAD);
+    ares_process_fd((ares_channel)data->conn->resolve_async.resolver,
+                    ARES_SOCKET_BAD, ARES_SOCKET_BAD);
   else {
     /* move through the descriptors and ask for processing on them */
     for(i = 0; i < num; i++)
-      ares_process_fd((ares_channel)data->state.async.resolver,
+      ares_process_fd((ares_channel)data->conn->resolve_async.resolver,
                       (pfd[i].revents & (POLLRDNORM|POLLIN))?
                       pfd[i].fd:ARES_SOCKET_BAD,
                       (pfd[i].revents & (POLLWRNORM|POLLOUT))?
@@ -368,7 +368,7 @@ static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
 CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
                                    struct Curl_dns_entry **dns)
 {
-  struct thread_data *res = data->state.async.tdata;
+  struct thread_data *res = data->conn->resolve_async.tdata;
   CURLcode result = CURLE_OK;
 
   DEBUGASSERT(dns);
@@ -397,7 +397,7 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
        ARES_ECANCELLED synchronously for all pending responses.  This will
        leave us with res->num_pending == 0, which is perfect for the next
        block. */
-    ares_cancel((ares_channel)data->state.async.resolver);
+    ares_cancel((ares_channel)data->conn->resolve_async.resolver);
     DEBUGASSERT(res->num_pending == 0);
   }
 #endif
@@ -408,12 +408,12 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
        them */
     res->temp_ai = NULL;
 
-    if(!data->state.async.dns)
+    if(!data->conn->resolve_async.dns)
       result = Curl_resolver_error(data);
     else
-      *dns = data->state.async.dns;
+      *dns = data->conn->resolve_async.dns;
 
-    destroy_async_data(&data->state.async);
+    destroy_async_data(&data->conn->resolve_async);
   }
 
   return result;
@@ -464,7 +464,8 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
     store.tv_sec = itimeout/1000;
     store.tv_usec = (itimeout%1000)*1000;
 
-    tvp = ares_timeout((ares_channel)data->state.async.resolver, &store, &tv);
+    tvp = ares_timeout((ares_channel)data->conn->resolve_async.resolver,
+                       &store, &tv);
 
     /* use the timeout period ares returned to us above if less than one
        second is left, otherwise just use 1000ms to make sure the progress
@@ -478,7 +479,7 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
       return CURLE_UNRECOVERABLE_POLL;
     result = Curl_resolver_is_resolved(data, entry);
 
-    if(result || data->state.async.done)
+    if(result || data->conn->resolve_async.done)
       break;
 
     if(Curl_pgrsUpdate(data))
@@ -499,12 +500,12 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
   }
   if(result)
     /* failure, so we cancel the ares operation */
-    ares_cancel((ares_channel)data->state.async.resolver);
+    ares_cancel((ares_channel)data->conn->resolve_async.resolver);
 
   /* Operation complete, if the lookup was successful we now have the entry
      in the cache. */
   if(entry)
-    *entry = data->state.async.dns;
+    *entry = data->conn->resolve_async.dns;
 
   if(result)
     /* close the connection, since we can't return failure here without
@@ -571,12 +572,13 @@ static void query_completed_cb(void *arg,  /* (struct connectdata *) */
        be valid so only defer it when we know the 'status' says its fine! */
     return;
 
-  res = data->state.async.tdata;
+  res = data->conn->resolve_async.tdata;
   if(res) {
     res->num_pending--;
 
     if(CURL_ASYNC_SUCCESS == status) {
-      struct Curl_addrinfo *ai = Curl_he2ai(hostent, data->state.async.port);
+      struct Curl_addrinfo *ai = Curl_he2ai(hostent,
+                                            data->conn->resolve_async.port);
       if(ai) {
         compound_results(res, ai);
       }
@@ -727,14 +729,16 @@ static void addrinfo_cb(void *arg, int status, int timeouts,
                         struct ares_addrinfo *result)
 {
   struct Curl_easy *data = (struct Curl_easy *)arg;
-  struct thread_data *res = data->state.async.tdata;
-  (void)timeouts;
-  if(ARES_SUCCESS == status) {
-    res->temp_ai = ares2addr(result->nodes);
-    res->last_status = CURL_ASYNC_SUCCESS;
-    ares_freeaddrinfo(result);
+  if(data->conn) {
+    struct thread_data *res = data->conn->resolve_async.tdata;
+    (void)timeouts;
+    if(ARES_SUCCESS == status) {
+      res->temp_ai = ares2addr(result->nodes);
+      res->last_status = CURL_ASYNC_SUCCESS;
+      ares_freeaddrinfo(result);
+    }
+    res->num_pending--;
   }
-  res->num_pending--;
 }
 
 #endif
@@ -755,15 +759,15 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
   size_t namelen = strlen(hostname);
   *waitp = 0; /* default to synchronous response */
 
-  res = calloc(sizeof(struct thread_data) + namelen, 1);
+  res = calloc(1, sizeof(struct thread_data) + namelen);
   if(res) {
     strcpy(res->hostname, hostname);
-    data->state.async.hostname = res->hostname;
-    data->state.async.port = port;
-    data->state.async.done = FALSE;   /* not done */
-    data->state.async.status = 0;     /* clear */
-    data->state.async.dns = NULL;     /* clear */
-    data->state.async.tdata = res;
+    data->conn->resolve_async.hostname = res->hostname;
+    data->conn->resolve_async.port = port;
+    data->conn->resolve_async.done = FALSE;   /* not done */
+    data->conn->resolve_async.status = 0;     /* clear */
+    data->conn->resolve_async.dns = NULL;     /* clear */
+    data->conn->resolve_async.tdata = res;
 
     /* initial status - failed */
     res->last_status = ARES_ENOTFOUND;
@@ -793,8 +797,8 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
       hints.ai_flags = ARES_AI_NUMERICSERV;
       msnprintf(service, sizeof(service), "%d", port);
       res->num_pending = 1;
-      ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname,
-                       service, &hints, addrinfo_cb, data);
+      ares_getaddrinfo((ares_channel)data->conn->resolve_async.resolver,
+                       hostname, service, &hints, addrinfo_cb, data);
     }
 #else
 
@@ -804,10 +808,10 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
       res->num_pending = 2;
 
       /* areschannel is already setup in the Curl_open() function */
-      ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
-                          PF_INET, query_completed_cb, data);
-      ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
-                          PF_INET6, query_completed_cb, data);
+      ares_gethostbyname((ares_channel)data->conn->resolve_async.resolver,
+                          hostname, PF_INET, query_completed_cb, data);
+      ares_gethostbyname((ares_channel)data->conn->resolve_async.resolver,
+                          hostname, PF_INET6, query_completed_cb, data);
     }
     else
 #endif
@@ -815,7 +819,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
       res->num_pending = 1;
 
       /* areschannel is already setup in the Curl_open() function */
-      ares_gethostbyname((ares_channel)data->state.async.resolver,
+      ares_gethostbyname((ares_channel)data->conn->resolve_async.resolver,
                          hostname, PF_INET,
                          query_completed_cb, data);
     }
@@ -829,6 +833,7 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data,
                               char *servers)
 {
   CURLcode result = CURLE_NOT_BUILT_IN;
+  ares_channel channel, lchannel = NULL;
   int ares_result;
 
   /* If server is NULL or empty, this would purge all DNS servers
@@ -841,11 +846,23 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data,
     return CURLE_OK;
 
 #ifdef HAVE_CARES_SERVERS_CSV
+  if(data->conn)
+    channel = data->conn->resolve_async.resolver;
+  else {
+    /* we are called by setopt on a data without a connection (yet). In that
+     * case we set the value on a local instance for checking.
+     * The configured data options are set when the connection for this
+     * transfer is created. */
+    result = Curl_resolver_init(data, (void **)&lchannel);
+    if(result)
+      goto out;
+    channel = lchannel;
+  }
+
 #ifdef HAVE_CARES_PORTS_CSV
-  ares_result = ares_set_servers_ports_csv(data->state.async.resolver,
-                                           servers);
+  ares_result = ares_set_servers_ports_csv(channel, servers);
 #else
-  ares_result = ares_set_servers_csv(data->state.async.resolver, servers);
+  ares_result = ares_set_servers_csv(channel, servers);
 #endif
   switch(ares_result) {
   case ARES_SUCCESS:
@@ -861,6 +878,9 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data,
     result = CURLE_BAD_FUNCTION_ARGUMENT;
     break;
   }
+out:
+  if(lchannel)
+    Curl_resolver_cleanup(lchannel);
 #else /* too old c-ares version! */
   (void)data;
   (void)(ares_result);
@@ -872,11 +892,14 @@ CURLcode Curl_set_dns_interface(struct Curl_easy *data,
                                 const char *interf)
 {
 #ifdef HAVE_CARES_LOCAL_DEV
-  if(!interf)
-    interf = "";
-
-  ares_set_local_dev((ares_channel)data->state.async.resolver, interf);
+  if(data->conn) {
+    /* not a setopt test run, set the value */
+    if(!interf)
+      interf = "";
 
+    ares_set_local_dev((ares_channel)data->conn->resolve_async.resolver,
+                       interf);
+  }
   return CURLE_OK;
 #else /* c-ares version too old! */
   (void)data;
@@ -900,8 +923,11 @@ CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
     }
   }
 
-  ares_set_local_ip4((ares_channel)data->state.async.resolver,
-                     ntohl(a4.s_addr));
+  if(data->conn) {
+    /* not a setopt test run, set the value */
+    ares_set_local_ip4((ares_channel)data->conn->resolve_async.resolver,
+                       ntohl(a4.s_addr));
+  }
 
   return CURLE_OK;
 #else /* c-ares version too old! */
@@ -927,7 +953,10 @@ CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
     }
   }
 
-  ares_set_local_ip6((ares_channel)data->state.async.resolver, a6);
+  if(data->conn) {
+    /* not a setopt test run, set the value */
+    ares_set_local_ip6((ares_channel)data->conn->resolve_async.resolver, a6);
+  }
 
   return CURLE_OK;
 #else /* c-ares version too old! */

+ 25 - 25
lib/asyn-thread.c

@@ -136,7 +136,7 @@ static void destroy_async_data(struct Curl_async *);
  */
 void Curl_resolver_cancel(struct Curl_easy *data)
 {
-  destroy_async_data(&data->state.async);
+  destroy_async_data(&data->conn->resolve_async);
 }
 
 /* This function is used to init a threaded resolve */
@@ -173,7 +173,7 @@ struct thread_data {
 
 static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data)
 {
-  return &(data->state.async.tdata->tsd);
+  return &(data->conn->resolve_async.tdata->tsd);
 }
 
 /* Destroy resolver thread synchronization data */
@@ -196,7 +196,7 @@ void destroy_thread_sync_data(struct thread_sync_data *tsd)
    * the other end (for reading) is always closed in the parent thread.
    */
   if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
-    sclose(tsd->sock_pair[1]);
+    wakeup_close(tsd->sock_pair[1]);
   }
 #endif
   memset(tsd, 0, sizeof(*tsd));
@@ -233,8 +233,8 @@ int init_thread_sync_data(struct thread_data *td,
   Curl_mutex_init(tsd->mtx);
 
 #ifndef CURL_DISABLE_SOCKETPAIR
-  /* create socket pair, avoid AF_LOCAL since it doesn't build on Solaris */
-  if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &tsd->sock_pair[0]) < 0) {
+  /* create socket pair or pipe */
+  if(wakeup_create(&tsd->sock_pair[0]) < 0) {
     tsd->sock_pair[0] = CURL_SOCKET_BAD;
     tsd->sock_pair[1] = CURL_SOCKET_BAD;
     goto err_exit;
@@ -254,7 +254,7 @@ int init_thread_sync_data(struct thread_data *td,
 err_exit:
 #ifndef CURL_DISABLE_SOCKETPAIR
   if(tsd->sock_pair[0] != CURL_SOCKET_BAD) {
-    sclose(tsd->sock_pair[0]);
+    wakeup_close(tsd->sock_pair[0]);
     tsd->sock_pair[0] = CURL_SOCKET_BAD;
   }
 #endif
@@ -320,7 +320,7 @@ static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
     if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
       /* DNS has been resolved, signal client task */
       buf[0] = 1;
-      if(swrite(tsd->sock_pair[1],  buf, sizeof(buf)) < 0) {
+      if(wakeup_write(tsd->sock_pair[1],  buf, sizeof(buf)) < 0) {
         /* update sock_erro to errno */
         tsd->sock_error = SOCKERRNO;
       }
@@ -428,9 +428,9 @@ static bool init_resolve_thread(struct Curl_easy *data,
 {
   struct thread_data *td = calloc(1, sizeof(struct thread_data));
   int err = ENOMEM;
-  struct Curl_async *asp = &data->state.async;
+  struct Curl_async *asp = &data->conn->resolve_async;
 
-  data->state.async.tdata = td;
+  data->conn->resolve_async.tdata = td;
   if(!td)
     goto errno_exit;
 
@@ -488,7 +488,7 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data,
   CURLcode result = CURLE_OK;
 
   DEBUGASSERT(data);
-  td = data->state.async.tdata;
+  td = data->conn->resolve_async.tdata;
   DEBUGASSERT(td);
   DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
 
@@ -500,18 +500,18 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data,
   else
     DEBUGASSERT(0);
 
-  data->state.async.done = TRUE;
+  data->conn->resolve_async.done = TRUE;
 
   if(entry)
-    *entry = data->state.async.dns;
+    *entry = data->conn->resolve_async.dns;
 
-  if(!data->state.async.dns && report)
+  if(!data->conn->resolve_async.dns && report)
     /* a name was not resolved, report error */
     result = Curl_resolver_error(data);
 
-  destroy_async_data(&data->state.async);
+  destroy_async_data(&data->conn->resolve_async);
 
-  if(!data->state.async.dns && report)
+  if(!data->conn->resolve_async.dns && report)
     connclose(data->conn, "asynch resolve failed");
 
   return result;
@@ -524,7 +524,7 @@ static CURLcode thread_wait_resolv(struct Curl_easy *data,
  */
 void Curl_resolver_kill(struct Curl_easy *data)
 {
-  struct thread_data *td = data->state.async.tdata;
+  struct thread_data *td = data->conn->resolve_async.tdata;
 
   /* 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
@@ -563,7 +563,7 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
 CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
                                    struct Curl_dns_entry **entry)
 {
-  struct thread_data *td = data->state.async.tdata;
+  struct thread_data *td = data->conn->resolve_async.tdata;
   int done = 0;
 
   DEBUGASSERT(entry);
@@ -581,13 +581,13 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
   if(done) {
     getaddrinfo_complete(data);
 
-    if(!data->state.async.dns) {
+    if(!data->conn->resolve_async.dns) {
       CURLcode result = Curl_resolver_error(data);
-      destroy_async_data(&data->state.async);
+      destroy_async_data(&data->conn->resolve_async);
       return result;
     }
-    destroy_async_data(&data->state.async);
-    *entry = data->state.async.dns;
+    destroy_async_data(&data->conn->resolve_async);
+    *entry = data->conn->resolve_async.dns;
   }
   else {
     /* poll for name lookup done with exponential backoff up to 250ms */
@@ -619,9 +619,9 @@ int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
   int ret_val = 0;
   timediff_t milli;
   timediff_t ms;
-  struct resdata *reslv = (struct resdata *)data->state.async.resolver;
+  struct resdata *reslv = (struct resdata *)data->conn->resolve_async.resolver;
 #ifndef CURL_DISABLE_SOCKETPAIR
-  struct thread_data *td = data->state.async.tdata;
+  struct thread_data *td = data->conn->resolve_async.tdata;
 #else
   (void)socks;
 #endif
@@ -662,7 +662,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
                                                 int port,
                                                 int *waitp)
 {
-  struct resdata *reslv = (struct resdata *)data->state.async.resolver;
+  struct resdata *reslv = (struct resdata *)data->conn->resolve_async.resolver;
 
   *waitp = 0; /* default to synchronous response */
 
@@ -691,7 +691,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
 {
   struct addrinfo hints;
   int pf = PF_INET;
-  struct resdata *reslv = (struct resdata *)data->state.async.resolver;
+  struct resdata *reslv = (struct resdata *)data->conn->resolve_async.resolver;
 
   *waitp = 0; /* default to synchronous response */
 

+ 1 - 0
lib/base64.c

@@ -31,6 +31,7 @@
   !defined(CURL_DISABLE_SMTP) || \
   !defined(CURL_DISABLE_POP3) || \
   !defined(CURL_DISABLE_IMAP) || \
+  !defined(CURL_DISABLE_DIGEST_AUTH) || \
   !defined(CURL_DISABLE_DOH) || defined(USE_SSL) || defined(BUILDING_CURL)
 #include "curl/curl.h"
 #include "warnless.h"

+ 38 - 68
lib/c-hyper.c

@@ -22,6 +22,10 @@
  *
  ***************************************************************************/
 
+/* Curl's integration with Hyper. This replaces certain functions in http.c,
+ * based on configuration #defines. This implementation supports HTTP/1.1 but
+ * not HTTP/2.
+ */
 #include "curl_setup.h"
 
 #if !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER)
@@ -172,17 +176,15 @@ static int hyper_each_header(void *userdata,
 
   Curl_debug(data, CURLINFO_HEADER_IN, headp, len);
 
-  if(!data->state.hconnect || !data->set.suppress_connect_headers) {
-    writetype = CLIENTWRITE_HEADER;
-    if(data->state.hconnect)
-      writetype |= CLIENTWRITE_CONNECT;
-    if(data->req.httpcode/100 == 1)
-      writetype |= CLIENTWRITE_1XX;
-    result = Curl_client_write(data, writetype, headp, len);
-    if(result) {
-      data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
-      return HYPER_ITER_BREAK;
-    }
+  writetype = CLIENTWRITE_HEADER;
+  if(data->state.hconnect)
+    writetype |= CLIENTWRITE_CONNECT;
+  if(data->req.httpcode/100 == 1)
+    writetype |= CLIENTWRITE_1XX;
+  result = Curl_client_write(data, writetype, headp, len);
+  if(result) {
+    data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
+    return HYPER_ITER_BREAK;
   }
 
   result = Curl_bump_headersize(data, len, FALSE);
@@ -201,7 +203,7 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk)
   struct SingleRequest *k = &data->req;
   CURLcode result = CURLE_OK;
 
-  if(0 == k->bodywrites++) {
+  if(0 == k->bodywrites) {
     bool done = FALSE;
 #if defined(USE_NTLM)
     struct connectdata *conn = data->conn;
@@ -241,11 +243,6 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk)
       return HYPER_ITER_BREAK;
     }
   }
-  if(k->ignorebody)
-    return HYPER_ITER_CONTINUE;
-  if(0 == len)
-    return HYPER_ITER_CONTINUE;
-  Curl_debug(data, CURLINFO_DATA_IN, buf, len);
   result = Curl_client_write(data, CLIENTWRITE_BODY, buf, len);
 
   if(result) {
@@ -253,12 +250,6 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk)
     return HYPER_ITER_BREAK;
   }
 
-  data->req.bytecount += len;
-  result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
-  if(result) {
-    data->state.hresult = result;
-    return HYPER_ITER_BREAK;
-  }
   return HYPER_ITER_CONTINUE;
 }
 
@@ -310,13 +301,14 @@ static CURLcode status_line(struct Curl_easy *data,
   Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&data->state.headerb),
              len);
 
-  if(!data->state.hconnect || !data->set.suppress_connect_headers) {
-    writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS;
-    result = Curl_client_write(data, writetype,
-                               Curl_dyn_ptr(&data->state.headerb), len);
-    if(result)
-      return result;
-  }
+  writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS;
+  if(data->state.hconnect)
+    writetype |= CLIENTWRITE_CONNECT;
+  result = Curl_client_write(data, writetype,
+                             Curl_dyn_ptr(&data->state.headerb), len);
+  if(result)
+    return result;
+
   result = Curl_bump_headersize(data, len, FALSE);
   return result;
 }
@@ -551,11 +543,9 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
 
 static CURLcode debug_request(struct Curl_easy *data,
                               const char *method,
-                              const char *path,
-                              bool h2)
+                              const char *path)
 {
-  char *req = aprintf("%s %s HTTP/%s\r\n", method, path,
-                      h2?"2":"1.1");
+  char *req = aprintf("%s %s HTTP/1.1\r\n", method, path);
   if(!req)
     return CURLE_OUT_OF_MEMORY;
   Curl_debug(data, CURLINFO_HEADER_OUT, req, strlen(req));
@@ -637,7 +627,6 @@ CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers,
 static CURLcode request_target(struct Curl_easy *data,
                                struct connectdata *conn,
                                const char *method,
-                               bool h2,
                                hyper_request *req)
 {
   CURLcode result;
@@ -649,26 +638,13 @@ static CURLcode request_target(struct Curl_easy *data,
   if(result)
     return result;
 
-  if(h2 && hyper_request_set_uri_parts(req,
-                                       /* scheme */
-                                       (uint8_t *)data->state.up.scheme,
-                                       strlen(data->state.up.scheme),
-                                       /* authority */
-                                       (uint8_t *)conn->host.name,
-                                       strlen(conn->host.name),
-                                       /* path_and_query */
-                                       (uint8_t *)Curl_dyn_uptr(&r),
-                                       Curl_dyn_len(&r))) {
-    failf(data, "error setting uri parts to hyper");
-    result = CURLE_OUT_OF_MEMORY;
-  }
-  else if(!h2 && hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r),
+  if(hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r),
                                        Curl_dyn_len(&r))) {
     failf(data, "error setting uri to hyper");
     result = CURLE_OUT_OF_MEMORY;
   }
   else
-    result = debug_request(data, method, Curl_dyn_ptr(&r), h2);
+    result = debug_request(data, method, Curl_dyn_ptr(&r));
 
   Curl_dyn_free(&r);
 
@@ -899,7 +875,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   const char *p_accept; /* Accept: string */
   const char *method;
   Curl_HttpReq httpreq;
-  bool h2 = FALSE;
   const char *te = NULL; /* transfer-encoding */
   hyper_code rc;
 
@@ -907,6 +882,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
      may be parts of the request that is not yet sent, since we can deal with
      the rest of the request in the PERFORM phase. */
   *done = TRUE;
+  Curl_client_cleanup(data);
 
   infof(data, "Time for the Hyper dance");
   memset(h, 0, sizeof(struct hyptransfer));
@@ -917,6 +893,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
 
   Curl_http_method(data, conn, &method, &httpreq);
 
+  DEBUGASSERT(data->req.bytecount ==  0);
+
   /* setup the authentication headers */
   {
     char *pq = NULL;
@@ -972,8 +950,9 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     goto error;
   }
   if(conn->alpn == CURL_HTTP_VERSION_2) {
-    hyper_clientconn_options_http2(options, 1);
-    h2 = TRUE;
+    failf(data, "ALPN protocol h2 not supported with Hyper");
+    result = CURLE_UNSUPPORTED_PROTOCOL;
+    goto error;
   }
   hyper_clientconn_options_set_preserve_header_case(options, 1);
   hyper_clientconn_options_set_preserve_header_order(options, 1);
@@ -1024,7 +1003,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     }
   }
   else {
-    if(!h2 && !data->state.disableexpect) {
+    if(!data->state.disableexpect) {
       data->state.expect100header = TRUE;
     }
   }
@@ -1035,7 +1014,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     goto error;
   }
 
-  result = request_target(data, conn, method, h2, req);
+  result = request_target(data, conn, method, req);
   if(result)
     goto error;
 
@@ -1056,19 +1035,10 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   if(result)
     goto error;
 
-  if(!h2) {
-    if(data->state.aptr.host) {
-      result = Curl_hyper_header(data, headers, data->state.aptr.host);
-      if(result)
-        goto error;
-    }
-  }
-  else {
-    /* For HTTP/2, we show the Host: header as if we sent it, to make it look
-       like for HTTP/1 but it isn't actually sent since :authority is then
-       used. */
-    Curl_debug(data, CURLINFO_HEADER_OUT, data->state.aptr.host,
-               strlen(data->state.aptr.host));
+  if(data->state.aptr.host) {
+    result = Curl_hyper_header(data, headers, data->state.aptr.host);
+    if(result)
+      goto error;
   }
 
   if(data->state.aptr.proxyuserpwd) {

+ 25 - 30
lib/cf-h1-proxy.c

@@ -374,7 +374,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
   curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
   char *linep;
   size_t perline;
-  int error;
+  int error, writetype;
 
 #define SELECT_OK      0
 #define SELECT_ERROR   1
@@ -386,12 +386,12 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
     return CURLE_OK;
 
   while(ts->keepon) {
-    ssize_t gotbytes;
+    ssize_t nread;
     char byte;
 
     /* Read one byte at a time to avoid a race condition. Wait at most one
        second before looping to ensure continuous pgrsUpdates. */
-    result = Curl_read(data, tunnelsocket, &byte, 1, &gotbytes);
+    result = Curl_read(data, tunnelsocket, &byte, 1, &nread);
     if(result == CURLE_AGAIN)
       /* socket buffer drained, return */
       return CURLE_OK;
@@ -404,7 +404,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
       break;
     }
 
-    if(gotbytes <= 0) {
+    if(nread <= 0) {
       if(data->set.proxyauth && data->state.authproxy.avail &&
          data->state.aptr.proxyuserpwd) {
         /* proxy auth was requested and there was proxy auth available,
@@ -437,11 +437,11 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
            properly to know when the end of the body is reached */
         CHUNKcode r;
         CURLcode extra;
-        ssize_t tookcareof = 0;
+        size_t consumed = 0;
 
         /* now parse the chunked piece of data so that we can
            properly tell when the stream ends */
-        r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra);
+        r = Curl_httpchunk_read(data, &byte, 1, &consumed, &extra);
         if(r == CHUNKE_STOP) {
           /* we're done reading chunks! */
           infof(data, "chunk reading DONE");
@@ -467,15 +467,12 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
     /* output debug if that is requested */
     Curl_debug(data, CURLINFO_HEADER_IN, linep, perline);
 
-    if(!data->set.suppress_connect_headers) {
-      /* send the header to the callback */
-      int writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
-        (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
-
-      result = Curl_client_write(data, writetype, linep, perline);
-      if(result)
-        return result;
-    }
+    /* send the header to the callback */
+    writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
+      (ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
+    result = Curl_client_write(data, writetype, linep, perline);
+    if(result)
+      return result;
 
     result = Curl_bump_headersize(data, perline, TRUE);
     if(result)
@@ -502,6 +499,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
         else if(ts->chunked_encoding) {
           CHUNKcode r;
           CURLcode extra;
+          size_t consumed = 0;
 
           infof(data, "Ignore chunked response-body");
 
@@ -516,8 +514,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
 
           /* now parse the chunked piece of data so that we can properly
              tell when the stream ends */
-          r = Curl_httpchunk_read(data, linep + 1, 1, &gotbytes,
-                                  &extra);
+          r = Curl_httpchunk_read(data, linep + 1, 1, &consumed, &extra);
           if(r == CHUNKE_STOP) {
             /* we're done reading chunks! */
             infof(data, "chunk reading DONE");
@@ -1038,31 +1035,29 @@ out:
   return result;
 }
 
-static int cf_h1_proxy_get_select_socks(struct Curl_cfilter *cf,
+static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
                                         struct Curl_easy *data,
-                                        curl_socket_t *socks)
+                                        struct easy_pollset *ps)
 {
   struct h1_tunnel_state *ts = cf->ctx;
-  int fds;
 
-  fds = cf->next->cft->get_select_socks(cf->next, data, socks);
-  if(!fds && cf->next->connected && !cf->connected) {
+  if(!cf->connected) {
     /* If we are not connected, but the filter "below" is
      * and not waiting on something, we are tunneling. */
-    socks[0] = Curl_conn_cf_get_socket(cf, data);
+    curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
     if(ts) {
       /* when we've sent a CONNECT to a proxy, we should rather either
          wait for the socket to become readable to be able to get the
          response headers or if we're still sending the request, wait
          for write. */
-      if(ts->CONNECT.sending == HTTPSEND_REQUEST) {
-        return GETSOCK_WRITESOCK(0);
-      }
-      return GETSOCK_READSOCK(0);
+      if(ts->CONNECT.sending == HTTPSEND_REQUEST)
+        Curl_pollset_set_out_only(data, ps, sock);
+      else
+        Curl_pollset_set_in_only(data, ps, sock);
     }
-    return GETSOCK_WRITESOCK(0);
+    else
+      Curl_pollset_set_out_only(data, ps, sock);
   }
-  return fds;
 }
 
 static void cf_h1_proxy_destroy(struct Curl_cfilter *cf,
@@ -1093,7 +1088,7 @@ struct Curl_cftype Curl_cft_h1_proxy = {
   cf_h1_proxy_connect,
   cf_h1_proxy_close,
   Curl_cf_http_proxy_get_host,
-  cf_h1_proxy_get_select_socks,
+  cf_h1_proxy_adjust_pollset,
   Curl_cf_def_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_recv,

+ 27 - 40
lib/cf-h2-proxy.c

@@ -688,12 +688,8 @@ static int proxy_h2_on_frame_recv(nghttp2_session *session,
        * window and *assume* that we treat this like a WINDOW_UPDATE. Some
        * servers send an explicit WINDOW_UPDATE, but not all seem to do that.
        * To be safe, we UNHOLD a stream in order not to stall. */
-      if((data->req.keepon & KEEP_SEND_HOLD) &&
-         (data->req.keepon & KEEP_SEND)) {
-        data->req.keepon &= ~KEEP_SEND_HOLD;
+      if(CURL_WANT_SEND(data)) {
         drain_tunnel(cf, data, &ctx->tunnel);
-        CURL_TRC_CF(data, cf, "[%d] un-holding after SETTINGS",
-                    stream_id);
       }
       break;
     case NGHTTP2_GOAWAY:
@@ -727,12 +723,8 @@ static int proxy_h2_on_frame_recv(nghttp2_session *session,
     }
     break;
   case NGHTTP2_WINDOW_UPDATE:
-    if((data->req.keepon & KEEP_SEND_HOLD) &&
-       (data->req.keepon & KEEP_SEND)) {
-      data->req.keepon &= ~KEEP_SEND_HOLD;
-      Curl_expire(data, 0, EXPIRE_RUN_NOW);
-      CURL_TRC_CF(data, cf, "[%d] unpausing after win update",
-                  stream_id);
+    if(CURL_WANT_SEND(data)) {
+      drain_tunnel(cf, data, &ctx->tunnel);
     }
     break;
   default:
@@ -909,7 +901,6 @@ static CURLcode proxy_h2_submit(int32_t *pstream_id,
 {
   struct dynhds h2_headers;
   nghttp2_nv *nva = NULL;
-  unsigned int i;
   int32_t stream_id = -1;
   size_t nheader;
   CURLcode result;
@@ -920,22 +911,12 @@ static CURLcode proxy_h2_submit(int32_t *pstream_id,
   if(result)
     goto out;
 
-  nheader = Curl_dynhds_count(&h2_headers);
-  nva = malloc(sizeof(nghttp2_nv) * nheader);
+  nva = Curl_dynhds_to_nva(&h2_headers, &nheader);
   if(!nva) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
   }
 
-  for(i = 0; i < nheader; ++i) {
-    struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
-    nva[i].name = (unsigned char *)e->name;
-    nva[i].namelen = e->namelen;
-    nva[i].value = (unsigned char *)e->value;
-    nva[i].valuelen = e->valuelen;
-    nva[i].flags = NGHTTP2_NV_FLAG_NONE;
-  }
-
   if(read_callback) {
     nghttp2_data_provider data_prd;
 
@@ -1187,25 +1168,31 @@ static bool cf_h2_proxy_data_pending(struct Curl_cfilter *cf,
   return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
 }
 
-static int cf_h2_proxy_get_select_socks(struct Curl_cfilter *cf,
-                                        struct Curl_easy *data,
-                                        curl_socket_t *sock)
+static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf,
+                                       struct Curl_easy *data,
+                                       struct easy_pollset *ps)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
-  int bitmap = GETSOCK_BLANK;
-  struct cf_call_data save;
-
-  CF_DATA_SAVE(save, cf, data);
-  sock[0] = Curl_conn_cf_get_socket(cf, data);
-  bitmap |= GETSOCK_READSOCK(0);
+  curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
+  bool want_recv, want_send;
 
-  /* HTTP/2 layer wants to send data) AND there's a window to send data in */
-  if(nghttp2_session_want_write(ctx->h2) &&
-     nghttp2_session_get_remote_window_size(ctx->h2))
-    bitmap |= GETSOCK_WRITESOCK(0);
+  Curl_pollset_check(data, ps, sock, &want_recv, &want_send);
+  if(ctx->h2 && (want_recv || want_send)) {
+    struct cf_call_data save;
+    bool c_exhaust, s_exhaust;
 
-  CF_DATA_RESTORE(cf, save);
-  return bitmap;
+    CF_DATA_SAVE(save, cf, data);
+    c_exhaust = !nghttp2_session_get_remote_window_size(ctx->h2);
+    s_exhaust = ctx->tunnel.stream_id >= 0 &&
+                !nghttp2_session_get_stream_remote_window_size(
+                   ctx->h2, ctx->tunnel.stream_id);
+    want_recv = (want_recv || c_exhaust || s_exhaust);
+    want_send = (!s_exhaust && want_send) ||
+                (!c_exhaust && nghttp2_session_want_write(ctx->h2));
+
+    Curl_pollset_set(data, ps, sock, want_recv, want_send);
+    CF_DATA_RESTORE(cf, save);
+  }
 }
 
 static ssize_t h2_handle_tunnel_close(struct Curl_cfilter *cf,
@@ -1542,7 +1529,7 @@ struct Curl_cftype Curl_cft_h2_proxy = {
   cf_h2_proxy_connect,
   cf_h2_proxy_close,
   Curl_cf_http_proxy_get_host,
-  cf_h2_proxy_get_select_socks,
+  cf_h2_proxy_adjust_pollset,
   cf_h2_proxy_data_pending,
   cf_h2_proxy_send,
   cf_h2_proxy_recv,
@@ -1560,7 +1547,7 @@ CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf,
   CURLcode result = CURLE_OUT_OF_MEMORY;
 
   (void)data;
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx)
     goto out;
 

+ 7 - 13
lib/cf-haproxy.c

@@ -171,23 +171,17 @@ static void cf_haproxy_close(struct Curl_cfilter *cf,
     cf->next->cft->do_close(cf->next, data);
 }
 
-static int cf_haproxy_get_select_socks(struct Curl_cfilter *cf,
-                                       struct Curl_easy *data,
-                                       curl_socket_t *socks)
+static void cf_haproxy_adjust_pollset(struct Curl_cfilter *cf,
+                                      struct Curl_easy *data,
+                                      struct easy_pollset *ps)
 {
-  int fds;
-
-  fds = cf->next->cft->get_select_socks(cf->next, data, socks);
-  if(!fds && cf->next->connected && !cf->connected) {
+  if(cf->next->connected && !cf->connected) {
     /* If we are not connected, but the filter "below" is
      * and not waiting on something, we are sending. */
-    socks[0] = Curl_conn_cf_get_socket(cf, data);
-    return GETSOCK_WRITESOCK(0);
+    Curl_pollset_set_out_only(data, ps, Curl_conn_cf_get_socket(cf, data));
   }
-  return fds;
 }
 
-
 struct Curl_cftype Curl_cft_haproxy = {
   "HAPROXY",
   0,
@@ -196,7 +190,7 @@ struct Curl_cftype Curl_cft_haproxy = {
   cf_haproxy_connect,
   cf_haproxy_close,
   Curl_cf_def_get_host,
-  cf_haproxy_get_select_socks,
+  cf_haproxy_adjust_pollset,
   Curl_cf_def_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_recv,
@@ -214,7 +208,7 @@ static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf,
   CURLcode result;
 
   (void)data;
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;

+ 17 - 37
lib/cf-https-connect.c

@@ -188,9 +188,6 @@ static CURLcode baller_connected(struct Curl_cfilter *cf,
 #endif
     infof(data, "using HTTP/2");
     break;
-  case CURL_HTTP_VERSION_1_1:
-    infof(data, "using HTTP/1.1");
-    break;
   default:
     infof(data, "using HTTP/1.x");
     break;
@@ -325,42 +322,25 @@ out:
   return result;
 }
 
-static int cf_hc_get_select_socks(struct Curl_cfilter *cf,
+static void cf_hc_adjust_pollset(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
-                                  curl_socket_t *socks)
+                                  struct easy_pollset *ps)
 {
-  struct cf_hc_ctx *ctx = cf->ctx;
-  size_t i, j, s;
-  int brc, rc = GETSOCK_BLANK;
-  curl_socket_t bsocks[MAX_SOCKSPEREASYHANDLE];
-  struct cf_hc_baller *ballers[2];
-
-  if(cf->connected)
-    return cf->next->cft->get_select_socks(cf->next, data, socks);
-
-  ballers[0] = &ctx->h3_baller;
-  ballers[1] = &ctx->h21_baller;
-  for(i = s = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) {
-    struct cf_hc_baller *b = ballers[i];
-    if(!cf_hc_baller_is_active(b))
-      continue;
-    brc = Curl_conn_cf_get_select_socks(b->cf, data, bsocks);
-    CURL_TRC_CF(data, cf, "get_selected_socks(%s) -> %x", b->name, brc);
-    if(!brc)
-      continue;
-    for(j = 0; j < MAX_SOCKSPEREASYHANDLE && s < MAX_SOCKSPEREASYHANDLE; ++j) {
-      if((brc & GETSOCK_WRITESOCK(j)) || (brc & GETSOCK_READSOCK(j))) {
-        socks[s] = bsocks[j];
-        if(brc & GETSOCK_WRITESOCK(j))
-          rc |= GETSOCK_WRITESOCK(s);
-        if(brc & GETSOCK_READSOCK(j))
-          rc |= GETSOCK_READSOCK(s);
-        s++;
-      }
+  if(!cf->connected) {
+    struct cf_hc_ctx *ctx = cf->ctx;
+    struct cf_hc_baller *ballers[2];
+    size_t i;
+
+    ballers[0] = &ctx->h3_baller;
+    ballers[1] = &ctx->h21_baller;
+    for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) {
+      struct cf_hc_baller *b = ballers[i];
+      if(!cf_hc_baller_is_active(b))
+        continue;
+      Curl_conn_cf_adjust_pollset(b->cf, data, ps);
     }
+    CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num);
   }
-  CURL_TRC_CF(data, cf, "get_selected_socks -> %x", rc);
-  return rc;
 }
 
 static bool cf_hc_data_pending(struct Curl_cfilter *cf,
@@ -455,7 +435,7 @@ struct Curl_cftype Curl_cft_http_connect = {
   cf_hc_connect,
   cf_hc_close,
   Curl_cf_def_get_host,
-  cf_hc_get_select_socks,
+  cf_hc_adjust_pollset,
   cf_hc_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_recv,
@@ -475,7 +455,7 @@ static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
   CURLcode result = CURLE_OK;
 
   (void)data;
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;

+ 39 - 54
lib/cf-socket.c

@@ -81,7 +81,7 @@
 #include "memdebug.h"
 
 
-#if defined(ENABLE_IPV6) && defined(IPV6_V6ONLY) && defined(WIN32)
+#if defined(ENABLE_IPV6) && defined(IPV6_V6ONLY) && defined(_WIN32)
 /* It makes support for IPv4-mapped IPv6 addresses.
  * Linux kernel, NetBSD, FreeBSD and Darwin: default is off;
  * Windows Vista and later: default is on;
@@ -102,11 +102,7 @@ static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
 #if defined(TCP_NODELAY)
   curl_socklen_t onoff = (curl_socklen_t) 1;
   int level = IPPROTO_TCP;
-#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
   char buffer[STRERROR_LEN];
-#else
-  (void) data;
-#endif
 
   if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
                 sizeof(onoff)) < 0)
@@ -127,6 +123,7 @@ static void nosigpipe(struct Curl_easy *data,
                       curl_socket_t sockfd)
 {
   int onoff = 1;
+  (void)data;
   if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
                 sizeof(onoff)) < 0) {
 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
@@ -662,7 +659,7 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
   int err = 0;
   curl_socklen_t errSize = sizeof(err);
 
-#ifdef WIN32
+#ifdef _WIN32
   /*
    * In October 2003 we effectively nullified this function on Windows due to
    * problems with it using all CPU in multi-threaded cases.
@@ -883,34 +880,14 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
   struct cf_socket_ctx *ctx = cf->ctx;
 
   if(ctx && CURL_SOCKET_BAD != ctx->sock) {
-    if(ctx->active) {
-      /* We share our socket at cf->conn->sock[cf->sockindex] when active.
-       * If it is no longer there, someone has stolen (and hopefully
-       * closed it) and we just forget about it.
-       */
-      if(ctx->sock == cf->conn->sock[cf->sockindex]) {
-        CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
-                    ", active)", ctx->sock);
-        socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
-        cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
-      }
-      else {
-        CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
-                    ") no longer at conn->sock[], discarding", ctx->sock);
-        /* TODO: we do not want this to happen. Need to check which
-         * code is messing with conn->sock[cf->sockindex] */
-      }
-      ctx->sock = CURL_SOCKET_BAD;
-      if(cf->sockindex == FIRSTSOCKET)
-        cf->conn->remote_addr = NULL;
-    }
-    else {
-      /* this is our local socket, we did never publish it */
-      CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
-                  ", not active)", ctx->sock);
-      socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
-      ctx->sock = CURL_SOCKET_BAD;
-    }
+    CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
+                ")", ctx->sock);
+    if(ctx->sock == cf->conn->sock[cf->sockindex])
+      cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
+    socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
+    ctx->sock = CURL_SOCKET_BAD;
+    if(ctx->active && cf->sockindex == FIRSTSOCKET)
+      cf->conn->remote_addr = NULL;
     Curl_bufq_reset(&ctx->recvbuf);
     ctx->active = FALSE;
     ctx->buffer_recv = FALSE;
@@ -1169,6 +1146,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
 
   *done = FALSE; /* a very negative world view is best */
   if(ctx->sock == CURL_SOCKET_BAD) {
+    int error;
 
     result = cf_socket_open(cf, data);
     if(result)
@@ -1181,8 +1159,12 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
 
     /* Connect TCP socket */
     rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen);
+    error = SOCKERRNO;
+    set_local_ip(cf, data);
+    CURL_TRC_CF(data, cf, "local address %s port %d...",
+                ctx->l_ip, ctx->l_port);
     if(-1 == rc) {
-      result = socket_connect_result(data, ctx->r_ip, SOCKERRNO);
+      result = socket_connect_result(data, ctx->r_ip, error);
       goto out;
     }
   }
@@ -1220,13 +1202,14 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
 out:
   if(result) {
     if(ctx->error) {
+      set_local_ip(cf, data);
       data->state.os_errno = ctx->error;
       SET_SOCKERRNO(ctx->error);
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
       {
         char buffer[STRERROR_LEN];
-        infof(data, "connect to %s port %u failed: %s",
-              ctx->r_ip, ctx->r_port,
+        infof(data, "connect to %s port %u from %s port %d failed: %s",
+              ctx->r_ip, ctx->r_port, ctx->l_ip, ctx->l_port,
               Curl_strerror(ctx->error, buffer, sizeof(buffer)));
       }
 #endif
@@ -1252,20 +1235,19 @@ static void cf_socket_get_host(struct Curl_cfilter *cf,
   *pport = cf->conn->port;
 }
 
-static int cf_socket_get_select_socks(struct Curl_cfilter *cf,
+static void cf_socket_adjust_pollset(struct Curl_cfilter *cf,
                                       struct Curl_easy *data,
-                                      curl_socket_t *socks)
+                                      struct easy_pollset *ps)
 {
   struct cf_socket_ctx *ctx = cf->ctx;
-  int rc = GETSOCK_BLANK;
 
-  (void)data;
-  if(!cf->connected && ctx->sock != CURL_SOCKET_BAD) {
-    socks[0] = ctx->sock;
-    rc |= GETSOCK_WRITESOCK(0);
+  if(ctx->sock != CURL_SOCKET_BAD) {
+    if(!cf->connected)
+      Curl_pollset_set_out_only(data, ps, ctx->sock);
+    else
+      Curl_pollset_add_in(data, ps, ctx->sock);
+    CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num);
   }
-
-  return rc;
 }
 
 static bool cf_socket_data_pending(struct Curl_cfilter *cf,
@@ -1518,6 +1500,9 @@ static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf,
   case CF_CTRL_DATA_SETUP:
     Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
     break;
+  case CF_CTRL_FORGET_SOCKET:
+    ctx->sock = CURL_SOCKET_BAD;
+    break;
   }
   return CURLE_OK;
 }
@@ -1612,7 +1597,7 @@ struct Curl_cftype Curl_cft_tcp = {
   cf_tcp_connect,
   cf_socket_close,
   cf_socket_get_host,
-  cf_socket_get_select_socks,
+  cf_socket_adjust_pollset,
   cf_socket_data_pending,
   cf_socket_send,
   cf_socket_recv,
@@ -1635,7 +1620,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
   (void)data;
   (void)conn;
   DEBUGASSERT(transport == TRNSPRT_TCP);
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
@@ -1742,7 +1727,7 @@ struct Curl_cftype Curl_cft_udp = {
   cf_udp_connect,
   cf_socket_close,
   cf_socket_get_host,
-  cf_socket_get_select_socks,
+  cf_socket_adjust_pollset,
   cf_socket_data_pending,
   cf_socket_send,
   cf_socket_recv,
@@ -1765,7 +1750,7 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
   (void)data;
   (void)conn;
   DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC);
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
@@ -1793,7 +1778,7 @@ struct Curl_cftype Curl_cft_unix = {
   cf_tcp_connect,
   cf_socket_close,
   cf_socket_get_host,
-  cf_socket_get_select_socks,
+  cf_socket_adjust_pollset,
   cf_socket_data_pending,
   cf_socket_send,
   cf_socket_recv,
@@ -1816,7 +1801,7 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
   (void)data;
   (void)conn;
   DEBUGASSERT(transport == TRNSPRT_UNIX);
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
@@ -1857,7 +1842,7 @@ struct Curl_cftype Curl_cft_tcp_accept = {
   cf_tcp_accept_connect,
   cf_socket_close,
   cf_socket_get_host,              /* TODO: not accurate */
-  cf_socket_get_select_socks,
+  cf_socket_adjust_pollset,
   cf_socket_data_pending,
   cf_socket_send,
   cf_socket_recv,
@@ -1879,7 +1864,7 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
   Curl_conn_cf_discard_all(data, conn, sockindex);
   DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD);
 
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;

+ 179 - 25
lib/cfilters.c

@@ -33,6 +33,7 @@
 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
 #include "multiif.h"
 #include "progress.h"
+#include "select.h"
 #include "warnless.h"
 
 /* The last 3 #include files should be in this order */
@@ -70,12 +71,14 @@ void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data,
   }
 }
 
-int Curl_cf_def_get_select_socks(struct Curl_cfilter *cf,
+void Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
-                                 curl_socket_t *socks)
+                                 struct easy_pollset *ps)
 {
-  return cf->next?
-    cf->next->cft->get_select_socks(cf->next, data, socks) : 0;
+  /* NOP */
+  (void)cf;
+  (void)data;
+  (void)ps;
 }
 
 bool Curl_cf_def_data_pending(struct Curl_cfilter *cf,
@@ -212,7 +215,7 @@ CURLcode Curl_cf_create(struct Curl_cfilter **pcf,
   CURLcode result = CURLE_OUT_OF_MEMORY;
 
   DEBUGASSERT(cft);
-  cf = calloc(sizeof(*cf), 1);
+  cf = calloc(1, sizeof(*cf));
   if(!cf)
     goto out;
 
@@ -303,15 +306,6 @@ void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
     cf->cft->do_close(cf, data);
 }
 
-int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data,
-                                  curl_socket_t *socks)
-{
-  if(cf)
-    return cf->cft->get_select_socks(cf, data, socks);
-  return 0;
-}
-
 ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                           const void *buf, size_t len, CURLcode *err)
 {
@@ -433,22 +427,31 @@ bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex)
   return FALSE;
 }
 
-int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex,
-                               curl_socket_t *socks)
+void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 struct easy_pollset *ps)
+{
+  /* Get the lowest not-connected filter, if there are any */
+  while(cf && !cf->connected && cf->next && !cf->next->connected)
+    cf = cf->next;
+  /* From there on, give all filters a chance to adjust the pollset.
+   * Lower filters are called later, so they may override */
+  while(cf) {
+    cf->cft->adjust_pollset(cf, data, ps);
+    cf = cf->next;
+  }
+}
+
+void Curl_conn_adjust_pollset(struct Curl_easy *data,
+                               struct easy_pollset *ps)
 {
-  struct Curl_cfilter *cf;
+  int i;
 
   DEBUGASSERT(data);
   DEBUGASSERT(data->conn);
-  cf = data->conn->cfilter[sockindex];
-
-  /* if the next one is not yet connected, that's the one we want */
-  while(cf && cf->next && !cf->next->connected)
-    cf = cf->next;
-  if(cf) {
-    return cf->cft->get_select_socks(cf, data, socks);
+  for(i = 0; i < 2; ++i) {
+    Curl_conn_cf_adjust_pollset(data->conn->cfilter[i], data, ps);
   }
-  return GETSOCK_BLANK;
 }
 
 void Curl_conn_get_host(struct Curl_easy *data, int sockindex,
@@ -524,6 +527,18 @@ curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex)
   return data->conn? data->conn->sock[sockindex] : CURL_SOCKET_BAD;
 }
 
+void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex)
+{
+  if(data->conn) {
+    struct Curl_cfilter *cf = data->conn->cfilter[sockindex];
+    if(cf)
+      (void)Curl_conn_cf_cntrl(cf, data, TRUE,
+                               CF_CTRL_FORGET_SOCKET, 0, NULL);
+    fake_sclose(data->conn->sock[sockindex]);
+    data->conn->sock[sockindex] = CURL_SOCKET_BAD;
+  }
+}
+
 static CURLcode cf_cntrl_all(struct connectdata *conn,
                              struct Curl_easy *data,
                              bool ignore_result,
@@ -646,3 +661,142 @@ size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
                               &n, NULL) : CURLE_UNKNOWN_OPTION;
   return (result || n <= 0)? 1 : (size_t)n;
 }
+
+
+void Curl_pollset_reset(struct Curl_easy *data,
+                        struct easy_pollset *ps)
+{
+  size_t i;
+  (void)data;
+  memset(ps, 0, sizeof(*ps));
+  for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++)
+    ps->sockets[i] = CURL_SOCKET_BAD;
+}
+
+/**
+ *
+ */
+void Curl_pollset_change(struct Curl_easy *data,
+                       struct easy_pollset *ps, curl_socket_t sock,
+                       int add_flags, int remove_flags)
+{
+  unsigned int i;
+
+  (void)data;
+  DEBUGASSERT(VALID_SOCK(sock));
+  if(!VALID_SOCK(sock))
+    return;
+
+  DEBUGASSERT(add_flags <= (CURL_POLL_IN|CURL_POLL_OUT));
+  DEBUGASSERT(remove_flags <= (CURL_POLL_IN|CURL_POLL_OUT));
+  DEBUGASSERT((add_flags&remove_flags) == 0); /* no overlap */
+  for(i = 0; i < ps->num; ++i) {
+    if(ps->sockets[i] == sock) {
+      ps->actions[i] &= (unsigned char)(~remove_flags);
+      ps->actions[i] |= (unsigned char)add_flags;
+      /* all gone? remove socket */
+      if(!ps->actions[i]) {
+        if((i + 1) < ps->num) {
+          memmove(&ps->sockets[i], &ps->sockets[i + 1],
+                  (ps->num - (i + 1)) * sizeof(ps->sockets[0]));
+          memmove(&ps->actions[i], &ps->actions[i + 1],
+                  (ps->num - (i + 1)) * sizeof(ps->actions[0]));
+        }
+        --ps->num;
+      }
+      return;
+    }
+  }
+  /* not present */
+  if(add_flags) {
+    /* Having more SOCKETS per easy handle than what is defined
+     * is a programming error. This indicates that we need
+     * to raise this limit, making easy_pollset larger.
+     * Since we use this in tight loops, we do not want to make
+     * the pollset dynamic unnecessarily.
+     * The current maximum in practise is HTTP/3 eyeballing where
+     * we have up to 4 sockets involved in connection setup.
+     */
+    DEBUGASSERT(i < MAX_SOCKSPEREASYHANDLE);
+    if(i < MAX_SOCKSPEREASYHANDLE) {
+      ps->sockets[i] = sock;
+      ps->actions[i] = (unsigned char)add_flags;
+      ps->num = i + 1;
+    }
+  }
+}
+
+void Curl_pollset_set(struct Curl_easy *data,
+                      struct easy_pollset *ps, curl_socket_t sock,
+                      bool do_in, bool do_out)
+{
+  Curl_pollset_change(data, ps, sock,
+                      (do_in?CURL_POLL_IN:0)|(do_out?CURL_POLL_OUT:0),
+                      (!do_in?CURL_POLL_IN:0)|(!do_out?CURL_POLL_OUT:0));
+}
+
+static void ps_add(struct Curl_easy *data, struct easy_pollset *ps,
+                   int bitmap, curl_socket_t *socks)
+{
+  if(bitmap) {
+    int i;
+    for(i = 0; i < MAX_SOCKSPEREASYHANDLE; ++i) {
+      if(!(bitmap & GETSOCK_MASK_RW(i)) || !VALID_SOCK((socks[i]))) {
+        break;
+      }
+      if(bitmap & GETSOCK_READSOCK(i)) {
+        if(bitmap & GETSOCK_WRITESOCK(i))
+          Curl_pollset_add_inout(data, ps, socks[i]);
+        else
+          /* is READ, since we checked MASK_RW above */
+          Curl_pollset_add_in(data, ps, socks[i]);
+      }
+      else
+        Curl_pollset_add_out(data, ps, socks[i]);
+    }
+  }
+}
+
+void Curl_pollset_add_socks(struct Curl_easy *data,
+                            struct easy_pollset *ps,
+                            int (*get_socks_cb)(struct Curl_easy *data,
+                                                struct connectdata *conn,
+                                                curl_socket_t *socks))
+{
+  curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
+  int bitmap;
+
+  DEBUGASSERT(data->conn);
+  bitmap = get_socks_cb(data, data->conn, socks);
+  ps_add(data, ps, bitmap, socks);
+}
+
+void Curl_pollset_add_socks2(struct Curl_easy *data,
+                             struct easy_pollset *ps,
+                             int (*get_socks_cb)(struct Curl_easy *data,
+                                                 curl_socket_t *socks))
+{
+  curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
+  int bitmap;
+
+  bitmap = get_socks_cb(data, socks);
+  ps_add(data, ps, bitmap, socks);
+}
+
+void Curl_pollset_check(struct Curl_easy *data,
+                        struct easy_pollset *ps, curl_socket_t sock,
+                        bool *pwant_read, bool *pwant_write)
+{
+  unsigned int i;
+
+  (void)data;
+  DEBUGASSERT(VALID_SOCK(sock));
+  for(i = 0; i < ps->num; ++i) {
+    if(ps->sockets[i] == sock) {
+      *pwant_read = !!(ps->actions[i] & CURL_POLL_IN);
+      *pwant_write = !!(ps->actions[i] & CURL_POLL_OUT);
+      return;
+    }
+  }
+  *pwant_read = *pwant_write = FALSE;
+}

+ 95 - 18
lib/cfilters.h

@@ -60,14 +60,34 @@ typedef void     Curl_cft_get_host(struct Curl_cfilter *cf,
                                   const char **pdisplay_host,
                                   int *pport);
 
-/* Filters may return sockets and fdset flags they are waiting for.
- * The passes array has room for up to MAX_SOCKSPEREASYHANDLE sockets.
- * @return read/write fdset for index in socks
- *         or GETSOCK_BLANK when nothing to wait on
+struct easy_pollset;
+
+/* Passing in an easy_pollset for monitoring of sockets, let
+ * filters add or remove sockets actions (CURL_POLL_OUT, CURL_POLL_IN).
+ * This may add a socket or, in case no actions remain, remove
+ * a socket from the set.
+ *
+ * Filter implementations need to call filters "below" *after* they have
+ * made their adjustments. This allows lower filters to override "upper"
+ * actions. If a "lower" filter is unable to write, it needs to be able
+ * to disallow POLL_OUT.
+ *
+ * A filter without own restrictions/preferences should not modify
+ * the pollset. Filters, whose filter "below" is not connected, should
+ * also do no adjustments.
+ *
+ * Examples: a TLS handshake, while ongoing, might remove POLL_IN
+ * when it needs to write, or vice versa. A HTTP/2 filter might remove
+ * POLL_OUT when a stream window is exhausted and a WINDOW_UPDATE needs
+ * to be received first and add instead POLL_IN.
+ *
+ * @param cf     the filter to ask
+ * @param data   the easy handle the pollset is about
+ * @param ps     the pollset (inout) for the easy handle
  */
-typedef int      Curl_cft_get_select_socks(struct Curl_cfilter *cf,
-                                           struct Curl_easy *data,
-                                           curl_socket_t *socks);
+typedef void     Curl_cft_adjust_pollset(struct Curl_cfilter *cf,
+                                          struct Curl_easy *data,
+                                          struct easy_pollset *ps);
 
 typedef bool     Curl_cft_data_pending(struct Curl_cfilter *cf,
                                        const struct Curl_easy *data);
@@ -110,6 +130,7 @@ typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf,
 #define CF_CTRL_DATA_DONE_SEND        8  /* 0          NULL     ignored */
 /* update conn info at connection and data */
 #define CF_CTRL_CONN_INFO_UPDATE (256+0) /* 0          NULL     ignored */
+#define CF_CTRL_FORGET_SOCKET    (256+1) /* 0          NULL     ignored */
 
 /**
  * Handle event/control for the filter.
@@ -171,7 +192,7 @@ struct Curl_cftype {
   Curl_cft_connect *do_connect;           /* establish connection */
   Curl_cft_close *do_close;               /* close conn */
   Curl_cft_get_host *get_host;            /* host filter talks to */
-  Curl_cft_get_select_socks *get_select_socks;/* sockets to select on */
+  Curl_cft_adjust_pollset *adjust_pollset; /* adjust transfer poll set */
   Curl_cft_data_pending *has_data_pending;/* conn has data pending */
   Curl_cft_send *do_send;                 /* send data */
   Curl_cft_recv *do_recv;                 /* receive data */
@@ -200,9 +221,9 @@ void Curl_cf_def_destroy_this(struct Curl_cfilter *cf,
 void     Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data,
                               const char **phost, const char **pdisplay_host,
                               int *pport);
-int      Curl_cf_def_get_select_socks(struct Curl_cfilter *cf,
-                                      struct Curl_easy *data,
-                                      curl_socket_t *socks);
+void     Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf,
+                                     struct Curl_easy *data,
+                                     struct easy_pollset *ps);
 bool     Curl_cf_def_data_pending(struct Curl_cfilter *cf,
                                   const struct Curl_easy *data);
 ssize_t  Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
@@ -279,9 +300,6 @@ CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
                               struct Curl_easy *data,
                               bool blocking, bool *done);
 void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data);
-int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data,
-                                  curl_socket_t *socks);
 ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                           const void *buf, size_t len, CURLcode *err);
 ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
@@ -364,11 +382,22 @@ bool Curl_conn_data_pending(struct Curl_easy *data,
 curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex);
 
 /**
- * Get any select fd flags and the socket filters at chain `sockindex`
- * at connection `conn` might be waiting for.
+ * Tell filters to forget about the socket at sockindex.
  */
-int Curl_conn_get_select_socks(struct Curl_easy *data, int sockindex,
-                               curl_socket_t *socks);
+void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex);
+
+/**
+ * Adjust the pollset for the filter chain startgin at `cf`.
+ */
+void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 struct easy_pollset *ps);
+
+/**
+ * Adjust pollset from filters installed at transfer's connection.
+ */
+void Curl_conn_adjust_pollset(struct Curl_easy *data,
+                               struct easy_pollset *ps);
 
 /**
  * Receive data through the filter chain at `sockindex` for connection
@@ -468,6 +497,54 @@ size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
                                     int sockindex);
 
 
+void Curl_pollset_reset(struct Curl_easy *data,
+                        struct easy_pollset *ps);
+
+/* Change the poll flags (CURL_POLL_IN/CURL_POLL_OUT) to the poll set for
+ * socket `sock`. If the socket is not already part of the poll set, it
+ * will be added.
+ * If the socket is present and all poll flags are cleared, it will be removed.
+ */
+void Curl_pollset_change(struct Curl_easy *data,
+                         struct easy_pollset *ps, curl_socket_t sock,
+                         int add_flags, int remove_flags);
+
+void Curl_pollset_set(struct Curl_easy *data,
+                      struct easy_pollset *ps, curl_socket_t sock,
+                      bool do_in, bool do_out);
+
+#define Curl_pollset_add_in(data, ps, sock) \
+          Curl_pollset_change((data), (ps), (sock), CURL_POLL_IN, 0)
+#define Curl_pollset_add_out(data, ps, sock) \
+          Curl_pollset_change((data), (ps), (sock), CURL_POLL_OUT, 0)
+#define Curl_pollset_add_inout(data, ps, sock) \
+          Curl_pollset_change((data), (ps), (sock), \
+                               CURL_POLL_IN|CURL_POLL_OUT, 0)
+#define Curl_pollset_set_in_only(data, ps, sock) \
+          Curl_pollset_change((data), (ps), (sock), \
+                               CURL_POLL_IN, CURL_POLL_OUT)
+#define Curl_pollset_set_out_only(data, ps, sock) \
+          Curl_pollset_change((data), (ps), (sock), \
+                               CURL_POLL_OUT, CURL_POLL_IN)
+
+void Curl_pollset_add_socks(struct Curl_easy *data,
+                            struct easy_pollset *ps,
+                            int (*get_socks_cb)(struct Curl_easy *data,
+                                                struct connectdata *conn,
+                                                curl_socket_t *socks));
+void Curl_pollset_add_socks2(struct Curl_easy *data,
+                             struct easy_pollset *ps,
+                             int (*get_socks_cb)(struct Curl_easy *data,
+                                                 curl_socket_t *socks));
+
+/**
+ * Check if the pollset, as is, wants to read and/or write regarding
+ * the given socket.
+ */
+void Curl_pollset_check(struct Curl_easy *data,
+                        struct easy_pollset *ps, curl_socket_t sock,
+                        bool *pwant_read, bool *pwant_write);
+
 /**
  * Types and macros used to keep the current easy handle in filter calls,
  * allowing for nested invocations. See #10336.

+ 15 - 10
lib/conncache.c

@@ -107,7 +107,7 @@ int Curl_conncache_init(struct conncache *connc, int size)
   connc->closure_handle = curl_easy_init();
   if(!connc->closure_handle)
     return 1; /* bad */
-  connc->closure_handle->internal = true;
+  connc->closure_handle->state.internal = true;
 
   Curl_hash_init(&connc->hash, size, Curl_hash_str,
                  Curl_str_key_compare, free_bundle_hash_entry);
@@ -243,7 +243,7 @@ CURLcode Curl_conncache_add_conn(struct Curl_easy *data)
   conn->connection_id = connc->next_connection_id++;
   connc->num_conn++;
 
-  DEBUGF(infof(data, "Added connection %ld. "
+  DEBUGF(infof(data, "Added connection %" CURL_FORMAT_CURL_OFF_T ". "
                "The cache now contains %zu members",
                conn->connection_id, connc->num_conn));
 
@@ -379,21 +379,26 @@ conncache_find_first_connection(struct conncache *connc)
 bool Curl_conncache_return_conn(struct Curl_easy *data,
                                 struct connectdata *conn)
 {
-  /* data->multi->maxconnects can be negative, deal with it. */
-  size_t maxconnects =
-    (data->multi->maxconnects < 0) ? data->multi->num_easy * 4:
-    data->multi->maxconnects;
+  unsigned int maxconnects = !data->multi->maxconnects ?
+    data->multi->num_easy * 4: data->multi->maxconnects;
   struct connectdata *conn_candidate = NULL;
 
   conn->lastused = Curl_now(); /* it was used up until now */
-  if(maxconnects > 0 &&
-     Curl_conncache_size(data) > maxconnects) {
+  if(maxconnects && Curl_conncache_size(data) > maxconnects) {
     infof(data, "Connection cache is full, closing the oldest one");
 
     conn_candidate = Curl_conncache_extract_oldest(data);
     if(conn_candidate) {
-      /* the winner gets the honour of being disconnected */
-      Curl_disconnect(data, conn_candidate, /* dead_connection */ FALSE);
+      /* Use the closure handle for this disconnect so that anything that
+         happens during the disconnect is not stored and associated with the
+         'data' handle which already just finished a transfer and it is
+         important that details from this (unrelated) disconnect does not
+         taint meta-data in the data handle. */
+      struct conncache *connc = data->state.conn_cache;
+      connc->closure_handle->state.buffer = data->state.buffer;
+      connc->closure_handle->set.buffer_size = data->set.buffer_size;
+      Curl_disconnect(connc->closure_handle, conn_candidate,
+                      /* dead_connection */ FALSE);
     }
   }
 

+ 50 - 43
lib/connect.c

@@ -84,6 +84,9 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
+#ifndef ARRAYSIZE
+#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#endif
 
 /*
  * Curl_timeleft() returns the amount of milliseconds left allowed for the
@@ -348,6 +351,7 @@ void Curl_conncontrol(struct connectdata *conn,
  */
 struct eyeballer {
   const char *name;
+  const struct Curl_addrinfo *first; /* complete address list, not owned */
   const struct Curl_addrinfo *addr;  /* List of addresses to try, not owned */
   int ai_family;                     /* matching address family only */
   cf_ip_connect_create *cf_create;   /* for creating cf */
@@ -359,9 +363,12 @@ struct eyeballer {
   expire_id timeout_id;              /* ID for Curl_expire() */
   CURLcode result;
   int error;
+  BIT(rewinded);                     /* if we rewinded the addr list */
   BIT(has_started);                  /* attempts have started */
   BIT(is_done);                      /* out of addresses/time */
   BIT(connected);                    /* cf has connected */
+  BIT(inconclusive);                 /* connect was not a hard failure, we
+                                      * might talk to a restarting server */
 };
 
 
@@ -408,7 +415,7 @@ static CURLcode eyeballer_new(struct eyeballer **pballer,
 #endif
                   "ip"));
   baller->cf_create = cf_create;
-  baller->addr = addr;
+  baller->first = baller->addr = addr;
   baller->ai_family = ai_family;
   baller->primary = primary;
   baller->delay_ms = delay_ms;
@@ -438,6 +445,13 @@ static void baller_free(struct eyeballer *baller,
   }
 }
 
+static void baller_rewind(struct eyeballer *baller)
+{
+  baller->rewinded = TRUE;
+  baller->addr = baller->first;
+  baller->inconclusive = FALSE;
+}
+
 static void baller_next_addr(struct eyeballer *baller)
 {
   baller->addr = addr_next_match(baller->addr, baller->ai_family);
@@ -528,6 +542,10 @@ static CURLcode baller_start_next(struct Curl_cfilter *cf,
 {
   if(cf->sockindex == FIRSTSOCKET) {
     baller_next_addr(baller);
+    /* If we get inconclusive answers from the server(s), we make
+     * a second iteration over the address list */
+    if(!baller->addr && baller->inconclusive && !baller->rewinded)
+      baller_rewind(baller);
     baller_start(cf, data, baller, timeoutms);
   }
   else {
@@ -566,6 +584,8 @@ static CURLcode baller_connect(struct Curl_cfilter *cf,
         baller->result = CURLE_OPERATION_TIMEDOUT;
       }
     }
+    else if(baller->result == CURLE_WEIRD_SERVER_REPLY)
+      baller->inconclusive = TRUE;
   }
   return baller->result;
 }
@@ -595,7 +615,7 @@ evaluate:
   *connected = FALSE; /* a very negative world view is best */
   now = Curl_now();
   ongoing = not_started = 0;
-  for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+  for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
     struct eyeballer *baller = ctx->baller[i];
 
     if(!baller || baller->is_done)
@@ -656,7 +676,7 @@ evaluate:
   if(not_started > 0) {
     int added = 0;
 
-    for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+    for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
       struct eyeballer *baller = ctx->baller[i];
 
       if(!baller || baller->has_started)
@@ -691,13 +711,13 @@ evaluate:
   /* all ballers have failed to connect. */
   CURL_TRC_CF(data, cf, "all eyeballers failed");
   result = CURLE_COULDNT_CONNECT;
-  for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+  for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
     struct eyeballer *baller = ctx->baller[i];
+    if(!baller)
+      continue;
     CURL_TRC_CF(data, cf, "%s assess started=%d, result=%d",
-                baller?baller->name:NULL,
-                baller?baller->has_started:0,
-                baller?baller->result:0);
-    if(baller && baller->has_started && baller->result) {
+                baller->name, baller->has_started, baller->result);
+    if(baller->has_started && baller->result) {
       result = baller->result;
       break;
     }
@@ -838,7 +858,7 @@ static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data)
 
   DEBUGASSERT(ctx);
   DEBUGASSERT(data);
-  for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+  for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
     baller_free(ctx->baller[i], data);
     ctx->baller[i] = NULL;
   }
@@ -846,35 +866,22 @@ static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data)
   ctx->winner = NULL;
 }
 
-static int cf_he_get_select_socks(struct Curl_cfilter *cf,
+static void cf_he_adjust_pollset(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
-                                  curl_socket_t *socks)
+                                  struct easy_pollset *ps)
 {
   struct cf_he_ctx *ctx = cf->ctx;
-  size_t i, s;
-  int wrc, rc = GETSOCK_BLANK;
-  curl_socket_t wsocks[MAX_SOCKSPEREASYHANDLE];
-
-  if(cf->connected)
-    return cf->next->cft->get_select_socks(cf->next, data, socks);
-
-  for(i = s = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
-    struct eyeballer *baller = ctx->baller[i];
-    if(!baller || !baller->cf)
-      continue;
+  size_t i;
 
-    wrc = Curl_conn_cf_get_select_socks(baller->cf, data, wsocks);
-    if(wrc) {
-      /* TODO: we assume we get at most one socket back */
-      socks[s] = wsocks[0];
-      if(wrc & GETSOCK_WRITESOCK(0))
-        rc |= GETSOCK_WRITESOCK(s);
-      if(wrc & GETSOCK_READSOCK(0))
-        rc |= GETSOCK_READSOCK(s);
-      s++;
+  if(!cf->connected) {
+    for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+      struct eyeballer *baller = ctx->baller[i];
+      if(!baller || !baller->cf)
+        continue;
+      Curl_conn_cf_adjust_pollset(baller->cf, data, ps);
     }
+    CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num);
   }
-  return rc;
 }
 
 static CURLcode cf_he_connect(struct Curl_cfilter *cf,
@@ -956,7 +963,7 @@ static bool cf_he_data_pending(struct Curl_cfilter *cf,
   if(cf->connected)
     return cf->next->cft->has_data_pending(cf->next, data);
 
-  for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+  for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
     struct eyeballer *baller = ctx->baller[i];
     if(!baller || !baller->cf)
       continue;
@@ -975,7 +982,7 @@ static struct curltime get_max_baller_time(struct Curl_cfilter *cf,
   size_t i;
 
   memset(&tmax, 0, sizeof(tmax));
-  for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+  for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
     struct eyeballer *baller = ctx->baller[i];
 
     memset(&t, 0, sizeof(t));
@@ -1000,7 +1007,7 @@ static CURLcode cf_he_query(struct Curl_cfilter *cf,
       int reply_ms = -1;
       size_t i;
 
-      for(i = 0; i < sizeof(ctx->baller)/sizeof(ctx->baller[0]); i++) {
+      for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
         struct eyeballer *baller = ctx->baller[i];
         int breply_ms;
 
@@ -1055,7 +1062,7 @@ struct Curl_cftype Curl_cft_happy_eyeballs = {
   cf_he_connect,
   cf_he_close,
   Curl_cf_def_get_host,
-  cf_he_get_select_socks,
+  cf_he_adjust_pollset,
   cf_he_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_recv,
@@ -1089,7 +1096,7 @@ cf_happy_eyeballs_create(struct Curl_cfilter **pcf,
   (void)data;
   (void)conn;
   *pcf = NULL;
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;
@@ -1122,13 +1129,13 @@ struct transport_provider transport_providers[] = {
 #ifdef ENABLE_QUIC
   { TRNSPRT_QUIC, Curl_cf_quic_create },
 #endif
+#ifndef CURL_DISABLE_TFTP
   { TRNSPRT_UDP, Curl_cf_udp_create },
+#endif
+#ifdef USE_UNIX_SOCKETS
   { TRNSPRT_UNIX, Curl_cf_unix_create },
-};
-
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
 #endif
+};
 
 static cf_ip_connect_create *get_cf_create(int transport)
 {
@@ -1319,7 +1326,7 @@ struct Curl_cftype Curl_cft_setup = {
   cf_setup_connect,
   cf_setup_close,
   Curl_cf_def_get_host,
-  Curl_cf_def_get_select_socks,
+  Curl_cf_def_adjust_pollset,
   Curl_cf_def_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_recv,
@@ -1340,7 +1347,7 @@ static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
   CURLcode result = CURLE_OK;
 
   (void)data;
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx) {
     result = CURLE_OUT_OF_MEMORY;
     goto out;

+ 134 - 147
lib/content_encoding.c

@@ -63,6 +63,9 @@
 
 #ifndef CURL_DISABLE_HTTP
 
+/* allow no more than 5 "chained" compression steps */
+#define MAX_ENCODE_STACK 5
+
 #define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */
 
 
@@ -95,7 +98,7 @@ typedef enum {
 
 /* Deflate and gzip writer. */
 struct zlib_writer {
-  struct contenc_writer super;
+  struct Curl_cwriter super;
   zlibInitState zlib_init;   /* zlib init state */
   uInt trailerlen;           /* Remaining trailer byte count. */
   z_stream z;                /* State structure for zlib. */
@@ -171,7 +174,7 @@ static CURLcode process_trailer(struct Curl_easy *data,
 }
 
 static CURLcode inflate_stream(struct Curl_easy *data,
-                               struct contenc_writer *writer,
+                               struct Curl_cwriter *writer, int type,
                                zlibInitState started)
 {
   struct zlib_writer *zp = (struct zlib_writer *) writer;
@@ -196,7 +199,7 @@ static CURLcode inflate_stream(struct Curl_easy *data,
     return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY);
 
   /* because the buffer size is fixed, iteratively decompress and transfer to
-     the client via downstream_write function. */
+     the client via next_write function. */
   while(!done) {
     int status;                   /* zlib status */
     done = TRUE;
@@ -217,7 +220,7 @@ static CURLcode inflate_stream(struct Curl_easy *data,
     if(z->avail_out != DSIZ) {
       if(status == Z_OK || status == Z_STREAM_END) {
         zp->zlib_init = started;      /* Data started. */
-        result = Curl_unencode_write(data, writer->downstream, decomp,
+        result = Curl_cwriter_write(data, writer->next, type, decomp,
                                      DSIZ - z->avail_out);
         if(result) {
           exit_zlib(data, z, &zp->zlib_init, result);
@@ -274,8 +277,8 @@ static CURLcode inflate_stream(struct Curl_easy *data,
 
 
 /* Deflate handler. */
-static CURLcode deflate_init_writer(struct Curl_easy *data,
-                                    struct contenc_writer *writer)
+static CURLcode deflate_do_init(struct Curl_easy *data,
+                                    struct Curl_cwriter *writer)
 {
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
@@ -290,13 +293,16 @@ static CURLcode deflate_init_writer(struct Curl_easy *data,
   return CURLE_OK;
 }
 
-static CURLcode deflate_unencode_write(struct Curl_easy *data,
-                                       struct contenc_writer *writer,
+static CURLcode deflate_do_write(struct Curl_easy *data,
+                                       struct Curl_cwriter *writer, int type,
                                        const char *buf, size_t nbytes)
 {
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
 
+  if(!(type & CLIENTWRITE_BODY))
+    return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+
   /* Set the compressed input when this function is called */
   z->next_in = (Bytef *) buf;
   z->avail_in = (uInt) nbytes;
@@ -305,11 +311,11 @@ static CURLcode deflate_unencode_write(struct Curl_easy *data,
     return process_trailer(data, zp);
 
   /* Now uncompress the data */
-  return inflate_stream(data, writer, ZLIB_INFLATING);
+  return inflate_stream(data, writer, type, ZLIB_INFLATING);
 }
 
-static void deflate_close_writer(struct Curl_easy *data,
-                                 struct contenc_writer *writer)
+static void deflate_do_close(struct Curl_easy *data,
+                                 struct Curl_cwriter *writer)
 {
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
@@ -317,19 +323,19 @@ static void deflate_close_writer(struct Curl_easy *data,
   exit_zlib(data, z, &zp->zlib_init, CURLE_OK);
 }
 
-static const struct content_encoding deflate_encoding = {
+static const struct Curl_cwtype deflate_encoding = {
   "deflate",
   NULL,
-  deflate_init_writer,
-  deflate_unencode_write,
-  deflate_close_writer,
+  deflate_do_init,
+  deflate_do_write,
+  deflate_do_close,
   sizeof(struct zlib_writer)
 };
 
 
 /* Gzip handler. */
-static CURLcode gzip_init_writer(struct Curl_easy *data,
-                                 struct contenc_writer *writer)
+static CURLcode gzip_do_init(struct Curl_easy *data,
+                                 struct Curl_cwriter *writer)
 {
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
@@ -441,19 +447,22 @@ static enum {
 }
 #endif
 
-static CURLcode gzip_unencode_write(struct Curl_easy *data,
-                                    struct contenc_writer *writer,
+static CURLcode gzip_do_write(struct Curl_easy *data,
+                                    struct Curl_cwriter *writer, int type,
                                     const char *buf, size_t nbytes)
 {
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
 
+  if(!(type & CLIENTWRITE_BODY))
+    return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+
   if(zp->zlib_init == ZLIB_INIT_GZIP) {
     /* Let zlib handle the gzip decompression entirely */
     z->next_in = (Bytef *) buf;
     z->avail_in = (uInt) nbytes;
     /* Now uncompress the data */
-    return inflate_stream(data, writer, ZLIB_INIT_GZIP);
+    return inflate_stream(data, writer, type, ZLIB_INIT_GZIP);
   }
 
 #ifndef OLD_ZLIB_SUPPORT
@@ -565,12 +574,12 @@ static CURLcode gzip_unencode_write(struct Curl_easy *data,
   }
 
   /* We've parsed the header, now uncompress the data */
-  return inflate_stream(data, writer, ZLIB_GZIP_INFLATING);
+  return inflate_stream(data, writer, type, ZLIB_GZIP_INFLATING);
 #endif
 }
 
-static void gzip_close_writer(struct Curl_easy *data,
-                              struct contenc_writer *writer)
+static void gzip_do_close(struct Curl_easy *data,
+                              struct Curl_cwriter *writer)
 {
   struct zlib_writer *zp = (struct zlib_writer *) writer;
   z_stream *z = &zp->z;     /* zlib state structure */
@@ -578,12 +587,12 @@ static void gzip_close_writer(struct Curl_easy *data,
   exit_zlib(data, z, &zp->zlib_init, CURLE_OK);
 }
 
-static const struct content_encoding gzip_encoding = {
+static const struct Curl_cwtype gzip_encoding = {
   "gzip",
   "x-gzip",
-  gzip_init_writer,
-  gzip_unencode_write,
-  gzip_close_writer,
+  gzip_do_init,
+  gzip_do_write,
+  gzip_do_close,
   sizeof(struct zlib_writer)
 };
 
@@ -593,7 +602,7 @@ static const struct content_encoding gzip_encoding = {
 #ifdef HAVE_BROTLI
 /* Brotli writer. */
 struct brotli_writer {
-  struct contenc_writer super;
+  struct Curl_cwriter super;
   BrotliDecoderState *br;    /* State structure for brotli. */
 };
 
@@ -635,8 +644,8 @@ static CURLcode brotli_map_error(BrotliDecoderErrorCode be)
   return CURLE_WRITE_ERROR;
 }
 
-static CURLcode brotli_init_writer(struct Curl_easy *data,
-                                   struct contenc_writer *writer)
+static CURLcode brotli_do_init(struct Curl_easy *data,
+                                   struct Curl_cwriter *writer)
 {
   struct brotli_writer *bp = (struct brotli_writer *) writer;
   (void) data;
@@ -645,8 +654,8 @@ static CURLcode brotli_init_writer(struct Curl_easy *data,
   return bp->br? CURLE_OK: CURLE_OUT_OF_MEMORY;
 }
 
-static CURLcode brotli_unencode_write(struct Curl_easy *data,
-                                      struct contenc_writer *writer,
+static CURLcode brotli_do_write(struct Curl_easy *data,
+                                      struct Curl_cwriter *writer, int type,
                                       const char *buf, size_t nbytes)
 {
   struct brotli_writer *bp = (struct brotli_writer *) writer;
@@ -657,6 +666,9 @@ static CURLcode brotli_unencode_write(struct Curl_easy *data,
   CURLcode result = CURLE_OK;
   BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
 
+  if(!(type & CLIENTWRITE_BODY))
+    return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+
   if(!bp->br)
     return CURLE_WRITE_ERROR;  /* Stream already ended. */
 
@@ -670,7 +682,7 @@ static CURLcode brotli_unencode_write(struct Curl_easy *data,
     dstleft = DSIZ;
     r = BrotliDecoderDecompressStream(bp->br,
                                       &nbytes, &src, &dstleft, &dst, NULL);
-    result = Curl_unencode_write(data, writer->downstream,
+    result = Curl_cwriter_write(data, writer->next, type,
                                  decomp, DSIZ - dstleft);
     if(result)
       break;
@@ -693,8 +705,8 @@ static CURLcode brotli_unencode_write(struct Curl_easy *data,
   return result;
 }
 
-static void brotli_close_writer(struct Curl_easy *data,
-                                struct contenc_writer *writer)
+static void brotli_do_close(struct Curl_easy *data,
+                                struct Curl_cwriter *writer)
 {
   struct brotli_writer *bp = (struct brotli_writer *) writer;
 
@@ -706,12 +718,12 @@ static void brotli_close_writer(struct Curl_easy *data,
   }
 }
 
-static const struct content_encoding brotli_encoding = {
+static const struct Curl_cwtype brotli_encoding = {
   "br",
   NULL,
-  brotli_init_writer,
-  brotli_unencode_write,
-  brotli_close_writer,
+  brotli_do_init,
+  brotli_do_write,
+  brotli_do_close,
   sizeof(struct brotli_writer)
 };
 #endif
@@ -720,13 +732,13 @@ static const struct content_encoding brotli_encoding = {
 #ifdef HAVE_ZSTD
 /* Zstd writer. */
 struct zstd_writer {
-  struct contenc_writer super;
+  struct Curl_cwriter super;
   ZSTD_DStream *zds;    /* State structure for zstd. */
   void *decomp;
 };
 
-static CURLcode zstd_init_writer(struct Curl_easy *data,
-                                 struct contenc_writer *writer)
+static CURLcode zstd_do_init(struct Curl_easy *data,
+                                 struct Curl_cwriter *writer)
 {
   struct zstd_writer *zp = (struct zstd_writer *) writer;
 
@@ -737,8 +749,8 @@ static CURLcode zstd_init_writer(struct Curl_easy *data,
   return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY;
 }
 
-static CURLcode zstd_unencode_write(struct Curl_easy *data,
-                                    struct contenc_writer *writer,
+static CURLcode zstd_do_write(struct Curl_easy *data,
+                                    struct Curl_cwriter *writer, int type,
                                     const char *buf, size_t nbytes)
 {
   CURLcode result = CURLE_OK;
@@ -747,6 +759,9 @@ static CURLcode zstd_unencode_write(struct Curl_easy *data,
   ZSTD_outBuffer out;
   size_t errorCode;
 
+  if(!(type & CLIENTWRITE_BODY))
+    return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+
   if(!zp->decomp) {
     zp->decomp = malloc(DSIZ);
     if(!zp->decomp)
@@ -766,7 +781,7 @@ static CURLcode zstd_unencode_write(struct Curl_easy *data,
       return CURLE_BAD_CONTENT_ENCODING;
     }
     if(out.pos > 0) {
-      result = Curl_unencode_write(data, writer->downstream,
+      result = Curl_cwriter_write(data, writer->next, type,
                                    zp->decomp, out.pos);
       if(result)
         break;
@@ -778,8 +793,8 @@ static CURLcode zstd_unencode_write(struct Curl_easy *data,
   return result;
 }
 
-static void zstd_close_writer(struct Curl_easy *data,
-                              struct contenc_writer *writer)
+static void zstd_do_close(struct Curl_easy *data,
+                              struct Curl_cwriter *writer)
 {
   struct zstd_writer *zp = (struct zstd_writer *) writer;
 
@@ -795,52 +810,30 @@ static void zstd_close_writer(struct Curl_easy *data,
   }
 }
 
-static const struct content_encoding zstd_encoding = {
+static const struct Curl_cwtype zstd_encoding = {
   "zstd",
   NULL,
-  zstd_init_writer,
-  zstd_unencode_write,
-  zstd_close_writer,
+  zstd_do_init,
+  zstd_do_write,
+  zstd_do_close,
   sizeof(struct zstd_writer)
 };
 #endif
 
 
 /* Identity handler. */
-static CURLcode identity_init_writer(struct Curl_easy *data,
-                                     struct contenc_writer *writer)
-{
-  (void)data;
-  (void)writer;
-  return CURLE_OK;
-}
-
-static CURLcode identity_unencode_write(struct Curl_easy *data,
-                                        struct contenc_writer *writer,
-                                        const char *buf, size_t nbytes)
-{
-  return Curl_unencode_write(data, writer->downstream, buf, nbytes);
-}
-
-static void identity_close_writer(struct Curl_easy *data,
-                                  struct contenc_writer *writer)
-{
-  (void) data;
-  (void) writer;
-}
-
-static const struct content_encoding identity_encoding = {
+static const struct Curl_cwtype identity_encoding = {
   "identity",
   "none",
-  identity_init_writer,
-  identity_unencode_write,
-  identity_close_writer,
-  sizeof(struct contenc_writer)
+  Curl_cwriter_def_init,
+  Curl_cwriter_def_write,
+  Curl_cwriter_def_close,
+  sizeof(struct Curl_cwriter)
 };
 
 
 /* supported content encodings table. */
-static const struct content_encoding * const encodings[] = {
+static const struct Curl_cwtype * const encodings[] = {
   &identity_encoding,
 #ifdef HAVE_LIBZ
   &deflate_encoding,
@@ -856,13 +849,17 @@ static const struct content_encoding * const encodings[] = {
 };
 
 
-/* Return a list of comma-separated names of supported encodings. */
-char *Curl_all_content_encodings(void)
+/* Provide a list of comma-separated names of supported encodings.
+*/
+void Curl_all_content_encodings(char *buf, size_t blen)
 {
   size_t len = 0;
-  const struct content_encoding * const *cep;
-  const struct content_encoding *ce;
-  char *ace;
+  const struct Curl_cwtype * const *cep;
+  const struct Curl_cwtype *ce;
+
+  DEBUGASSERT(buf);
+  DEBUGASSERT(blen);
+  buf[0] = 0;
 
   for(cep = encodings; *cep; cep++) {
     ce = *cep;
@@ -870,12 +867,12 @@ char *Curl_all_content_encodings(void)
       len += strlen(ce->name) + 2;
   }
 
-  if(!len)
-    return strdup(CONTENT_ENCODING_DEFAULT);
-
-  ace = malloc(len);
-  if(ace) {
-    char *p = ace;
+  if(!len) {
+    if(blen >= sizeof(CONTENT_ENCODING_DEFAULT))
+      strcpy(buf, CONTENT_ENCODING_DEFAULT);
+  }
+  else if(blen > len) {
+    char *p = buf;
     for(cep = encodings; *cep; cep++) {
       ce = *cep;
       if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) {
@@ -887,75 +884,60 @@ char *Curl_all_content_encodings(void)
     }
     p[-2] = '\0';
   }
-
-  return ace;
 }
 
-
 /* Deferred error dummy writer. */
-static CURLcode error_init_writer(struct Curl_easy *data,
-                                  struct contenc_writer *writer)
+static CURLcode error_do_init(struct Curl_easy *data,
+                                  struct Curl_cwriter *writer)
 {
   (void)data;
   (void)writer;
   return CURLE_OK;
 }
 
-static CURLcode error_unencode_write(struct Curl_easy *data,
-                                     struct contenc_writer *writer,
+static CURLcode error_do_write(struct Curl_easy *data,
+                                     struct Curl_cwriter *writer, int type,
                                      const char *buf, size_t nbytes)
 {
-  char *all = Curl_all_content_encodings();
+  char all[256];
+  (void)Curl_all_content_encodings(all, sizeof(all));
 
   (void) writer;
   (void) buf;
   (void) nbytes;
 
-  if(!all)
-    return CURLE_OUT_OF_MEMORY;
+  if(!(type & CLIENTWRITE_BODY))
+    return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+
   failf(data, "Unrecognized content encoding type. "
         "libcurl understands %s content encodings.", all);
-  free(all);
   return CURLE_BAD_CONTENT_ENCODING;
 }
 
-static void error_close_writer(struct Curl_easy *data,
-                               struct contenc_writer *writer)
+static void error_do_close(struct Curl_easy *data,
+                               struct Curl_cwriter *writer)
 {
   (void) data;
   (void) writer;
 }
 
-static const struct content_encoding error_encoding = {
-  NULL,
+static const struct Curl_cwtype error_writer = {
+  "ce-error",
   NULL,
-  error_init_writer,
-  error_unencode_write,
-  error_close_writer,
-  sizeof(struct contenc_writer)
+  error_do_init,
+  error_do_write,
+  error_do_close,
+  sizeof(struct Curl_cwriter)
 };
 
-/* Write data using an unencoding writer stack. "nbytes" is not
-   allowed to be 0. */
-CURLcode Curl_unencode_write(struct Curl_easy *data,
-                             struct contenc_writer *writer,
-                             const char *buf, size_t nbytes)
-{
-  if(!nbytes)
-    return CURLE_OK;
-  if(!writer)
-    return CURLE_WRITE_ERROR;
-  return writer->handler->unencode_write(data, writer, buf, nbytes);
-}
-
 /* Find the content encoding by name. */
-static const struct content_encoding *find_encoding(const char *name,
+static const struct Curl_cwtype *find_encoding(const char *name,
                                                     size_t len)
 {
-  const struct content_encoding * const *cep;
+  const struct Curl_cwtype * const *cep;
 
   for(cep = encodings; *cep; cep++) {
-    const struct content_encoding *ce = *cep;
+    const struct Curl_cwtype *ce = *cep;
     if((strncasecompare(name, ce->name, len) && !ce->name[len]) ||
        (ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len]))
       return ce;
@@ -969,7 +951,8 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
                                      const char *enclist, int is_transfer)
 {
   struct SingleRequest *k = &data->req;
-  unsigned int order = is_transfer? 2: 1;
+  Curl_cwriter_phase phase = is_transfer?
+                             CURL_CW_TRANSFER_DECODE:CURL_CW_CONTENT_DECODE;
   CURLcode result;
 
   do {
@@ -992,23 +975,32 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
       Curl_httpchunk_init(data);   /* init our chunky engine. */
     }
     else if(namelen) {
-      const struct content_encoding *encoding;
-      struct contenc_writer *writer;
-      if(is_transfer && !data->set.http_transfer_encoding)
+      const struct Curl_cwtype *cwt;
+      struct Curl_cwriter *writer;
+
+      if((is_transfer && !data->set.http_transfer_encoding) ||
+         (!is_transfer && data->set.http_ce_skip)) {
         /* not requested, ignore */
         return CURLE_OK;
+      }
 
-      encoding = find_encoding(name, namelen);
-      if(!encoding)
-        encoding = &error_encoding;  /* Defer error at stack use. */
+      if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) {
+        failf(data, "Reject response due to more than %u content encodings",
+              MAX_ENCODE_STACK);
+        return CURLE_BAD_CONTENT_ENCODING;
+      }
+
+      cwt = find_encoding(name, namelen);
+      if(!cwt)
+        cwt = &error_writer;  /* Defer error at use. */
 
-      result = Curl_client_create_writer(&writer, data, encoding, order);
+      result = Curl_cwriter_create(&writer, data, cwt, phase);
       if(result)
         return result;
 
-      result = Curl_client_add_writer(data, writer);
+      result = Curl_cwriter_add(data, writer);
       if(result) {
-        Curl_client_free_writer(data, writer);
+        Curl_cwriter_free(data, writer);
         return result;
       }
     }
@@ -1028,20 +1020,15 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
   return CURLE_NOT_BUILT_IN;
 }
 
-CURLcode Curl_unencode_write(struct Curl_easy *data,
-                             struct contenc_writer *writer,
-                             const char *buf, size_t nbytes)
+void Curl_all_content_encodings(char *buf, size_t blen)
 {
-  (void) data;
-  (void) writer;
-  (void) buf;
-  (void) nbytes;
-  return CURLE_NOT_BUILT_IN;
+  DEBUGASSERT(buf);
+  DEBUGASSERT(blen);
+  if(blen < sizeof(CONTENT_ENCODING_DEFAULT))
+    buf[0] = 0;
+  else
+    strcpy(buf, CONTENT_ENCODING_DEFAULT);
 }
 
-char *Curl_all_content_encodings(void)
-{
-  return strdup(CONTENT_ENCODING_DEFAULT);  /* Satisfy caller. */
-}
 
 #endif /* CURL_DISABLE_HTTP */

+ 2 - 7
lib/content_encoding.h

@@ -25,15 +25,10 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
-struct contenc_writer;
+struct Curl_cwriter;
 
-char *Curl_all_content_encodings(void);
+void Curl_all_content_encodings(char *buf, size_t blen);
 
 CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
                                      const char *enclist, int is_transfer);
-CURLcode Curl_unencode_write(struct Curl_easy *data,
-                             struct contenc_writer *writer,
-                             const char *buf, size_t nbytes);
-void Curl_unencode_cleanup(struct Curl_easy *data);
-
 #endif /* HEADER_CURL_CONTENT_ENCODING_H */

+ 19 - 13
lib/cookie.c

@@ -330,7 +330,7 @@ static char *sanitize_cookie_path(const char *cookie_path)
  */
 void Curl_cookie_loadfiles(struct Curl_easy *data)
 {
-  struct curl_slist *list = data->set.cookielist;
+  struct curl_slist *list = data->state.cookielist;
   if(list) {
     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
     while(list) {
@@ -365,9 +365,7 @@ static void strstore(char **str, const char *newstr, size_t len)
   DEBUGASSERT(newstr);
   DEBUGASSERT(str);
   free(*str);
-  *str = Curl_memdup(newstr, len + 1);
-  if(*str)
-    (*str)[len] = 0;
+  *str = Curl_strndup(newstr, len);
 }
 
 /*
@@ -1029,15 +1027,23 @@ Curl_cookie_add(struct Curl_easy *data,
    * dereference it.
    */
   if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) {
-    const psl_ctx_t *psl = Curl_psl_use(data);
-    int acceptable;
-
-    if(psl) {
-      acceptable = psl_is_cookie_domain_acceptable(psl, domain, co->domain);
-      Curl_psl_release(data);
+    bool acceptable = FALSE;
+    char lcase[256];
+    char lcookie[256];
+    size_t dlen = strlen(domain);
+    size_t clen = strlen(co->domain);
+    if((dlen < sizeof(lcase)) && (clen < sizeof(lcookie))) {
+      const psl_ctx_t *psl = Curl_psl_use(data);
+      if(psl) {
+        /* the PSL check requires lowercase domain name and pattern */
+        Curl_strntolower(lcase, domain, dlen + 1);
+        Curl_strntolower(lcookie, co->domain, clen + 1);
+        acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie);
+        Curl_psl_release(data);
+      }
+      else
+        acceptable = !bad_domain(domain, strlen(domain));
     }
-    else
-      acceptable = !bad_domain(domain, strlen(domain));
 
     if(!acceptable) {
       infof(data, "cookie '%s' dropped, domain '%s' must not "
@@ -1347,7 +1353,7 @@ static int cookie_sort_ct(const void *p1, const void *p2)
 
 static struct Cookie *dup_cookie(struct Cookie *src)
 {
-  struct Cookie *d = calloc(sizeof(struct Cookie), 1);
+  struct Cookie *d = calloc(1, sizeof(struct Cookie));
   if(d) {
     CLONE(domain);
     CLONE(path);

+ 12 - 15
lib/curl_config.h.cmake

@@ -74,9 +74,15 @@
 /* disables FTP */
 #cmakedefine CURL_DISABLE_FTP 1
 
+/* disables curl_easy_options API for existing options to curl_easy_setopt */
+#cmakedefine CURL_DISABLE_GETOPTIONS 1
+
 /* disables GOPHER */
 #cmakedefine CURL_DISABLE_GOPHER 1
 
+/* disables headers-api support */
+#cmakedefine CURL_DISABLE_HEADERS_API 1
+
 /* disables HSTS support */
 #cmakedefine CURL_DISABLE_HSTS 1
 
@@ -98,6 +104,9 @@
 /* disables MIME support */
 #cmakedefine CURL_DISABLE_MIME 1
 
+/* disables local binding support */
+#cmakedefine CURL_DISABLE_BINDLOCAL 1
+
 /* disables MQTT */
 #cmakedefine CURL_DISABLE_MQTT 1
 
@@ -168,9 +177,6 @@
 /* Define to 1 if you have _Atomic support. */
 #cmakedefine HAVE_ATOMIC 1
 
-/* Define to 1 if you have the `fchmod' function. */
-#cmakedefine HAVE_FCHMOD 1
-
 /* Define to 1 if you have the `fnmatch' function. */
 #cmakedefine HAVE_FNMATCH 1
 
@@ -208,6 +214,9 @@
 /* Define to 1 if you have the fseeko function. */
 #cmakedefine HAVE_FSEEKO 1
 
+/* Define to 1 if you have the fseeko declaration. */
+#cmakedefine HAVE_DECL_FSEEKO 1
+
 /* Define to 1 if you have the _fseeki64 function. */
 #cmakedefine HAVE__FSEEKI64 1
 
@@ -313,9 +322,6 @@
 /* Define to 1 if symbol `ADDRESS_FAMILY' exists */
 #cmakedefine HAVE_ADDRESS_FAMILY 1
 
-/* Define to 1 if you have the <inttypes.h> header file. */
-#cmakedefine HAVE_INTTYPES_H 1
-
 /* Define to 1 if you have the ioctlsocket function. */
 #cmakedefine HAVE_IOCTLSOCKET 1
 
@@ -497,9 +503,6 @@
 /* Define to 1 if you have the <stdbool.h> header file. */
 #cmakedefine HAVE_STDBOOL_H 1
 
-/* Define to 1 if you have the <stdint.h> header file. */
-#cmakedefine HAVE_STDINT_H 1
-
 /* Define to 1 if you have the strcasecmp function. */
 #cmakedefine HAVE_STRCASECMP 1
 
@@ -596,12 +599,6 @@
 /* Define to 1 if you have the <utime.h> header file. */
 #cmakedefine HAVE_UTIME_H 1
 
-/* Define to 1 if compiler supports C99 variadic macro style. */
-#cmakedefine HAVE_VARIADIC_MACROS_C99 1
-
-/* Define to 1 if compiler supports old gcc variadic macro style. */
-#cmakedefine HAVE_VARIADIC_MACROS_GCC 1
-
 /* Define to 1 if you have the windows.h header file. */
 #cmakedefine HAVE_WINDOWS_H 1
 

+ 2 - 1
lib/curl_hmac.h

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

+ 3 - 3
lib/curl_memory.h

@@ -68,7 +68,7 @@
 #undef send
 #undef recv
 
-#ifdef WIN32
+#ifdef _WIN32
 #  ifdef UNICODE
 #    undef wcsdup
 #    undef _wcsdup
@@ -134,7 +134,7 @@ extern curl_free_callback Curl_cfree;
 extern curl_realloc_callback Curl_crealloc;
 extern curl_strdup_callback Curl_cstrdup;
 extern curl_calloc_callback Curl_ccalloc;
-#if defined(WIN32) && defined(UNICODE)
+#if defined(_WIN32) && defined(UNICODE)
 extern curl_wcsdup_callback Curl_cwcsdup;
 #endif
 
@@ -160,7 +160,7 @@ extern curl_wcsdup_callback Curl_cwcsdup;
 #undef free
 #define free(ptr) Curl_cfree(ptr)
 
-#ifdef WIN32
+#ifdef _WIN32
 #  ifdef UNICODE
 #    undef wcsdup
 #    define wcsdup(ptr) Curl_cwcsdup(ptr)

+ 2 - 2
lib/curl_multibyte.c

@@ -32,7 +32,7 @@
 
 #include "curl_setup.h"
 
-#if defined(WIN32)
+#if defined(_WIN32)
 
 #include "curl_multibyte.h"
 
@@ -84,7 +84,7 @@ char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w)
   return str_utf8;
 }
 
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 #if defined(USE_WIN32_LARGE_FILES) || defined(USE_WIN32_SMALL_FILES)
 

+ 4 - 4
lib/curl_multibyte.h

@@ -25,7 +25,7 @@
  ***************************************************************************/
 #include "curl_setup.h"
 
-#if defined(WIN32)
+#if defined(_WIN32)
 
  /*
   * MultiByte conversions using Windows kernel32 library.
@@ -33,7 +33,7 @@
 
 wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8);
 char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w);
-#endif /* WIN32 */
+#endif /* _WIN32 */
 
 /*
  * Macros curlx_convert_UTF8_to_tchar(), curlx_convert_tchar_to_UTF8()
@@ -54,7 +54,7 @@ char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w);
  * ensure that the curl memdebug override macros do not replace them.
  */
 
-#if defined(UNICODE) && defined(WIN32)
+#if defined(UNICODE) && defined(_WIN32)
 
 #define curlx_convert_UTF8_to_tchar(ptr) curlx_convert_UTF8_to_wchar((ptr))
 #define curlx_convert_tchar_to_UTF8(ptr) curlx_convert_wchar_to_UTF8((ptr))
@@ -78,7 +78,7 @@ typedef union {
   const unsigned char *const_tbyte_ptr;
 } xcharp_u;
 
-#endif /* UNICODE && WIN32 */
+#endif /* UNICODE && _WIN32 */
 
 #define curlx_unicodefree(ptr)                          \
   do {                                                  \

+ 9 - 0
lib/curl_ntlm_core.c

@@ -111,6 +111,7 @@
 #  include <wincrypt.h>
 #else
 #  error "Can't compile NTLM support without a crypto library with DES."
+#  define CURL_NTLM_NOT_SUPPORTED
 #endif
 
 #include "urldata.h"
@@ -130,6 +131,7 @@
 #define NTLMv2_BLOB_SIGNATURE "\x01\x01\x00\x00"
 #define NTLMv2_BLOB_LEN       (44 -16 + ntlm->target_info_len + 4)
 
+#if !defined(CURL_NTLM_NOT_SUPPORTED)
 /*
 * Turns a 56-bit key into being 64-bit wide.
 */
@@ -144,6 +146,7 @@ static void extend_key_56_to_64(const unsigned char *key_56, char *key)
   key[6] = (unsigned char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6));
   key[7] = (unsigned char) ((key_56[6] << 1) & 0xFF);
 }
+#endif
 
 #if defined(USE_OPENSSL_DES) || defined(USE_WOLFSSL)
 /*
@@ -337,6 +340,10 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys,
   encrypt_des(plaintext, results, keys);
   encrypt_des(plaintext, results + 8, keys + 7);
   encrypt_des(plaintext, results + 16, keys + 14);
+#else
+  (void)keys;
+  (void)plaintext;
+  (void)results;
 #endif
 }
 
@@ -347,9 +354,11 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password,
                                    unsigned char *lmbuffer /* 21 bytes */)
 {
   unsigned char pw[14];
+#if !defined(CURL_NTLM_NOT_SUPPORTED)
   static const unsigned char magic[] = {
     0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 /* i.e. KGS!@#$% */
   };
+#endif
   size_t len = CURLMIN(strlen(password), 14);
 
   Curl_strntoupper((char *)pw, password, len);

+ 8 - 6
lib/curl_ntlm_wb.c

@@ -68,7 +68,9 @@
 
 /* Portable 'sclose_nolog' used only in child process instead of 'sclose'
    to avoid fooling the socket leak detector */
-#if defined(HAVE_CLOSESOCKET)
+#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))
@@ -189,7 +191,7 @@ static CURLcode ntlm_wb_init(struct Curl_easy *data, struct ntlmdata *ntlm,
     goto done;
   }
 
-  if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, sockfds)) {
+  if(wakeup_create(sockfds)) {
     failf(data, "Could not open socket pair. errno %d: %s",
           errno, Curl_strerror(errno, buffer, sizeof(buffer)));
     goto done;
@@ -197,8 +199,8 @@ static CURLcode ntlm_wb_init(struct Curl_easy *data, struct ntlmdata *ntlm,
 
   child_pid = fork();
   if(child_pid == -1) {
-    sclose(sockfds[0]);
-    sclose(sockfds[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;
@@ -268,7 +270,7 @@ static CURLcode ntlm_wb_response(struct Curl_easy *data, struct ntlmdata *ntlm,
   Curl_dyn_init(&b, MAX_NTLM_WB_RESPONSE);
 
   while(len_in > 0) {
-    ssize_t written = swrite(ntlm->ntlm_auth_hlpr_socket, input, len_in);
+    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)
@@ -282,7 +284,7 @@ static CURLcode ntlm_wb_response(struct Curl_easy *data, struct ntlmdata *ntlm,
   /* Read one line */
   while(1) {
     ssize_t size =
-      sread(ntlm->ntlm_auth_hlpr_socket, buf, data->set.buffer_size);
+      wakeup_read(ntlm->ntlm_auth_hlpr_socket, buf, data->set.buffer_size);
     if(size == -1) {
       if(errno == EINTR)
         continue;

+ 1 - 1
lib/curl_path.h

@@ -28,7 +28,7 @@
 #include <curl/curl.h>
 #include "urldata.h"
 
-#ifdef WIN32
+#ifdef _WIN32
 #  undef  PATH_MAX
 #  define PATH_MAX MAX_PATH
 #  ifndef R_OK

+ 1 - 1
lib/curl_rtmp.c

@@ -39,7 +39,7 @@
 /* The last #include file should be: */
 #include "memdebug.h"
 
-#if defined(WIN32) && !defined(USE_LWIPSOCK)
+#if defined(_WIN32) && !defined(USE_LWIPSOCK)
 #define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e)
 #define SET_RCVTIMEO(tv,s)   int tv = s*1000
 #elif defined(LWIP_SO_SNDRCVTIMEO_NONSTANDARD)

+ 3 - 0
lib/curl_sasl.c

@@ -262,6 +262,8 @@ static void sasl_state(struct SASL *sasl, struct Curl_easy *data,
   sasl->state = newstate;
 }
 
+#if defined(USE_NTLM) || defined(USE_GSASL) || defined(USE_KERBEROS5) || \
+  !defined(CURL_DISABLE_DIGEST_AUTH)
 /* Get the SASL server message and convert it to binary. */
 static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data,
                                    struct bufref *out)
@@ -284,6 +286,7 @@ static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data,
   }
   return result;
 }
+#endif
 
 /* Encode the outgoing SASL message. */
 static CURLcode build_message(struct SASL *sasl, struct bufref *msg)

+ 48 - 93
lib/curl_setup.h

@@ -28,6 +28,11 @@
 #define CURL_NO_OLDIES
 #endif
 
+/* Set default _WIN32_WINNT */
+#ifdef __MINGW32__
+#include <_mingw.h>
+#endif
+
 /*
  * Disable Visual Studio warnings:
  * 4127 "conditional expression is constant"
@@ -36,15 +41,7 @@
 #pragma warning(disable:4127)
 #endif
 
-/*
- * Define WIN32 when build target is Win32 API
- */
-
-#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
-#define WIN32
-#endif
-
-#ifdef WIN32
+#ifdef _WIN32
 /*
  * Don't include unneeded stuff in Windows headers to avoid compiler
  * warnings and macro clashes.
@@ -82,7 +79,7 @@
 #ifdef _WIN32_WCE
 #  include "config-win32ce.h"
 #else
-#  ifdef WIN32
+#  ifdef _WIN32
 #    include "config-win32.h"
 #  endif
 #endif
@@ -214,6 +211,23 @@
 #  define CURL_DISABLE_RTSP
 #endif
 
+/*
+ * When HTTP is disabled, disable HTTP-only features
+ */
+
+#if defined(CURL_DISABLE_HTTP)
+#  define CURL_DISABLE_ALTSVC 1
+#  define CURL_DISABLE_COOKIES 1
+#  define CURL_DISABLE_BASIC_AUTH 1
+#  define CURL_DISABLE_BEARER_AUTH 1
+#  define CURL_DISABLE_AWS 1
+#  define CURL_DISABLE_DOH 1
+#  define CURL_DISABLE_FORM_API 1
+#  define CURL_DISABLE_HEADERS_API 1
+#  define CURL_DISABLE_HSTS 1
+#  define CURL_DISABLE_HTTP_AUTH 1
+#endif
+
 /* ================================================================ */
 /* No system header file shall be included in this file before this */
 /* point.                                                           */
@@ -331,23 +345,6 @@
 #include <curl/stdcheaders.h>
 #endif
 
-#ifdef __POCC__
-#  include <sys/types.h>
-#  include <unistd.h>
-#  define sys_nerr EILSEQ
-#endif
-
-/*
- * Salford-C kludge section (mostly borrowed from wxWidgets).
- */
-#ifdef __SALFORDC__
-  #pragma suppress 353             /* Possible nested comments */
-  #pragma suppress 593             /* Define not used */
-  #pragma suppress 61              /* enum has no name */
-  #pragma suppress 106             /* unnamed, unused parameter */
-  #include <clib.h>
-#endif
-
 /*
  * Large file (>2Gb) support using WIN32 functions.
  */
@@ -500,11 +497,11 @@
    5. set dir/file naming defines
    */
 
-#ifdef WIN32
+#ifdef _WIN32
 
 #  define DIR_CHAR      "\\"
 
-#else /* WIN32 */
+#else /* _WIN32 */
 
 #  ifdef MSDOS  /* Watt-32 */
 
@@ -529,48 +526,19 @@
 
 #  define DIR_CHAR      "/"
 
-#  ifndef fileno /* sunos 4 have this as a macro! */
-     int fileno(FILE *stream);
-#  endif
-
-#endif /* WIN32 */
-
-/*
- * msvc 6.0 requires PSDK in order to have INET6_ADDRSTRLEN
- * defined in ws2tcpip.h as well as to provide IPv6 support.
- * Does not apply if lwIP is used.
- */
-
-#if defined(_MSC_VER) && !defined(__POCC__) && !defined(USE_LWIPSOCK)
-#  if !defined(HAVE_WS2TCPIP_H) || \
-     ((_MSC_VER < 1300) && !defined(INET6_ADDRSTRLEN))
-#    undef HAVE_GETADDRINFO_THREADSAFE
-#    undef HAVE_FREEADDRINFO
-#    undef HAVE_GETADDRINFO
-#    undef ENABLE_IPV6
-#  endif
-#endif
+#endif /* _WIN32 */
 
 /* ---------------------------------------------------------------- */
 /*             resolver specialty compile-time defines              */
 /*         CURLRES_* defines to use in the host*.c sources          */
 /* ---------------------------------------------------------------- */
 
-/*
- * lcc-win32 doesn't have _beginthreadex(), lacks threads support.
- */
-
-#if defined(__LCC__) && defined(WIN32)
-#  undef USE_THREADS_POSIX
-#  undef USE_THREADS_WIN32
-#endif
-
 /*
  * MSVC threads support requires a multi-threaded runtime library.
  * _beginthreadex() is not available in single-threaded ones.
  */
 
-#if defined(_MSC_VER) && !defined(__POCC__) && !defined(_MT)
+#if defined(_MSC_VER) && !defined(_MT)
 #  undef USE_THREADS_POSIX
 #  undef USE_THREADS_WIN32
 #endif
@@ -581,6 +549,9 @@
 
 #if defined(ENABLE_IPV6) && defined(HAVE_GETADDRINFO)
 #  define CURLRES_IPV6
+#elif defined(ENABLE_IPV6) && (defined(_WIN32) || defined(__CYGWIN__))
+/* assume on Windows that IPv6 without getaddrinfo is a broken build */
+#  error "Unexpected build: IPv6 is enabled but getaddrinfo was not found."
 #else
 #  define CURLRES_IPV4
 #endif
@@ -600,35 +571,6 @@
 
 /* ---------------------------------------------------------------- */
 
-/*
- * msvc 6.0 does not have struct sockaddr_storage and
- * does not define IPPROTO_ESP in winsock2.h. But both
- * are available if PSDK is properly installed.
- */
-
-#if defined(_MSC_VER) && !defined(__POCC__)
-#  if !defined(HAVE_WINSOCK2_H) || ((_MSC_VER < 1300) && !defined(IPPROTO_ESP))
-#    undef HAVE_STRUCT_SOCKADDR_STORAGE
-#  endif
-#endif
-
-/*
- * Intentionally fail to build when using msvc 6.0 without PSDK installed.
- * The brave of heart can circumvent this, defining ALLOW_MSVC6_WITHOUT_PSDK
- * in lib/config-win32.h although absolutely discouraged and unsupported.
- */
-
-#if defined(_MSC_VER) && !defined(__POCC__)
-#  if !defined(HAVE_WINDOWS_H) || ((_MSC_VER < 1300) && !defined(_FILETIME_))
-#    if !defined(ALLOW_MSVC6_WITHOUT_PSDK)
-#      error MSVC 6.0 requires "February 2003 Platform SDK" a.k.a. \
-             "Windows Server 2003 PSDK"
-#    else
-#      define CURL_DISABLE_LDAP 1
-#    endif
-#  endif
-#endif
-
 #if defined(HAVE_LIBIDN2) && defined(HAVE_IDN2_H) && !defined(USE_WIN32_IDN)
 /* The lib and header are present */
 #define USE_LIBIDN2
@@ -694,6 +636,18 @@
 #  define WARN_UNUSED_RESULT
 #endif
 
+/* noreturn attribute */
+
+#if !defined(CURL_NORETURN)
+#if (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__)
+#  define CURL_NORETURN  __attribute__((__noreturn__))
+#elif defined(_MSC_VER) && (_MSC_VER >= 1200)
+#  define CURL_NORETURN  __declspec(noreturn)
+#else
+#  define CURL_NORETURN
+#endif
+#endif
+
 /*
  * Include macros and defines that should only be processed once.
  */
@@ -752,7 +706,7 @@
 /* In Windows the default file mode is text but an application can override it.
 Therefore we specify it explicitly. https://github.com/curl/curl/pull/258
 */
-#if defined(WIN32) || defined(MSDOS)
+#if defined(_WIN32) || defined(MSDOS)
 #define FOPEN_READTEXT "rt"
 #define FOPEN_WRITETEXT "wt"
 #define FOPEN_APPENDTEXT "at"
@@ -807,7 +761,8 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
 #define UNITTEST static
 #endif
 
-#if defined(USE_NGHTTP2) || defined(USE_HYPER)
+/* Hyper supports HTTP2 also, but Curl's integration with Hyper does not */
+#if defined(USE_NGHTTP2)
 #define USE_HTTP2
 #endif
 
@@ -820,11 +775,11 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
 /* Certain Windows implementations are not aligned with what curl expects,
    so always use the local one on this platform. E.g. the mingw-w64
    implementation can return wrong results for non-ASCII inputs. */
-#if defined(HAVE_BASENAME) && defined(WIN32)
+#if defined(HAVE_BASENAME) && defined(_WIN32)
 #undef HAVE_BASENAME
 #endif
 
-#if defined(USE_UNIX_SOCKETS) && defined(WIN32)
+#if defined(USE_UNIX_SOCKETS) && defined(_WIN32)
 #  if !defined(UNIX_PATH_MAX)
      /* Replicating logic present in afunix.h
         (distributed with newer Windows 10 SDK versions only) */

+ 2 - 6
lib/curl_setup_once.h

@@ -56,7 +56,7 @@
 #include <sys/time.h>
 #endif
 
-#ifdef WIN32
+#ifdef _WIN32
 #include <io.h>
 #include <fcntl.h>
 #endif
@@ -70,11 +70,7 @@
 #endif
 
 #ifdef USE_WOLFSSL
-#  if defined(HAVE_STDINT_H)
-#    include <stdint.h>
-#  elif defined(HAVE_INTTYPES_H)
-#    include <inttypes.h>
-#  endif
+#include <stdint.h>
 #endif
 
 #ifdef USE_SCHANNEL

+ 16 - 0
lib/curl_sspi.h

@@ -88,6 +88,22 @@ extern PSecurityFunctionTable s_pSecFn;
 # define CRYPT_E_REVOKED                      ((HRESULT)0x80092010L)
 #endif
 
+#ifndef CRYPT_E_NO_REVOCATION_DLL
+# define CRYPT_E_NO_REVOCATION_DLL            ((HRESULT)0x80092011L)
+#endif
+
+#ifndef CRYPT_E_NO_REVOCATION_CHECK
+# define CRYPT_E_NO_REVOCATION_CHECK          ((HRESULT)0x80092012L)
+#endif
+
+#ifndef CRYPT_E_REVOCATION_OFFLINE
+# define CRYPT_E_REVOCATION_OFFLINE           ((HRESULT)0x80092013L)
+#endif
+
+#ifndef CRYPT_E_NOT_IN_REVOCATION_DATABASE
+# define CRYPT_E_NOT_IN_REVOCATION_DATABASE   ((HRESULT)0x80092014L)
+#endif
+
 #ifdef UNICODE
 #  define SECFLAG_WINNT_AUTH_IDENTITY \
      (unsigned long)SEC_WINNT_AUTH_IDENTITY_UNICODE

+ 6 - 20
lib/curl_trc.c

@@ -61,10 +61,6 @@ void Curl_debug(struct Curl_easy *data, curl_infotype type,
       "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
     if(data->set.fdebug) {
       bool inCallback = Curl_is_in_callback(data);
-      /* CURLOPT_DEBUGFUNCTION doc says the user may set CURLOPT_PRIVATE to
-         distinguish their handle from internal handles. */
-      if(data->internal)
-        DEBUGASSERT(!data->set.private_data);
       Curl_set_in_callback(data, true);
       (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata);
       Curl_set_in_callback(data, inCallback);
@@ -109,6 +105,8 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
   }
 }
 
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
+
 /* Curl_infof() is for info message along the way */
 #define MAXINFO 2048
 
@@ -128,13 +126,11 @@ void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
   }
 }
 
-#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
-
 void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
                        const char *fmt, ...)
 {
   DEBUGASSERT(cf);
-  if(data && Curl_trc_cf_is_verbose(cf, data)) {
+  if(Curl_trc_cf_is_verbose(cf, data)) {
     va_list ap;
     int len;
     char buffer[MAXINFO + 2];
@@ -232,24 +228,14 @@ CURLcode Curl_trc_init(void)
   if(config) {
     return Curl_trc_opt(config);
   }
-#endif
+#endif /* DEBUGBUILD */
   return CURLE_OK;
 }
-#else /* !CURL_DISABLE_VERBOSE_STRINGS) */
+#else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */
 
 CURLcode Curl_trc_init(void)
 {
   return CURLE_OK;
 }
 
-#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
-void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
-                       const char *fmt, ...)
-{
-  (void)data;
-  (void)cf;
-  (void)fmt;
-}
-#endif
-
-#endif /* !DEBUGBUILD */
+#endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */

+ 52 - 56
lib/curl_trc.h

@@ -54,19 +54,6 @@ CURLcode Curl_trc_opt(const char *config);
 void Curl_debug(struct Curl_easy *data, curl_infotype type,
                 char *ptr, size_t size);
 
-/**
- * Output an informational message when transfer's verbose logging is enabled.
- */
-void Curl_infof(struct Curl_easy *data,
-#if defined(__GNUC__) && !defined(printf) &&                    \
-  defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
-  !defined(__MINGW32__)
-                       const char *fmt, ...)
-                       __attribute__((format(printf, 2, 3)));
-#else
-                       const char *fmt, ...);
-#endif
-
 /**
  * Output a failure message on registered callbacks for transfer.
  */
@@ -82,39 +69,15 @@ void Curl_failf(struct Curl_easy *data,
 
 #define failf Curl_failf
 
-/**
- * Output an informational message when both transfer's verbose logging
- * and connection filters verbose logging are enabled.
- */
-void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
-#if defined(__GNUC__) && !defined(printf) &&                    \
-  defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
-  !defined(__MINGW32__)
-                       const char *fmt, ...)
-                       __attribute__((format(printf, 3, 4)));
-#else
-                       const char *fmt, ...);
-#endif
-
 #define CURL_LOG_LVL_NONE  0
 #define CURL_LOG_LVL_INFO  1
 
 
-#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
-/* informational messages enabled */
-
-#define Curl_trc_is_verbose(data)    ((data) && (data)->set.verbose)
-#define Curl_trc_cf_is_verbose(cf, data) \
-                            ((data) && (data)->set.verbose && \
-                            (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO)
-
-/* explainer: we have some mix configuration and werror settings
- * that define HAVE_VARIADIC_MACROS_C99 even though C89 is enforced
- * on gnuc and some other compiler. Need to treat carefully.
- */
-#if defined(HAVE_VARIADIC_MACROS_C99) && \
-    defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#define CURL_HAVE_C99
+#endif
 
+#ifdef CURL_HAVE_C99
 #define infof(data, ...) \
   do { if(Curl_trc_is_verbose(data)) \
          Curl_infof(data, __VA_ARGS__); } while(0)
@@ -122,29 +85,62 @@ void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
   do { if(Curl_trc_cf_is_verbose(cf, data)) \
          Curl_trc_cf_infof(data, cf, __VA_ARGS__); } while(0)
 
-#else /* no variadic macro args */
+#else
 #define infof Curl_infof
 #define CURL_TRC_CF Curl_trc_cf_infof
-#endif /* variadic macro args */
+#endif
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+/* informational messages enabled */
 
-#else /* !CURL_DISABLE_VERBOSE_STRINGS */
+#define Curl_trc_is_verbose(data)    ((data) && (data)->set.verbose)
+#define Curl_trc_cf_is_verbose(cf, data) \
+                            ((data) && (data)->set.verbose && \
+                            (cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO)
+
+/**
+ * Output an informational message when transfer's verbose logging is enabled.
+ */
+void Curl_infof(struct Curl_easy *data,
+#if defined(__GNUC__) && !defined(printf) && defined(CURL_HAVE_C99) &&  \
+  !defined(__MINGW32__)
+                       const char *fmt, ...)
+                       __attribute__((format(printf, 2, 3)));
+#else
+                       const char *fmt, ...);
+#endif
+
+/**
+ * Output an informational message when both transfer's verbose logging
+ * and connection filters verbose logging are enabled.
+ */
+void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
+#if defined(__GNUC__) && !defined(printf) && defined(CURL_HAVE_C99) && \
+  !defined(__MINGW32__)
+                      const char *fmt, ...)
+                      __attribute__((format(printf, 3, 4)));
+#else
+                      const char *fmt, ...);
+#endif
+
+#else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */
 /* 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)
 
-#if defined(HAVE_VARIADIC_MACROS_C99)
-#define infof(...)  Curl_nop_stmt
-#define CURL_TRC_CF(...)  Curl_nop_stmt
-#define Curl_trc_cf_infof(...)  Curl_nop_stmt
-#elif defined(HAVE_VARIADIC_MACROS_GCC)
-#define infof(x...)  Curl_nop_stmt
-#define CURL_TRC_CF(x...)  Curl_nop_stmt
-#define Curl_trc_cf_infof(x...)  Curl_nop_stmt
-#else
-#error "missing VARIADIC macro define, fix and rebuild!"
-#endif
+static void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
+{
+  (void)data; (void)fmt;
+}
+
+static void Curl_trc_cf_infof(struct Curl_easy *data,
+                              struct Curl_cfilter *cf,
+                              const char *fmt, ...)
+{
+  (void)data; (void)cf; (void)fmt;
+}
 
-#endif /* CURL_DISABLE_VERBOSE_STRINGS */
+#endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */
 
 #endif /* HEADER_CURL_TRC_H */

+ 26 - 17
lib/doh.c

@@ -242,7 +242,7 @@ static CURLcode dohprobe(struct Curl_easy *data,
     /* pass in the struct pointer via a local variable to please coverity and
        the gcc typecheck helpers */
     struct dynbuf *resp = &p->serverdoh;
-    doh->internal = true;
+    doh->state.internal = true;
     ERROR_CHECK_SETOPT(CURLOPT_URL, url);
     ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https");
     ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
@@ -252,6 +252,7 @@ static CURLcode dohprobe(struct Curl_easy *data,
     ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
 #ifdef USE_HTTP2
     ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
+    ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L);
 #endif
 #ifndef CURLDEBUG
     /* enforce HTTPS if not debug */
@@ -339,9 +340,10 @@ static CURLcode dohprobe(struct Curl_easy *data,
     doh->set.dohfor = data; /* identify for which transfer this is done */
     p->easy = doh;
 
-    /* DoH private_data must be null because the user must have a way to
-       distinguish their transfer's handle from DoH handles in user
-       callbacks (ie SSL CTX callback). */
+    /* DoH handles must not inherit private_data. The handles may be passed to
+       the user via callbacks and the user will be able to identify them as
+       internal handles because private data is not set. The user can then set
+       private_data via CURLOPT_PRIVATE if they so choose. */
     DEBUGASSERT(!doh->set.private_data);
 
     if(curl_multi_add_handle(multi, doh))
@@ -372,7 +374,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
   int slot;
   struct dohdata *dohp;
   struct connectdata *conn = data->conn;
-  *waitp = TRUE; /* this never returns synchronously */
+  *waitp = FALSE;
   (void)hostname;
   (void)port;
 
@@ -380,7 +382,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
   DEBUGASSERT(conn);
 
   /* start clean, consider allocating this struct on demand */
-  dohp = data->req.doh = calloc(sizeof(struct dohdata), 1);
+  dohp = data->req.doh = calloc(1, sizeof(struct dohdata));
   if(!dohp)
     return NULL;
 
@@ -412,12 +414,14 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
     dohp->pending++;
   }
 #endif
+  *waitp = TRUE; /* this never returns synchronously */
   return NULL;
 
 error:
   curl_slist_free_all(dohp->headers);
   data->req.doh->headers = NULL;
   for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
+    (void)curl_multi_remove_handle(data->multi, dohp->probe[slot].easy);
     Curl_close(&dohp->probe[slot].easy);
   }
   Curl_safefree(data->req.doh);
@@ -787,8 +791,8 @@ static void showdoh(struct Curl_easy *data,
  * must be an associated call later to Curl_freeaddrinfo().
  */
 
-static struct Curl_addrinfo *
-doh2ai(const struct dohentry *de, const char *hostname, int port)
+static CURLcode doh2ai(const struct dohentry *de, const char *hostname,
+                       int port, struct Curl_addrinfo **aip)
 {
   struct Curl_addrinfo *ai;
   struct Curl_addrinfo *prevai = NULL;
@@ -801,9 +805,10 @@ doh2ai(const struct dohentry *de, const char *hostname, int port)
   int i;
   size_t hostlen = strlen(hostname) + 1; /* include null-terminator */
 
-  if(!de)
-    /* no input == no output! */
-    return NULL;
+  DEBUGASSERT(de);
+
+  if(!de->numaddr)
+    return CURLE_COULDNT_RESOLVE_HOST;
 
   for(i = 0; i < de->numaddr; i++) {
     size_t ss_size;
@@ -876,8 +881,9 @@ doh2ai(const struct dohentry *de, const char *hostname, int port)
     Curl_freeaddrinfo(firstai);
     firstai = NULL;
   }
+  *aip = firstai;
 
-  return firstai;
+  return result;
 }
 
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
@@ -898,6 +904,7 @@ UNITTEST void de_cleanup(struct dohentry *d)
 CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
                               struct Curl_dns_entry **dnsp)
 {
+  struct connectdata *conn = data->conn;
   CURLcode result;
   struct dohdata *dohp = data->req.doh;
   *dnsp = NULL; /* defaults to no response */
@@ -906,7 +913,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
 
   if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
      !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
-    failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
+    failf(data, "Could not DoH-resolve: %s", conn->resolve_async.hostname);
     return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY:
       CURLE_COULDNT_RESOLVE_HOST;
   }
@@ -932,10 +939,12 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
                             p->dnstype,
                             &de);
       Curl_dyn_free(&p->serverdoh);
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
       if(rc[slot]) {
         infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
               type2name(p->dnstype), dohp->host);
       }
+#endif
     } /* next slot */
 
     result = CURLE_COULDNT_RESOLVE_HOST; /* until we know better */
@@ -947,10 +956,10 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
       infof(data, "DoH Host name: %s", dohp->host);
       showdoh(data, &de);
 
-      ai = doh2ai(&de, dohp->host, dohp->port);
-      if(!ai) {
+      result = doh2ai(&de, dohp->host, dohp->port, &ai);
+      if(result) {
         de_cleanup(&de);
-        return CURLE_OUT_OF_MEMORY;
+        return result;
       }
 
       if(data->share)
@@ -967,7 +976,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
         Curl_freeaddrinfo(ai);
       }
       else {
-        data->state.async.dns = dns;
+        conn->resolve_async.dns = dns;
         *dnsp = dns;
         result = CURLE_OK;      /* address resolution OK */
       }

+ 5 - 1
lib/dynbuf.c

@@ -77,6 +77,7 @@ static CURLcode dyn_nappend(struct dynbuf *s,
   DEBUGASSERT(indx < s->toobig);
   DEBUGASSERT(!s->leng || s->bufr);
   DEBUGASSERT(a <= s->toobig);
+  DEBUGASSERT(!len || mem);
 
   if(fit > s->toobig) {
     Curl_dyn_free(s);
@@ -174,10 +175,12 @@ CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
  */
 CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
 {
-  size_t n = strlen(str);
+  size_t n;
+  DEBUGASSERT(str);
   DEBUGASSERT(s);
   DEBUGASSERT(s->init == DYNINIT);
   DEBUGASSERT(!s->leng || s->bufr);
+  n = strlen(str);
   return dyn_nappend(s, (unsigned char *)str, n);
 }
 
@@ -191,6 +194,7 @@ CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
   DEBUGASSERT(s);
   DEBUGASSERT(s->init == DYNINIT);
   DEBUGASSERT(!s->leng || s->bufr);
+  DEBUGASSERT(fmt);
   rc = Curl_dyn_vprintf(s, fmt, ap);
 
   if(!rc)

+ 29 - 0
lib/dynhds.c

@@ -27,6 +27,10 @@
 #include "strcase.h"
 
 /* The last 3 #include files should be in this order */
+#ifdef USE_NGHTTP2
+#include <stdint.h>
+#include <nghttp2/nghttp2.h>
+#endif /* USE_NGHTTP2 */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
@@ -365,3 +369,28 @@ CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf)
   return result;
 }
 
+#ifdef USE_NGHTTP2
+
+nghttp2_nv *Curl_dynhds_to_nva(struct dynhds *dynhds, size_t *pcount)
+{
+  nghttp2_nv *nva = calloc(1, sizeof(nghttp2_nv) * dynhds->hds_len);
+  size_t i;
+
+  *pcount = 0;
+  if(!nva)
+    return NULL;
+
+  for(i = 0; i < dynhds->hds_len; ++i) {
+    struct dynhds_entry *e = dynhds->hds[i];
+    DEBUGASSERT(e);
+    nva[i].name = (unsigned char *)e->name;
+    nva[i].namelen = e->namelen;
+    nva[i].value = (unsigned char *)e->value;
+    nva[i].valuelen = e->valuelen;
+    nva[i].flags = NGHTTP2_NV_FLAG_NONE;
+  }
+  *pcount = dynhds->hds_len;
+  return nva;
+}
+
+#endif /* USE_NGHTTP2 */

+ 9 - 0
lib/dynhds.h

@@ -171,4 +171,13 @@ CURLcode Curl_dynhds_h1_add_line(struct dynhds *dynhds,
  */
 CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf);
 
+#ifdef USE_NGHTTP2
+
+#include <stdint.h>
+#include <nghttp2/nghttp2.h>
+
+nghttp2_nv *Curl_dynhds_to_nva(struct dynhds *dynhds, size_t *pcount);
+
+#endif /* USE_NGHTTP2 */
+
 #endif /* HEADER_CURL_DYNHDS_H */

+ 25 - 59
lib/easy.c

@@ -112,7 +112,7 @@ static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT;
 #define system_strdup strdup
 #endif
 
-#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__)
+#if defined(_MSC_VER) && defined(_DLL)
 #  pragma warning(disable:4232) /* MSVC extension, dllimport identity */
 #endif
 
@@ -125,11 +125,11 @@ curl_free_callback Curl_cfree = (curl_free_callback)free;
 curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc;
 curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup;
 curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
-#if defined(WIN32) && defined(UNICODE)
+#if defined(_WIN32) && defined(UNICODE)
 curl_wcsdup_callback Curl_cwcsdup = Curl_wcsdup;
 #endif
 
-#if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__)
+#if defined(_MSC_VER) && defined(_DLL)
 #  pragma warning(default:4232) /* MSVC extension, dllimport identity */
 #endif
 
@@ -153,7 +153,7 @@ static CURLcode global_init(long flags, bool memoryfuncs)
     Curl_crealloc = (curl_realloc_callback)realloc;
     Curl_cstrdup = (curl_strdup_callback)system_strdup;
     Curl_ccalloc = (curl_calloc_callback)calloc;
-#if defined(WIN32) && defined(UNICODE)
+#if defined(_WIN32) && defined(UNICODE)
     Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup;
 #endif
   }
@@ -188,18 +188,10 @@ static CURLcode global_init(long flags, bool memoryfuncs)
     goto fail;
   }
 
-#if defined(USE_SSH)
   if(Curl_ssh_init()) {
+    DEBUGF(fprintf(stderr, "Error: Curl_ssh_init failed\n"));
     goto fail;
   }
-#endif
-
-#ifdef USE_WOLFSSH
-  if(WS_SUCCESS != wolfSSH_Init()) {
-    DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n"));
-    return CURLE_FAILED_INIT;
-  }
-#endif
 
   easy_init_flags = flags;
 
@@ -295,7 +287,7 @@ void curl_global_cleanup(void)
   Curl_ssl_cleanup();
   Curl_resolver_global_cleanup();
 
-#ifdef WIN32
+#ifdef _WIN32
   Curl_win32_cleanup(easy_init_flags);
 #endif
 
@@ -752,7 +744,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
     return CURLE_RECURSIVE_API_CALL;
 
   /* Copy the MAXCONNECTS option to the multi handle */
-  curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects);
+  curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, (long)data->set.maxconnects);
 
   mcode = curl_multi_add_handle(multi, data);
   if(mcode) {
@@ -845,8 +837,10 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src)
   dst->set = src->set;
   Curl_mime_initpart(&dst->set.mimepost);
 
-  /* clear all string pointers first */
+  /* clear all dest string and blob pointers first, in case we error out
+     mid-function */
   memset(dst->set.str, 0, STRING_LAST * sizeof(char *));
+  memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *));
 
   /* duplicate all strings */
   for(i = (enum dupstring)0; i< STRING_LASTZEROTERMINATED; i++) {
@@ -855,8 +849,6 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src)
       return result;
   }
 
-  /* clear all blob pointers first */
-  memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *));
   /* duplicate all blobs */
   for(j = (enum dupblob)0; j < BLOB_LAST; j++) {
     result = Curl_setblobopt(&dst->set.blobs[j], src->set.blobs[j]);
@@ -866,10 +858,13 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src)
 
   /* duplicate memory areas pointed to */
   i = STRING_COPYPOSTFIELDS;
-  if(src->set.postfieldsize && src->set.str[i]) {
-    /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */
-    dst->set.str[i] = Curl_memdup(src->set.str[i],
-                                  curlx_sotouz(src->set.postfieldsize));
+  if(src->set.str[i]) {
+    if(src->set.postfieldsize == -1)
+      dst->set.str[i] = strdup(src->set.str[i]);
+    else
+      /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */
+      dst->set.str[i] = Curl_memdup(src->set.str[i],
+                                    curlx_sotouz(src->set.postfieldsize));
     if(!dst->set.str[i])
       return CURLE_OUT_OF_MEMORY;
     /* point to the new copy */
@@ -919,18 +914,19 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
   outcurl->progress.callback = data->progress.callback;
 
 #ifndef CURL_DISABLE_COOKIES
-  if(data->cookies) {
+  outcurl->state.cookielist = NULL;
+  if(data->cookies && data->state.cookie_engine) {
     /* If cookies are enabled in the parent handle, we enable them
        in the clone as well! */
-    outcurl->cookies = Curl_cookie_init(data, NULL, outcurl->cookies,
+    outcurl->cookies = Curl_cookie_init(outcurl, NULL, outcurl->cookies,
                                         data->set.cookiesession);
     if(!outcurl->cookies)
       goto fail;
   }
 
-  if(data->set.cookielist) {
-    outcurl->set.cookielist = Curl_slist_duplicate(data->set.cookielist);
-    if(!outcurl->set.cookielist)
+  if(data->state.cookielist) {
+    outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist);
+    if(!outcurl->state.cookielist)
       goto fail;
   }
 #endif
@@ -976,33 +972,6 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
     (void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
   }
 #endif
-  /* Clone the resolver handle, if present, for the new handle */
-  if(Curl_resolver_duphandle(outcurl,
-                             &outcurl->state.async.resolver,
-                             data->state.async.resolver))
-    goto fail;
-
-#ifdef USE_ARES
-  {
-    CURLcode rc;
-
-    rc = Curl_set_dns_servers(outcurl, data->set.str[STRING_DNS_SERVERS]);
-    if(rc && rc != CURLE_NOT_BUILT_IN)
-      goto fail;
-
-    rc = Curl_set_dns_interface(outcurl, data->set.str[STRING_DNS_INTERFACE]);
-    if(rc && rc != CURLE_NOT_BUILT_IN)
-      goto fail;
-
-    rc = Curl_set_dns_local_ip4(outcurl, data->set.str[STRING_DNS_LOCAL_IP4]);
-    if(rc && rc != CURLE_NOT_BUILT_IN)
-      goto fail;
-
-    rc = Curl_set_dns_local_ip6(outcurl, data->set.str[STRING_DNS_LOCAL_IP6]);
-    if(rc && rc != CURLE_NOT_BUILT_IN)
-      goto fail;
-  }
-#endif /* USE_ARES */
 
   Curl_initinfo(outcurl);
 
@@ -1016,13 +985,10 @@ fail:
 
   if(outcurl) {
 #ifndef CURL_DISABLE_COOKIES
-    curl_slist_free_all(outcurl->set.cookielist);
-    outcurl->set.cookielist = NULL;
+    free(outcurl->cookies);
 #endif
-    Curl_safefree(outcurl->state.buffer);
+    free(outcurl->state.buffer);
     Curl_dyn_free(&outcurl->state.headerb);
-    Curl_safefree(outcurl->state.url);
-    Curl_safefree(outcurl->state.referer);
     Curl_altsvc_cleanup(&outcurl->asi);
     Curl_hsts_cleanup(&outcurl->hsts);
     Curl_freeset(outcurl);

+ 9 - 0
lib/easy_lock.h

@@ -93,6 +93,15 @@ static inline void curl_simple_lock_unlock(curl_simple_lock *lock)
   atomic_store_explicit(lock, false, memory_order_release);
 }
 
+#elif defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
+
+#include <pthread.h>
+
+#define curl_simple_lock pthread_mutex_t
+#define CURL_SIMPLE_LOCK_INIT PTHREAD_MUTEX_INITIALIZER
+#define curl_simple_lock_lock(m) pthread_mutex_lock(m)
+#define curl_simple_lock_unlock(m) pthread_mutex_unlock(m)
+
 #else
 
 #undef  GLOBAL_INIT_IS_THREADSAFE

+ 1 - 7
lib/file.c

@@ -69,7 +69,7 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#if defined(WIN32) || defined(MSDOS) || defined(__EMX__)
+#if defined(_WIN32) || defined(MSDOS) || defined(__EMX__)
 #define DOS_FILESYSTEM 1
 #elif defined(__amigaos4__)
 #define AMIGA_FILESYSTEM 1
@@ -414,7 +414,6 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
   bool size_known;
   bool fstated = FALSE;
   char *buf = data->state.buffer;
-  curl_off_t bytecount = 0;
   int fd;
   struct FILEPROTO *file;
 
@@ -563,7 +562,6 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
     if(nread <= 0 || (size_known && (expected_size == 0)))
       break;
 
-    bytecount += nread;
     if(size_known)
       expected_size -= nread;
 
@@ -571,10 +569,6 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
     if(result)
       return result;
 
-    result = Curl_pgrsSetDownloadCounter(data, bytecount);
-    if(result)
-      return result;
-
     if(Curl_pgrsUpdate(data))
       result = CURLE_ABORTED_BY_CALLBACK;
     else

+ 59 - 18
lib/fopen.c

@@ -39,6 +39,51 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
+/*
+  The dirslash() function breaks a null-terminated pathname string into
+  directory and filename components then returns the directory component up
+  to, *AND INCLUDING*, a final '/'.  If there is no directory in the path,
+  this instead returns a "" string.
+
+  This function returns a pointer to malloc'ed memory.
+
+  The input path to this function is expected to have a file name part.
+*/
+
+#ifdef _WIN32
+#define PATHSEP "\\"
+#define IS_SEP(x) (((x) == '/') || ((x) == '\\'))
+#elif defined(MSDOS) || defined(__EMX__) || defined(OS2)
+#define PATHSEP "\\"
+#define IS_SEP(x) ((x) == '\\')
+#else
+#define PATHSEP "/"
+#define IS_SEP(x) ((x) == '/')
+#endif
+
+static char *dirslash(const char *path)
+{
+  size_t n;
+  struct dynbuf out;
+  DEBUGASSERT(path);
+  Curl_dyn_init(&out, CURL_MAX_INPUT_LENGTH);
+  n = strlen(path);
+  if(n) {
+    /* find the rightmost path separator, if any */
+    while(n && !IS_SEP(path[n-1]))
+      --n;
+    /* skip over all the path separators, if any */
+    while(n && IS_SEP(path[n-1]))
+      --n;
+  }
+  if(Curl_dyn_addn(&out, path, n))
+    return NULL;
+  /* if there was a directory, append a single trailing slash */
+  if(n && Curl_dyn_addn(&out, PATHSEP, 1))
+    return NULL;
+  return Curl_dyn_ptr(&out);
+}
+
 /*
  * Curl_fopen() opens a file for writing with a temp name, to be renamed
  * to the final name when completed. If there is an existing file using this
@@ -50,47 +95,44 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
                     FILE **fh, char **tempname)
 {
   CURLcode result = CURLE_WRITE_ERROR;
-  unsigned char randsuffix[9];
+  unsigned char randbuf[41];
   char *tempstore = NULL;
   struct_stat sb;
   int fd = -1;
+  char *dir = NULL;
   *tempname = NULL;
 
   *fh = fopen(filename, FOPEN_WRITETEXT);
   if(!*fh)
     goto fail;
-  if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode))
+  if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) {
     return CURLE_OK;
+  }
   fclose(*fh);
   *fh = NULL;
 
-  result = Curl_rand_alnum(data, randsuffix, sizeof(randsuffix));
+  result = Curl_rand_alnum(data, randbuf, sizeof(randbuf));
   if(result)
     goto fail;
 
-  tempstore = aprintf("%s.%s.tmp", filename, randsuffix);
+  dir = dirslash(filename);
+  if(dir) {
+    /* The temp file name should not end up too long for the target file
+       system */
+    tempstore = aprintf("%s%s.tmp", dir, randbuf);
+    free(dir);
+  }
+
   if(!tempstore) {
     result = CURLE_OUT_OF_MEMORY;
     goto fail;
   }
 
   result = CURLE_WRITE_ERROR;
-  fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600);
+  fd = open(tempstore, O_WRONLY | O_CREAT | O_EXCL, 0600|sb.st_mode);
   if(fd == -1)
     goto fail;
 
-#ifdef HAVE_FCHMOD
-  {
-    struct_stat nsb;
-    if((fstat(fd, &nsb) != -1) &&
-       (nsb.st_uid == sb.st_uid) && (nsb.st_gid == sb.st_gid)) {
-      /* if the user and group are the same, clone the original mode */
-      if(fchmod(fd, (mode_t)sb.st_mode) == -1)
-        goto fail;
-    }
-  }
-#endif
-
   *fh = fdopen(fd, FOPEN_WRITETEXT);
   if(!*fh)
     goto fail;
@@ -105,7 +147,6 @@ fail:
   }
 
   free(tempstore);
-
   return result;
 }
 

+ 4 - 4
lib/formdata.c

@@ -603,9 +603,9 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
            app passed in a bad combo, so we better check for that first. */
         if(form->name) {
           /* copy name (without strdup; possibly not null-terminated) */
-          form->name = Curl_memdup(form->name, form->namelength?
-                                   form->namelength:
-                                   strlen(form->name) + 1);
+          form->name = Curl_strndup(form->name, form->namelength?
+                                    form->namelength:
+                                    strlen(form->name));
         }
         if(!form->name) {
           return_value = CURL_FORMADD_MEMORY;
@@ -792,7 +792,7 @@ static CURLcode setname(curl_mimepart *part, const char *name, size_t len)
 /* wrap call to fseeko so it matches the calling convention of callback */
 static int fseeko_wrapper(void *stream, curl_off_t offset, int whence)
 {
-#if defined(HAVE_FSEEKO)
+#if defined(HAVE_FSEEKO) && defined(HAVE_DECL_FSEEKO)
   return fseeko(stream, (off_t)offset, whence);
 #elif defined(HAVE__FSEEKI64)
   return _fseeki64(stream, (__int64)offset, whence);

+ 3 - 3
lib/ftp.c

@@ -819,7 +819,7 @@ static int ftp_domore_getsock(struct Curl_easy *data,
   DEBUGF(infof(data, "ftp_domore_getsock()"));
   if(conn->cfilter[SECONDARYSOCKET]
      && !Curl_conn_is_connected(conn, SECONDARYSOCKET))
-    return Curl_conn_get_select_socks(data, SECONDARYSOCKET, socks);
+    return 0;
 
   if(FTP_STOP == ftpc->state) {
     int bits = GETSOCK_READSOCK(0);
@@ -947,7 +947,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
     char *port_start = NULL;
     char *port_sep = NULL;
 
-    addr = calloc(addrlen + 1, 1);
+    addr = calloc(1, addrlen + 1);
     if(!addr) {
       result = CURLE_OUT_OF_MEMORY;
       goto out;
@@ -4379,7 +4379,7 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data,
   CURLcode result = CURLE_OK;
   struct ftp_conn *ftpc = &conn->proto.ftpc;
 
-  ftp = calloc(sizeof(struct FTP), 1);
+  ftp = calloc(1, sizeof(struct FTP));
   if(!ftp)
     return CURLE_OUT_OF_MEMORY;
 

+ 0 - 3
lib/ftplistparser.c

@@ -55,9 +55,6 @@
 /* The last #include file should be: */
 #include "memdebug.h"
 
-/* allocs buffer which will contain one line of LIST command response */
-#define FTP_BUFFER_ALLOCSIZE 160
-
 typedef enum {
   PL_UNIX_TOTALSIZE = 0,
   PL_UNIX_FILETYPE,

+ 1 - 1
lib/functypes.h

@@ -38,7 +38,7 @@
    2. For systems with config-*.h files, define them there.
 */
 
-#ifdef WIN32
+#ifdef _WIN32
 /* int recv(SOCKET, char *, int, int) */
 #define RECV_TYPE_ARG1 SOCKET
 #define RECV_TYPE_ARG2 char *

+ 3 - 2
lib/getenv.c

@@ -31,10 +31,11 @@
 
 static char *GetEnv(const char *variable)
 {
-#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP)
+#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_APP) || \
+  defined(__ORBIS__) || defined(__PROSPERO__) /* PlayStation 4 and 5 */
   (void)variable;
   return NULL;
-#elif defined(WIN32)
+#elif defined(_WIN32)
   /* This uses Windows API instead of C runtime getenv() to get the environment
      variable since some changes aren't always visible to the latter. #4774 */
   char *buf = NULL;

+ 6 - 5
lib/hostasyn.c

@@ -67,10 +67,11 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
                                 int status,
                                 struct Curl_addrinfo *ai)
 {
+  struct connectdata *conn = data->conn;
   struct Curl_dns_entry *dns = NULL;
   CURLcode result = CURLE_OK;
 
-  data->state.async.status = status;
+  conn->resolve_async.status = status;
 
   if(CURL_ASYNC_SUCCESS == status) {
     if(ai) {
@@ -78,8 +79,8 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
 
       dns = Curl_cache_addr(data, ai,
-                            data->state.async.hostname, 0,
-                            data->state.async.port);
+                            conn->resolve_async.hostname, 0,
+                            conn->resolve_async.port);
       if(data->share)
         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
 
@@ -94,12 +95,12 @@ CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
     }
   }
 
-  data->state.async.dns = dns;
+  conn->resolve_async.dns = dns;
 
  /* Set async.done TRUE last in this function since it may be used multi-
     threaded and once this is TRUE the other thread may read fields from the
     async struct */
-  data->state.async.done = TRUE;
+  conn->resolve_async.done = TRUE;
 
   /* IPv4: The input hostent struct will be freed by ares when we return from
      this function */

+ 101 - 14
lib/hostip.c

@@ -117,6 +117,13 @@
 
 static void freednsentry(void *freethis);
 
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void show_resolve_info(struct Curl_easy *data,
+                              struct Curl_dns_entry *dns);
+#else
+#define show_resolve_info(x,y) Curl_nop_stmt
+#endif
+
 /*
  * Curl_printable_address() stores a printable version of the 1st address
  * given in the 'ai' argument. The result will be stored in the buf that is
@@ -481,9 +488,11 @@ Curl_cache_addr(struct Curl_easy *data,
       return NULL;
   }
 #endif
+  if(!hostlen)
+    hostlen = strlen(hostname);
 
   /* Create a new cache entry */
-  dns = calloc(1, sizeof(struct Curl_dns_entry));
+  dns = calloc(1, sizeof(struct Curl_dns_entry) + hostlen);
   if(!dns) {
     return NULL;
   }
@@ -497,6 +506,9 @@ Curl_cache_addr(struct Curl_easy *data,
   time(&dns->timestamp);
   if(dns->timestamp == 0)
     dns->timestamp = 1;   /* zero indicates permanent CURLOPT_RESOLVE entry */
+  dns->hostport = port;
+  if(hostlen)
+    memcpy(dns->hostname, hostname, hostlen);
 
   /* Store the resolved data in our DNS cache. */
   dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1,
@@ -521,7 +533,7 @@ static struct Curl_addrinfo *get_localhost6(int port, const char *name)
   struct sockaddr_in6 sa6;
   unsigned char ipv6[16];
   unsigned short port16 = (unsigned short)(port & 0xffff);
-  ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1);
+  ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1);
   if(!ca)
     return NULL;
 
@@ -568,7 +580,7 @@ static struct Curl_addrinfo *get_localhost(int port, const char *name)
     return NULL;
   memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4));
 
-  ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1);
+  ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1);
   if(!ca)
     return NULL;
   ca->ai_flags     = 0;
@@ -729,7 +741,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
       Curl_set_in_callback(data, true);
       st = data->set.resolver_start(
 #ifdef USE_CURL_ASYNC
-        data->state.async.resolver,
+        conn->resolve_async.resolver,
 #else
         NULL,
 #endif
@@ -823,8 +835,10 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
       if(!dns)
         /* returned failure, bail out nicely */
         Curl_freeaddrinfo(addr);
-      else
+      else {
         rc = CURLRESOLV_RESOLVED;
+        show_resolve_info(data, dns);
+      }
     }
   }
 
@@ -839,7 +853,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
  * execution.  This effectively causes the remainder of the application to run
  * within a signal handler which is nonportable and could lead to problems.
  */
-static
+CURL_NORETURN static
 void alarmfunc(int sig)
 {
   (void)sig;
@@ -1269,9 +1283,11 @@ err:
         Curl_freeaddrinfo(head);
         return CURLE_OUT_OF_MEMORY;
       }
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
       infof(data, "Added %.*s:%d:%s to DNS cache%s",
             (int)hlen, host_begin, port, addresses,
             permanent ? "" : " (non-permanent)");
+#endif
 
       /* Wildcard hostname */
       if((hlen == 1) && (host_begin[0] == '*')) {
@@ -1285,18 +1301,89 @@ err:
   return CURLE_OK;
 }
 
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void show_resolve_info(struct Curl_easy *data,
+                              struct Curl_dns_entry *dns)
+{
+  struct Curl_addrinfo *a;
+  CURLcode result = CURLE_OK;
+#ifdef CURLRES_IPV6
+  struct dynbuf out[2];
+#else
+  struct dynbuf out[1];
+#endif
+  DEBUGASSERT(data);
+  DEBUGASSERT(dns);
+
+  if(!data->set.verbose ||
+     /* ignore no name or numerical IP addresses */
+     !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname))
+    return;
+
+  a = dns->addr;
+
+  infof(data, "Host %s:%d was resolved.",
+        (dns->hostname[0] ? dns->hostname : "(none)"), dns->hostport);
+
+  Curl_dyn_init(&out[0], 1024);
+#ifdef CURLRES_IPV6
+  Curl_dyn_init(&out[1], 1024);
+#endif
+
+  while(a) {
+    if(
+#ifdef CURLRES_IPV6
+       a->ai_family == PF_INET6 ||
+#endif
+       a->ai_family == PF_INET) {
+      char buf[MAX_IPADR_LEN];
+      struct dynbuf *d = &out[(a->ai_family != PF_INET)];
+      Curl_printable_address(a, buf, sizeof(buf));
+      if(Curl_dyn_len(d))
+        result = Curl_dyn_addn(d, ", ", 2);
+      if(!result)
+        result = Curl_dyn_add(d, buf);
+      if(result) {
+        infof(data, "too many IP, can't show");
+        goto fail;
+      }
+    }
+    a = a->ai_next;
+  }
+
+#ifdef CURLRES_IPV6
+  infof(data, "IPv6: %s",
+        (Curl_dyn_len(&out[1]) ? Curl_dyn_ptr(&out[1]) : "(none)"));
+#endif
+  infof(data, "IPv4: %s",
+        (Curl_dyn_len(&out[0]) ? Curl_dyn_ptr(&out[0]) : "(none)"));
+
+fail:
+  Curl_dyn_free(&out[0]);
+#ifdef CURLRES_IPV6
+  Curl_dyn_free(&out[1]);
+#endif
+}
+#endif
+
 CURLcode Curl_resolv_check(struct Curl_easy *data,
                            struct Curl_dns_entry **dns)
 {
+  CURLcode result;
 #if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH)
   (void)data;
   (void)dns;
 #endif
 #ifndef CURL_DISABLE_DOH
-  if(data->conn->bits.doh)
-    return Curl_doh_is_resolved(data, dns);
+  if(data->conn->bits.doh) {
+    result = Curl_doh_is_resolved(data, dns);
+  }
+  else
 #endif
-  return Curl_resolver_is_resolved(data, dns);
+  result = Curl_resolver_is_resolved(data, dns);
+  if(*dns)
+    show_resolve_info(data, *dns);
+  return result;
 }
 
 int Curl_resolv_getsock(struct Curl_easy *data,
@@ -1328,9 +1415,9 @@ CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
   struct connectdata *conn = data->conn;
 
 #ifdef USE_CURL_ASYNC
-  if(data->state.async.dns) {
-    conn->dns_entry = data->state.async.dns;
-    data->state.async.dns = NULL;
+  if(conn->resolve_async.dns) {
+    conn->dns_entry = conn->resolve_async.dns;
+    conn->resolve_async.dns = NULL;
   }
 #endif
 
@@ -1352,11 +1439,11 @@ CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
 #ifdef USE_CURL_ASYNC
 CURLcode Curl_resolver_error(struct Curl_easy *data)
 {
+  struct connectdata *conn = data->conn;
   const char *host_or_proxy;
   CURLcode result;
 
 #ifndef CURL_DISABLE_PROXY
-  struct connectdata *conn = data->conn;
   if(conn->bits.httpproxy) {
     host_or_proxy = "proxy";
     result = CURLE_COULDNT_RESOLVE_PROXY;
@@ -1369,7 +1456,7 @@ CURLcode Curl_resolver_error(struct Curl_easy *data)
   }
 
   failf(data, "Could not resolve %s: %s", host_or_proxy,
-        data->state.async.hostname);
+        conn->resolve_async.hostname);
 
   return result;
 }

+ 4 - 0
lib/hostip.h

@@ -64,6 +64,10 @@ struct Curl_dns_entry {
   time_t timestamp;
   /* use-counter, use Curl_resolv_unlock to release reference */
   long inuse;
+  /* hostname port number that resolved to addr. */
+  int hostport;
+  /* hostname that resolved to addr. may be NULL (unix domain sockets). */
+  char hostname[1];
 };
 
 bool Curl_host_is_ipnum(const char *hostname);

+ 3 - 4
lib/hostip6.c

@@ -71,8 +71,7 @@ bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn)
 #if defined(CURLRES_SYNCH)
 
 #ifdef DEBUG_ADDRINFO
-static void dump_addrinfo(struct connectdata *conn,
-                          const struct Curl_addrinfo *ai)
+static void dump_addrinfo(const struct Curl_addrinfo *ai)
 {
   printf("dump_addrinfo:\n");
   for(; ai; ai = ai->ai_next) {
@@ -84,7 +83,7 @@ static void dump_addrinfo(struct connectdata *conn,
   }
 }
 #else
-#define dump_addrinfo(x,y) Curl_nop_stmt
+#define dump_addrinfo(x) Curl_nop_stmt
 #endif
 
 /*
@@ -149,7 +148,7 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
     Curl_addrinfo_set_port(res, port);
   }
 
-  dump_addrinfo(conn, res);
+  dump_addrinfo(res);
 
   return res;
 }

+ 18 - 10
lib/hsts.c

@@ -40,6 +40,7 @@
 #include "fopen.h"
 #include "rename.h"
 #include "share.h"
+#include "strdup.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -76,7 +77,7 @@ static time_t hsts_debugtime(void *unused)
 
 struct hsts *Curl_hsts_init(void)
 {
-  struct hsts *h = calloc(sizeof(struct hsts), 1);
+  struct hsts *h = calloc(1, sizeof(struct hsts));
   if(h) {
     Curl_llist_init(&h->list, NULL);
   }
@@ -108,7 +109,7 @@ void Curl_hsts_cleanup(struct hsts **hp)
 
 static struct stsentry *hsts_entry(void)
 {
-  return calloc(sizeof(struct stsentry), 1);
+  return calloc(1, sizeof(struct stsentry));
 }
 
 static CURLcode hsts_create(struct hsts *h,
@@ -116,23 +117,30 @@ static CURLcode hsts_create(struct hsts *h,
                             bool subdomains,
                             curl_off_t expires)
 {
-  struct stsentry *sts = hsts_entry();
+  struct stsentry *sts;
   char *duphost;
   size_t hlen;
+  DEBUGASSERT(h);
+  DEBUGASSERT(hostname);
+
+  hlen = strlen(hostname);
+  if(hlen && (hostname[hlen - 1] == '.'))
+    /* strip off any trailing dot */
+    --hlen;
+  if(!hlen)
+    /* no host name left */
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+
+  sts = hsts_entry();
   if(!sts)
     return CURLE_OUT_OF_MEMORY;
 
-  duphost = strdup(hostname);
+  duphost = Curl_strndup(hostname, hlen);
   if(!duphost) {
     free(sts);
     return CURLE_OUT_OF_MEMORY;
   }
 
-  hlen = strlen(duphost);
-  if(duphost[hlen - 1] == '.')
-    /* strip off trailing any dot */
-    duphost[--hlen] = 0;
-
   sts->host = duphost;
   sts->expires = expires;
   sts->includeSubDomains = subdomains;
@@ -564,7 +572,7 @@ CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h)
 
 void Curl_hsts_loadfiles(struct Curl_easy *data)
 {
-  struct curl_slist *l = data->set.hstslist;
+  struct curl_slist *l = data->state.hstslist;
   if(l) {
     Curl_share_lock(data, CURL_LOCK_DATA_HSTS, CURL_LOCK_ACCESS_SINGLE);
 

+ 107 - 129
lib/http.c

@@ -836,6 +836,7 @@ output_auth_headers(struct Curl_easy *data,
           (data->state.aptr.user ?
            data->state.aptr.user : ""));
 #else
+    (void)proxy;
     infof(data, "Server auth using %s with user '%s'",
           auth, data->state.aptr.user ?
           data->state.aptr.user : "");
@@ -845,7 +846,7 @@ output_auth_headers(struct Curl_easy *data,
   else
     authstatus->multipass = FALSE;
 
-  return CURLE_OK;
+  return result;
 }
 
 /**
@@ -970,17 +971,21 @@ Curl_http_output_auth(struct Curl_easy *data,
 }
 #endif
 
-/*
- * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate:
- * headers. They are dealt with both in the transfer.c main loop and in the
- * proxy CONNECT loop.
- */
-
+#if defined(USE_SPNEGO) || defined(USE_NTLM) || \
+  !defined(CURL_DISABLE_DIGEST_AUTH) || \
+  !defined(CURL_DISABLE_BASIC_AUTH) || \
+  !defined(CURL_DISABLE_BEARER_AUTH)
 static int is_valid_auth_separator(char ch)
 {
   return ch == '\0' || ch == ',' || ISSPACE(ch);
 }
+#endif
 
+/*
+ * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate:
+ * headers. They are dealt with both in the transfer.c main loop and in the
+ * proxy CONNECT loop.
+ */
 CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
                               const char *auth) /* the first non-space */
 {
@@ -992,11 +997,15 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
   curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state :
                                     &conn->http_negotiate_state;
 #endif
+#if defined(USE_SPNEGO) || \
+  defined(USE_NTLM) || \
+  !defined(CURL_DISABLE_DIGEST_AUTH) || \
+  !defined(CURL_DISABLE_BASIC_AUTH) || \
+  !defined(CURL_DISABLE_BEARER_AUTH)
+
   unsigned long *availp;
   struct auth *authp;
 
-  (void) conn; /* In case conditionals make it unused. */
-
   if(proxy) {
     availp = &data->info.proxyauthavail;
     authp = &data->state.authproxy;
@@ -1005,6 +1014,11 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
     availp = &data->info.httpauthavail;
     authp = &data->state.authhost;
   }
+#else
+  (void) proxy;
+#endif
+
+  (void) conn; /* In case conditionals make it unused. */
 
   /*
    * Here we check if we want the specific single authentication (using ==) and
@@ -1140,7 +1154,14 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
               }
             }
 #else
-           ;
+            {
+              /*
+               * Empty block to terminate the if-else chain correctly.
+               *
+               * A semicolon would yield the same result here, but can cause a
+               * compiler warning when -Wextra is enabled.
+               */
+            }
 #endif
 
     /* there may be multiple methods on one line, so keep reading */
@@ -1403,7 +1424,7 @@ CURLcode Curl_buffer_send(struct dynbuf *in,
      *   and install our own `data->state.fread_func` that
      *   on subsequent calls reads `in` empty.
      * - when the whisked away `in` is empty, the `fread_func`
-     *   is restored ot its original state.
+     *   is restored to its original state.
      * The problem is that `fread_func` can only return
      * `upload_buffer_size` lengths. If the send we do here
      * is larger and blocks, we do re-sending with smaller
@@ -1678,8 +1699,6 @@ static CURLcode expect100(struct Curl_easy *data,
                           struct dynbuf *req)
 {
   CURLcode result = CURLE_OK;
-  data->state.expect100header = FALSE; /* default to false unless it is set
-                                          to TRUE below */
   if(!data->state.disableexpect && Curl_use_http_1_1plus(data, conn) &&
      (conn->httpversion < 20)) {
     /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an
@@ -2414,14 +2433,16 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn,
     /* Convert the form structure into a mime structure, then keep
        the conversion */
     if(!data->state.formp) {
-      data->state.formp = calloc(sizeof(curl_mimepart), 1);
+      data->state.formp = calloc(1, sizeof(curl_mimepart));
       if(!data->state.formp)
         return CURLE_OUT_OF_MEMORY;
       Curl_mime_cleanpart(data->state.formp);
       result = Curl_getformdata(data, data->state.formp, data->set.httppost,
                                 data->state.fread_func);
-      if(result)
+      if(result) {
+        Curl_safefree(data->state.formp);
         return result;
+      }
       data->state.mimepost = data->state.formp;
     }
     break;
@@ -2494,6 +2515,29 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn,
   return result;
 }
 
+static CURLcode addexpect(struct Curl_easy *data, struct connectdata *conn,
+                          struct dynbuf *r)
+{
+  data->state.expect100header = FALSE;
+  /* Avoid Expect: 100-continue if Upgrade: is used */
+  if(data->req.upgr101 == UPGR101_INIT) {
+    struct HTTP *http = data->req.p.http;
+    /* For really small puts we don't use Expect: headers at all, and for
+       the somewhat bigger ones we allow the app to disable it. Just make
+       sure that the expect100header is always set to the preferred value
+       here. */
+    char *ptr = Curl_checkheaders(data, STRCONST("Expect"));
+    if(ptr) {
+      data->state.expect100header =
+        Curl_compareheader(ptr, STRCONST("Expect:"),
+                           STRCONST("100-continue"));
+    }
+    else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0)
+      return expect100(data, conn, r);
+  }
+  return CURLE_OK;
+}
+
 CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
                             struct dynbuf *r, Curl_HttpReq httpreq)
 {
@@ -2506,14 +2550,8 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
 #endif
   CURLcode result = CURLE_OK;
   struct HTTP *http = data->req.p.http;
-  const char *ptr;
-
-  /* If 'authdone' is FALSE, we must not set the write socket index to the
-     Curl_transfer() call below, as we're not ready to actually upload any
-     data yet. */
 
   switch(httpreq) {
-
   case HTTPREQ_PUT: /* Let's PUT the data to the server! */
 
     if(conn->bits.authneg)
@@ -2531,20 +2569,9 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
         return result;
     }
 
-    /* For really small puts we don't use Expect: headers at all, and for
-       the somewhat bigger ones we allow the app to disable it. Just make
-       sure that the expect100header is always set to the preferred value
-       here. */
-    ptr = Curl_checkheaders(data, STRCONST("Expect"));
-    if(ptr) {
-      data->state.expect100header =
-        Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue"));
-    }
-    else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) {
-      result = expect100(data, conn, r);
-      if(result)
-        return result;
-    }
+    result = addexpect(data, conn, r);
+    if(result)
+      return result;
 
     /* end of headers */
     result = Curl_dyn_addn(r, STRCONST("\r\n"));
@@ -2617,22 +2644,9 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
     }
 #endif
 
-    /* For really small posts we don't use Expect: headers at all, and for
-       the somewhat bigger ones we allow the app to disable it. Just make
-       sure that the expect100header is always set to the preferred value
-       here. */
-    ptr = Curl_checkheaders(data, STRCONST("Expect"));
-    if(ptr) {
-      data->state.expect100header =
-        Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue"));
-    }
-    else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) {
-      result = expect100(data, conn, r);
-      if(result)
-        return result;
-    }
-    else
-      data->state.expect100header = FALSE;
+    result = addexpect(data, conn, r);
+    if(result)
+      return result;
 
     /* make the request end in a true CRLF */
     result = Curl_dyn_addn(r, STRCONST("\r\n"));
@@ -2692,22 +2706,9 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
         return result;
     }
 
-    /* For really small posts we don't use Expect: headers at all, and for
-       the somewhat bigger ones we allow the app to disable it. Just make
-       sure that the expect100header is always set to the preferred value
-       here. */
-    ptr = Curl_checkheaders(data, STRCONST("Expect"));
-    if(ptr) {
-      data->state.expect100header =
-        Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue"));
-    }
-    else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) {
-      result = expect100(data, conn, r);
-      if(result)
-        return result;
-    }
-    else
-      data->state.expect100header = FALSE;
+    result = addexpect(data, conn, r);
+    if(result)
+      return result;
 
 #ifndef USE_HYPER
     /* With Hyper the body is always passed on separately */
@@ -3193,7 +3194,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
       DEBUGASSERT(Curl_conn_is_http2(data, conn, FIRSTSOCKET));
     break;
   case CURL_HTTP_VERSION_1_1:
-    /* continue with HTTP/1.1 when explicitly requested */
+    /* continue with HTTP/1.x when explicitly requested */
     break;
   default:
     /* Check if user wants to use HTTP/2 with clear TCP */
@@ -3685,7 +3686,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
           k->content_range = TRUE;
       }
     }
-    else
+    else if(k->httpcode < 300)
       data->state.resume_from = 0; /* get everything */
   }
 #if !defined(CURL_DISABLE_COOKIES)
@@ -3996,35 +3997,30 @@ CURLcode Curl_bump_headersize(struct Curl_easy *data,
  */
 CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
                                      struct connectdata *conn,
-                                     ssize_t *nread,
-                                     bool *stop_reading)
+                                     const char *buf, size_t blen,
+                                     size_t *pconsumed)
 {
   CURLcode result;
   struct SingleRequest *k = &data->req;
-  ssize_t onread = *nread;
-  char *ostr = k->str;
   char *headp;
-  char *str_start;
   char *end_ptr;
 
   /* header line within buffer loop */
+  *pconsumed = 0;
   do {
-    size_t rest_length;
-    size_t full_length;
+    size_t line_length;
     int writetype;
 
-    /* str_start is start of line within buf */
-    str_start = k->str;
-
     /* data is in network encoding so use 0x0a instead of '\n' */
-    end_ptr = memchr(str_start, 0x0a, *nread);
+    end_ptr = memchr(buf, 0x0a, blen);
 
     if(!end_ptr) {
       /* Not a complete header line within buffer, append the data to
          the end of the headerbuff. */
-      result = Curl_dyn_addn(&data->state.headerb, str_start, *nread);
+      result = Curl_dyn_addn(&data->state.headerb, buf, blen);
       if(result)
         return result;
+      *pconsumed += blen;
 
       if(!k->headerline) {
         /* check if this looks like a protocol header */
@@ -4036,31 +4032,28 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
         if(st == STATUS_BAD) {
           /* this is not the beginning of a protocol first header line */
           k->header = FALSE;
-          k->badheader = HEADER_ALLBAD;
+          k->badheader = TRUE;
           streamclose(conn, "bad HTTP: No end-of-message indicator");
           if(!data->set.http09_allowed) {
             failf(data, "Received HTTP/0.9 when not allowed");
             return CURLE_UNSUPPORTED_PROTOCOL;
           }
-          break;
+          goto out;
         }
       }
-
-      break; /* read more and try again */
+      goto out; /* read more and try again */
     }
 
     /* decrease the size of the remaining (supposed) header line */
-    rest_length = (end_ptr - k->str) + 1;
-    *nread -= (ssize_t)rest_length;
-
-    k->str = end_ptr + 1; /* move past new line */
-
-    full_length = k->str - str_start;
-
-    result = Curl_dyn_addn(&data->state.headerb, str_start, full_length);
+    line_length = (end_ptr - buf) + 1;
+    result = Curl_dyn_addn(&data->state.headerb, buf, line_length);
     if(result)
       return result;
 
+    blen -= line_length;
+    buf += line_length;
+    *pconsumed += line_length;
+
     /****
      * We now have a FULL header line in 'headerb'.
      *****/
@@ -4078,14 +4071,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
           return CURLE_UNSUPPORTED_PROTOCOL;
         }
         k->header = FALSE;
-        if(*nread)
+        if(blen)
           /* since there's more, this is a partial bad header */
-          k->badheader = HEADER_PARTHEADER;
+          k->badheader = TRUE;
         else {
           /* this was all we read so it's all a bad header */
-          k->badheader = HEADER_ALLBAD;
-          *nread = onread;
-          k->str = ostr;
+          k->badheader = TRUE;
           return CURLE_OK;
         }
         break;
@@ -4139,22 +4130,23 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
 
             /* switch to http2 now. The bytes after response headers
                are also processed here, otherwise they are lost. */
-            result = Curl_http2_upgrade(data, conn, FIRSTSOCKET,
-                                        k->str, *nread);
+            result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen);
             if(result)
               return result;
-            *nread = 0;
+            *pconsumed += blen;
+            blen = 0;
           }
 #ifdef USE_WEBSOCKETS
           else if(k->upgr101 == UPGR101_WS) {
             /* verify the response */
-            result = Curl_ws_accept(data, k->str, *nread);
+            result = Curl_ws_accept(data, buf, blen);
             if(result)
               return result;
             k->header = FALSE; /* no more header to parse! */
             if(data->set.connect_only) {
               k->keepon &= ~KEEP_RECV; /* read no more content */
-              *nread = 0;
+              *pconsumed += blen;
+              blen = 0;
             }
           }
 #endif
@@ -4366,7 +4358,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
          * out and return home.
          */
         if(data->req.no_body)
-          *stop_reading = TRUE;
+          k->download_done = TRUE;
 #ifndef CURL_DISABLE_RTSP
         else if((conn->handler->protocol & CURLPROTO_RTSP) &&
                 (data->set.rtspreq == RTSPREQ_DESCRIBE) &&
@@ -4375,7 +4367,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
              absent, a length 0 must be assumed.  It will prevent libcurl from
              hanging on DESCRIBE request that got refused for whatever
              reason */
-          *stop_reading = TRUE;
+          k->download_done = TRUE;
 #endif
 
         /* If max download size is *zero* (nothing) we already have
@@ -4386,15 +4378,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
         if(0 == k->maxdownload
            && !Curl_conn_is_http2(data, conn, FIRSTSOCKET)
            && !Curl_conn_is_http3(data, conn, FIRSTSOCKET))
-          *stop_reading = TRUE;
+          k->download_done = TRUE;
 
-        if(*stop_reading) {
-          /* we make sure that this socket isn't read more now */
-          k->keepon &= ~KEEP_RECV;
-        }
-
-        Curl_debug(data, CURLINFO_HEADER_IN, str_start, headerlen);
-        break; /* exit header line loop */
+        Curl_debug(data, CURLINFO_HEADER_IN,
+                   Curl_dyn_ptr(&data->state.headerb),
+                   Curl_dyn_len(&data->state.headerb));
+        goto out; /* exit header line loop */
       }
 
       /* We continue reading headers, reset the line-based header */
@@ -4583,12 +4572,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
 
     Curl_dyn_reset(&data->state.headerb);
   }
-  while(*k->str); /* header line within buffer */
+  while(blen);
 
   /* We might have reached the end of the header part here, but
      there might be a non-header part left in the end of the read
      buffer. */
-
+out:
   return CURLE_OK;
 }
 
@@ -4618,17 +4607,6 @@ out:
   return result;
 }
 
-/* simple implementation of strndup(), which isn't portable */
-static char *my_strndup(const char *ptr, size_t len)
-{
-  char *copy = malloc(len + 1);
-  if(!copy)
-    return NULL;
-  memcpy(copy, ptr, len);
-  copy[len] = '\0';
-  return copy;
-}
-
 CURLcode Curl_http_req_make(struct httpreq **preq,
                             const char *method, size_t m_len,
                             const char *scheme, size_t s_len,
@@ -4647,17 +4625,17 @@ CURLcode Curl_http_req_make(struct httpreq **preq,
     goto out;
   memcpy(req->method, method, m_len);
   if(scheme) {
-    req->scheme = my_strndup(scheme, s_len);
+    req->scheme = Curl_strndup(scheme, s_len);
     if(!req->scheme)
       goto out;
   }
   if(authority) {
-    req->authority = my_strndup(authority, a_len);
+    req->authority = Curl_strndup(authority, a_len);
     if(!req->authority)
       goto out;
   }
   if(path) {
-    req->path = my_strndup(path, p_len);
+    req->path = Curl_strndup(path, p_len);
     if(!req->path)
       goto out;
   }

+ 3 - 3
lib/http.h

@@ -227,8 +227,8 @@ CURLcode Curl_http_size(struct Curl_easy *data);
 
 CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
                                      struct connectdata *conn,
-                                     ssize_t *nread,
-                                     bool *stop_reading);
+                                     const char *buf, size_t blen,
+                                     size_t *pconsumed);
 
 /**
  * Curl_http_output_auth() setups the authentication headers for the
@@ -263,7 +263,7 @@ CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len);
  * All about a core HTTP request, excluding body and trailers
  */
 struct httpreq {
-  char method[12];
+  char method[24];
   char *scheme;
   char *authority;
   char *path;

+ 55 - 79
lib/http2.c

@@ -107,14 +107,14 @@ static int populate_settings(nghttp2_settings_entry *iv,
   return 3;
 }
 
-static size_t populate_binsettings(uint8_t *binsettings,
-                                   struct Curl_easy *data)
+static ssize_t populate_binsettings(uint8_t *binsettings,
+                                    struct Curl_easy *data)
 {
   nghttp2_settings_entry iv[H2_SETTINGS_IV_LEN];
   int ivlen;
 
   ivlen = populate_settings(iv, data);
-  /* this returns number of bytes it wrote */
+  /* this returns number of bytes it wrote or a negative number on error. */
   return nghttp2_pack_settings_payload(binsettings, H2_BINSETTINGS_LEN,
                                        iv, ivlen);
 }
@@ -369,12 +369,15 @@ static ssize_t nw_out_writer(void *writer_ctx,
 {
   struct Curl_cfilter *cf = writer_ctx;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
-  ssize_t nwritten;
 
-  nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err);
-  if(nwritten > 0)
-    CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten);
-  return nwritten;
+  if(data) {
+    ssize_t nwritten = Curl_conn_cf_send(cf->next, data,
+                                         (const char *)buf, buflen, err);
+    if(nwritten > 0)
+      CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten);
+    return nwritten;
+  }
+  return 0;
 }
 
 static ssize_t send_callback(nghttp2_session *h2,
@@ -452,9 +455,14 @@ static CURLcode cf_h2_ctx_init(struct Curl_cfilter *cf,
      * in the H1 request and we upgrade from there. This stream
      * is opened implicitly as #1. */
     uint8_t binsettings[H2_BINSETTINGS_LEN];
-    size_t  binlen; /* length of the binsettings data */
+    ssize_t binlen; /* length of the binsettings data */
 
     binlen = populate_binsettings(binsettings, data);
+    if(binlen <= 0) {
+      failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
+      result = CURLE_FAILED_INIT;
+      goto out;
+    }
 
     result = http2_data_setup(cf, data, &stream);
     if(result)
@@ -1076,16 +1084,11 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
       stream->reset = TRUE;
     }
     stream->send_closed = TRUE;
-    data->req.keepon &= ~KEEP_SEND_HOLD;
     drain_stream(cf, data, stream);
     break;
   case NGHTTP2_WINDOW_UPDATE:
-    if((data->req.keepon & KEEP_SEND_HOLD) &&
-       (data->req.keepon & KEEP_SEND)) {
-      data->req.keepon &= ~KEEP_SEND_HOLD;
+    if(CURL_WANT_SEND(data)) {
       drain_stream(cf, data, stream);
-      CURL_TRC_CF(data, cf, "[%d] un-holding after win update",
-                  stream_id);
     }
     break;
   default:
@@ -1230,15 +1233,10 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
          * window and *assume* that we treat this like a WINDOW_UPDATE. Some
          * servers send an explicit WINDOW_UPDATE, but not all seem to do that.
          * To be safe, we UNHOLD a stream in order not to stall. */
-        if((data->req.keepon & KEEP_SEND_HOLD) &&
-           (data->req.keepon & KEEP_SEND)) {
+        if(CURL_WANT_SEND(data)) {
           struct stream_ctx *stream = H2_STREAM_CTX(data);
-          data->req.keepon &= ~KEEP_SEND_HOLD;
-          if(stream) {
+          if(stream)
             drain_stream(cf, data, stream);
-            CURL_TRC_CF(data, cf, "[%d] un-holding after SETTINGS",
-                        stream_id);
-          }
         }
       }
       break;
@@ -1338,7 +1336,6 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
   stream->error = error_code;
   if(stream->error)
     stream->reset = TRUE;
-  data_s->req.keepon &= ~KEEP_SEND_HOLD;
 
   if(stream->error)
     CURL_TRC_CF(data_s, cf, "[%d] RESET: %s (err %d)",
@@ -1602,10 +1599,10 @@ static int error_callback(nghttp2_session *session,
                           size_t len,
                           void *userp)
 {
+  struct Curl_cfilter *cf = userp;
+  struct Curl_easy *data = CF_DATA_CURRENT(cf);
   (void)session;
-  (void)msg;
-  (void)len;
-  (void)userp;
+  failf(data, "%.*s", (int)len, msg);
   return 0;
 }
 #endif
@@ -1621,7 +1618,7 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
   size_t blen;
   struct SingleRequest *k = &data->req;
   uint8_t binsettings[H2_BINSETTINGS_LEN];
-  size_t  binlen; /* length of the binsettings data */
+  ssize_t binlen; /* length of the binsettings data */
 
   binlen = populate_binsettings(binsettings, data);
   if(binlen <= 0) {
@@ -2052,23 +2049,13 @@ static ssize_t h2_submit(struct stream_ctx **pstream,
   /* no longer needed */
   Curl_h1_req_parse_free(&stream->h1);
 
-  nheader = Curl_dynhds_count(&h2_headers);
-  nva = malloc(sizeof(nghttp2_nv) * nheader);
+  nva = Curl_dynhds_to_nva(&h2_headers, &nheader);
   if(!nva) {
     *err = CURLE_OUT_OF_MEMORY;
     nwritten = -1;
     goto out;
   }
 
-  for(i = 0; i < nheader; ++i) {
-    struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
-    nva[i].name = (unsigned char *)e->name;
-    nva[i].namelen = e->namelen;
-    nva[i].value = (unsigned char *)e->value;
-    nva[i].valuelen = e->valuelen;
-    nva[i].flags = NGHTTP2_NV_FLAG_NONE;
-  }
-
   h2_pri_spec(data, &pri_spec);
   if(!nghttp2_session_check_request_allowed(ctx->h2))
     CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)");
@@ -2272,14 +2259,6 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
      * frame buffer or our network out buffer. */
     size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2,
                                                                 stream->id);
-    if(rwin == 0) {
-      /* H2 flow window exhaustion. We need to HOLD upload until we get
-       * a WINDOW_UPDATE from the server. */
-      data->req.keepon |= KEEP_SEND_HOLD;
-      CURL_TRC_CF(data, cf, "[%d] holding send as remote flow "
-                  "window is exhausted", stream->id);
-    }
-
     /* Whatever the cause, we need to return CURL_EAGAIN for this call.
      * We have unwritten state that needs us being invoked again and EAGAIN
      * is the only way to ensure that. */
@@ -2331,38 +2310,34 @@ out:
   return nwritten;
 }
 
-static int cf_h2_get_select_socks(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data,
-                                  curl_socket_t *sock)
+static void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 struct easy_pollset *ps)
 {
   struct cf_h2_ctx *ctx = cf->ctx;
-  struct SingleRequest *k = &data->req;
-  struct stream_ctx *stream = H2_STREAM_CTX(data);
-  int bitmap = GETSOCK_BLANK;
-  struct cf_call_data save;
+  bool want_recv = CURL_WANT_RECV(data);
+  bool want_send = CURL_WANT_SEND(data);
 
-  CF_DATA_SAVE(save, cf, data);
-  sock[0] = Curl_conn_cf_get_socket(cf, data);
-
-  if(!(k->keepon & (KEEP_RECV_PAUSE|KEEP_RECV_HOLD)))
-    /* Unless paused - in an HTTP/2 connection we can basically always get a
-       frame so we should always be ready for one */
-    bitmap |= GETSOCK_READSOCK(0);
-
-  /* we're (still uploading OR the HTTP/2 layer wants to send data) AND
-     there's a window to send data in */
-  if((((k->keepon & KEEP_SENDBITS) == KEEP_SEND) ||
-      nghttp2_session_want_write(ctx->h2)) &&
-     (nghttp2_session_get_remote_window_size(ctx->h2) &&
-      nghttp2_session_get_stream_remote_window_size(ctx->h2,
-                                                    stream->id)))
-    bitmap |= GETSOCK_WRITESOCK(0);
+  if(ctx->h2 && (want_recv || want_send)) {
+    struct stream_ctx *stream = H2_STREAM_CTX(data);
+    curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
+    struct cf_call_data save;
+    bool c_exhaust, s_exhaust;
 
-  CF_DATA_RESTORE(cf, save);
-  return bitmap;
+    CF_DATA_SAVE(save, cf, data);
+    c_exhaust = !nghttp2_session_get_remote_window_size(ctx->h2);
+    s_exhaust = stream && stream->id >= 0 &&
+                !nghttp2_session_get_stream_remote_window_size(ctx->h2,
+                                                               stream->id);
+    want_recv = (want_recv || c_exhaust || s_exhaust);
+    want_send = (!s_exhaust && want_send) ||
+                (!c_exhaust && nghttp2_session_want_write(ctx->h2));
+
+    Curl_pollset_set(data, ps, sock, want_recv, want_send);
+    CF_DATA_RESTORE(cf, save);
+  }
 }
 
-
 static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
                               struct Curl_easy *data,
                               bool blocking, bool *done)
@@ -2511,14 +2486,15 @@ static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf,
   case CF_CTRL_DATA_PAUSE:
     result = http2_data_pause(cf, data, (arg1 != 0));
     break;
-  case CF_CTRL_DATA_DONE_SEND: {
+  case CF_CTRL_DATA_DONE_SEND:
     result = http2_data_done_send(cf, data);
     break;
-  }
-  case CF_CTRL_DATA_DONE: {
+  case CF_CTRL_DATA_DETACH:
+    http2_data_done(cf, data, TRUE);
+    break;
+  case CF_CTRL_DATA_DONE:
     http2_data_done(cf, data, arg1 != 0);
     break;
-  }
   default:
     break;
   }
@@ -2606,7 +2582,7 @@ struct Curl_cftype Curl_cft_nghttp2 = {
   cf_h2_connect,
   cf_h2_close,
   Curl_cf_def_get_host,
-  cf_h2_get_select_socks,
+  cf_h2_adjust_pollset,
   cf_h2_data_pending,
   cf_h2_send,
   cf_h2_recv,
@@ -2626,7 +2602,7 @@ static CURLcode http2_cfilter_add(struct Curl_cfilter **pcf,
   CURLcode result = CURLE_OUT_OF_MEMORY;
 
   DEBUGASSERT(data->conn);
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx)
     goto out;
 
@@ -2652,7 +2628,7 @@ static CURLcode http2_cfilter_insert_after(struct Curl_cfilter *cf,
   CURLcode result = CURLE_OUT_OF_MEMORY;
 
   (void)data;
-  ctx = calloc(sizeof(*ctx), 1);
+  ctx = calloc(1, sizeof(*ctx));
   if(!ctx)
     goto out;
 

+ 10 - 1
lib/http_aws_sigv4.c

@@ -456,6 +456,7 @@ static CURLcode canon_query(struct Curl_easy *data,
   for(i = 0; !result && (i < entry); i++, ap++) {
     size_t len;
     const char *q = ap->p;
+    bool found_equals = false;
     if(!ap->len)
       continue;
     for(len = ap->len; len && !result; q++, len--) {
@@ -467,9 +468,13 @@ static CURLcode canon_query(struct Curl_easy *data,
         case '.':
         case '_':
         case '~':
+          /* allowed as-is */
+          result = Curl_dyn_addn(dq, q, 1);
+          break;
         case '=':
           /* allowed as-is */
           result = Curl_dyn_addn(dq, q, 1);
+          found_equals = true;
           break;
         case '%':
           /* uppercase the following if hexadecimal */
@@ -497,7 +502,11 @@ static CURLcode canon_query(struct Curl_easy *data,
         }
       }
     }
-    if(i < entry - 1) {
+    if(!result && !found_equals) {
+      /* queries without value still need an equals */
+      result = Curl_dyn_addn(dq, "=", 1);
+    }
+    if(!result && i < entry - 1) {
       /* insert ampersands between query pairs */
       result = Curl_dyn_addn(dq, "&", 1);
     }

+ 41 - 42
lib/http_chunks.c

@@ -75,8 +75,6 @@
 
  */
 
-#define isxdigit_ascii(x) Curl_isxdigit(x)
-
 void Curl_httpchunk_init(struct Curl_easy *data)
 {
   struct connectdata *conn = data->conn;
@@ -98,9 +96,9 @@ void Curl_httpchunk_init(struct Curl_easy *data)
  * For example, 0x0d and 0x0a are used instead of '\r' and '\n'.
  */
 CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
-                              char *datap,
-                              ssize_t datalen,
-                              ssize_t *wrote,
+                              char *buf,
+                              size_t blen,
+                              size_t *pconsumed,
                               CURLcode *extrap)
 {
   CURLcode result = CURLE_OK;
@@ -108,28 +106,27 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
   struct Curl_chunker *ch = &conn->chunk;
   struct SingleRequest *k = &data->req;
   size_t piece;
-  curl_off_t length = (curl_off_t)datalen;
 
-  *wrote = 0; /* nothing's written yet */
+  *pconsumed = 0; /* nothing's written yet */
 
   /* the original data is written to the client, but we go on with the
      chunk read process, to properly calculate the content length */
   if(data->set.http_te_skip && !k->ignorebody) {
-    result = Curl_client_write(data, CLIENTWRITE_BODY, datap, datalen);
+    result = Curl_client_write(data, CLIENTWRITE_BODY, buf, blen);
     if(result) {
       *extrap = result;
       return CHUNKE_PASSTHRU_ERROR;
     }
   }
 
-  while(length) {
+  while(blen) {
     switch(ch->state) {
     case CHUNK_HEX:
-      if(ISXDIGIT(*datap)) {
+      if(ISXDIGIT(*buf)) {
         if(ch->hexindex < CHUNK_MAXNUM_LEN) {
-          ch->hexbuffer[ch->hexindex] = *datap;
-          datap++;
-          length--;
+          ch->hexbuffer[ch->hexindex] = *buf;
+          buf++;
+          blen--;
           ch->hexindex++;
         }
         else {
@@ -143,7 +140,7 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
              a hexadecimal digit. */
           return CHUNKE_ILLEGAL_HEX;
 
-        /* length and datap are unmodified */
+        /* blen and buf are unmodified */
         ch->hexbuffer[ch->hexindex] = 0;
 
         if(curlx_strtoofft(ch->hexbuffer, &endptr, 16, &ch->datasize))
@@ -154,7 +151,7 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
 
     case CHUNK_LF:
       /* waiting for the LF after a chunk size */
-      if(*datap == 0x0a) {
+      if(*buf == 0x0a) {
         /* we're now expecting data to come, unless size was zero! */
         if(0 == ch->datasize) {
           ch->state = CHUNK_TRAILER; /* now check for trailers */
@@ -163,19 +160,21 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
           ch->state = CHUNK_DATA;
       }
 
-      datap++;
-      length--;
+      buf++;
+      blen--;
       break;
 
     case CHUNK_DATA:
-      /* We expect 'datasize' of data. We have 'length' right now, it can be
+      /* We expect 'datasize' of data. We have 'blen' right now, it can be
          more or less than 'datasize'. Get the smallest piece.
       */
-      piece = curlx_sotouz((ch->datasize >= length)?length:ch->datasize);
+      piece = blen;
+      if(ch->datasize < (curl_off_t)blen)
+        piece = curlx_sotouz(ch->datasize);
 
       /* Write the data portion available */
       if(!data->set.http_te_skip && !k->ignorebody) {
-        result = Curl_client_write(data, CLIENTWRITE_BODY, datap, piece);
+        result = Curl_client_write(data, CLIENTWRITE_BODY, buf, piece);
 
         if(result) {
           *extrap = result;
@@ -183,10 +182,10 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
         }
       }
 
-      *wrote += piece;
+      *pconsumed += piece;
       ch->datasize -= piece; /* decrease amount left to expect */
-      datap += piece;    /* move read pointer forward */
-      length -= piece;   /* decrease space left in this round */
+      buf += piece;    /* move read pointer forward */
+      blen -= piece;   /* decrease space left in this round */
 
       if(0 == ch->datasize)
         /* end of data this round, we now expect a trailing CRLF */
@@ -194,18 +193,18 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
       break;
 
     case CHUNK_POSTLF:
-      if(*datap == 0x0a) {
+      if(*buf == 0x0a) {
         /* The last one before we go back to hex state and start all over. */
         Curl_httpchunk_init(data); /* sets state back to CHUNK_HEX */
       }
-      else if(*datap != 0x0d)
+      else if(*buf != 0x0d)
         return CHUNKE_BAD_CHUNK;
-      datap++;
-      length--;
+      buf++;
+      blen--;
       break;
 
     case CHUNK_TRAILER:
-      if((*datap == 0x0d) || (*datap == 0x0a)) {
+      if((*buf == 0x0d) || (*buf == 0x0a)) {
         char *tr = Curl_dyn_ptr(&conn->trailer);
         /* this is the end of a trailer, but if the trailer was zero bytes
            there was no trailer and we move on */
@@ -229,7 +228,7 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
           }
           Curl_dyn_reset(&conn->trailer);
           ch->state = CHUNK_TRAILER_CR;
-          if(*datap == 0x0a)
+          if(*buf == 0x0a)
             /* already on the LF */
             break;
         }
@@ -240,19 +239,19 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
         }
       }
       else {
-        result = Curl_dyn_addn(&conn->trailer, datap, 1);
+        result = Curl_dyn_addn(&conn->trailer, buf, 1);
         if(result)
           return CHUNKE_OUT_OF_MEMORY;
       }
-      datap++;
-      length--;
+      buf++;
+      blen--;
       break;
 
     case CHUNK_TRAILER_CR:
-      if(*datap == 0x0a) {
+      if(*buf == 0x0a) {
         ch->state = CHUNK_TRAILER_POSTCR;
-        datap++;
-        length--;
+        buf++;
+        blen--;
       }
       else
         return CHUNKE_BAD_CHUNK;
@@ -261,27 +260,27 @@ CHUNKcode Curl_httpchunk_read(struct Curl_easy *data,
     case CHUNK_TRAILER_POSTCR:
       /* We enter this state when a CR should arrive so we expect to
          have to first pass a CR before we wait for LF */
-      if((*datap != 0x0d) && (*datap != 0x0a)) {
+      if((*buf != 0x0d) && (*buf != 0x0a)) {
         /* not a CR then it must be another header in the trailer */
         ch->state = CHUNK_TRAILER;
         break;
       }
-      if(*datap == 0x0d) {
+      if(*buf == 0x0d) {
         /* skip if CR */
-        datap++;
-        length--;
+        buf++;
+        blen--;
       }
       /* now wait for the final LF */
       ch->state = CHUNK_STOP;
       break;
 
     case CHUNK_STOP:
-      if(*datap == 0x0a) {
-        length--;
+      if(*buf == 0x0a) {
+        blen--;
 
         /* Record the length of any data left in the end of the buffer
            even if there's no more chunks to read */
-        ch->datasize = curlx_sotouz(length);
+        ch->datasize = blen;
 
         return CHUNKE_STOP; /* return stop */
       }

+ 2 - 2
lib/http_chunks.h

@@ -93,8 +93,8 @@ struct Curl_chunker {
 
 /* The following functions are defined in http_chunks.c */
 void Curl_httpchunk_init(struct Curl_easy *data);
-CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, char *datap,
-                              ssize_t length, ssize_t *wrote,
+CHUNKcode Curl_httpchunk_read(struct Curl_easy *data, char *buf,
+                              size_t blen, size_t *pconsumed,
                               CURLcode *passthru);
 
 #endif /* HEADER_CURL_HTTP_CHUNKS_H */

+ 1 - 1
lib/http_proxy.c

@@ -299,7 +299,7 @@ struct Curl_cftype Curl_cft_http_proxy = {
   http_proxy_cf_connect,
   http_proxy_cf_close,
   Curl_cf_http_proxy_get_host,
-  Curl_cf_def_get_select_socks,
+  Curl_cf_def_adjust_pollset,
   Curl_cf_def_data_pending,
   Curl_cf_def_send,
   Curl_cf_def_recv,

+ 1 - 1
lib/idn.c

@@ -36,7 +36,7 @@
 #ifdef USE_LIBIDN2
 #include <idn2.h>
 
-#if defined(WIN32) && defined(UNICODE)
+#if defined(_WIN32) && defined(UNICODE)
 #define IDN2_LOOKUP(name, host, flags)                                  \
   idn2_lookup_u8((const uint8_t *)name, (uint8_t **)host, flags)
 #else

+ 1 - 3
lib/imap.c

@@ -1194,8 +1194,6 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
       if(result)
         return result;
 
-      data->req.bytecount += chunk;
-
       infof(data, "Written %zu bytes, %" CURL_FORMAT_CURL_OFF_TU
             " bytes are left for transfer", chunk, size - chunk);
 
@@ -1430,7 +1428,7 @@ static CURLcode imap_init(struct Curl_easy *data)
   CURLcode result = CURLE_OK;
   struct IMAP *imap;
 
-  imap = data->req.p.imap = calloc(sizeof(struct IMAP), 1);
+  imap = data->req.p.imap = calloc(1, sizeof(struct IMAP));
   if(!imap)
     result = CURLE_OUT_OF_MEMORY;
 

+ 3 - 17
lib/ldap.c

@@ -313,7 +313,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
   int ldap_ssl = 0;
   char *val_b64 = NULL;
   size_t val_b64_sz = 0;
-  curl_off_t dlsize = 0;
 #ifdef LDAP_OPT_NETWORK_TIMEOUT
   struct timeval ldap_timeout = {10, 0}; /* 10 sec connection/search timeout */
 #endif
@@ -327,7 +326,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
 
   *done = TRUE; /* unconditionally */
   infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d",
-          LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION);
+        LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION);
   infof(data, "LDAP local: %s", data->state.url);
 
 #ifdef HAVE_LDAP_URL_PARSE
@@ -345,7 +344,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
   if(conn->given->flags & PROTOPT_SSL)
     ldap_ssl = 1;
   infof(data, "LDAP local: trying to establish %s connection",
-          ldap_ssl ? "encrypted" : "cleartext");
+        ldap_ssl ? "encrypted" : "cleartext");
 
 #if defined(USE_WIN32_LDAP)
   host = curlx_convert_UTF8_to_tchar(conn->host.name);
@@ -535,6 +534,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
     goto quit;
   }
 
+  Curl_pgrsSetDownloadCounter(data, 0);
   rc = ldap_search_s(server, ludp->lud_dn, ludp->lud_scope,
                      ludp->lud_filter, ludp->lud_attrs, 0, &ldapmsg);
 
@@ -596,8 +596,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
         goto quit;
       }
 
-      dlsize += name_len + 5;
-
       FREE_ON_WINLDAP(name);
       ldap_memfree(dn);
     }
@@ -659,8 +657,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
             goto quit;
           }
 
-          dlsize += attr_len + 3;
-
           if((attr_len > 7) &&
              (strcmp(";binary", attr + (attr_len - 7)) == 0)) {
             /* Binary attribute, encode to base64. */
@@ -689,8 +685,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
 
                 goto quit;
               }
-
-              dlsize += val_b64_sz;
             }
           }
           else {
@@ -705,8 +699,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
 
               goto quit;
             }
-
-            dlsize += vals[i]->bv_len;
           }
 
           result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
@@ -719,8 +711,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
 
             goto quit;
           }
-
-          dlsize++;
         }
 
         /* Free memory used to store values */
@@ -734,10 +724,6 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
       result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
       if(result)
         goto quit;
-      dlsize++;
-      result = Curl_pgrsSetDownloadCounter(data, dlsize);
-      if(result)
-        goto quit;
     }
 
     if(ber)

+ 2 - 3
lib/md4.c

@@ -32,9 +32,8 @@
 #include "warnless.h"
 
 #ifdef USE_OPENSSL
-#include <openssl/opensslconf.h>
-#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3) && \
-   !defined(USE_AMISSL)
+#include <openssl/opensslv.h>
+#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) && !defined(USE_AMISSL)
 /* OpenSSL 3.0.0 marks the MD4 functions as deprecated */
 #define OPENSSL_NO_MD4
 #endif

+ 1 - 1
lib/memdebug.c

@@ -208,7 +208,7 @@ ALLOC_FUNC char *curl_dbg_strdup(const char *str,
   return mem;
 }
 
-#if defined(WIN32) && defined(UNICODE)
+#if defined(_WIN32) && defined(UNICODE)
 ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str,
                                     int line, const char *source)
 {

+ 2 - 2
lib/memdebug.h

@@ -64,7 +64,7 @@ CURL_EXTERN ALLOC_SIZE(2) void *curl_dbg_realloc(void *ptr,
 CURL_EXTERN void curl_dbg_free(void *ptr, int line, const char *source);
 CURL_EXTERN ALLOC_FUNC char *curl_dbg_strdup(const char *str, int line,
                                              const char *src);
-#if defined(WIN32) && defined(UNICODE)
+#if defined(_WIN32) && defined(UNICODE)
 CURL_EXTERN ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str,
                                                 int line,
                                                 const char *source);
@@ -121,7 +121,7 @@ CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source);
 #define send(a,b,c,d) curl_dbg_send(a,b,c,d, __LINE__, __FILE__)
 #define recv(a,b,c,d) curl_dbg_recv(a,b,c,d, __LINE__, __FILE__)
 
-#ifdef WIN32
+#ifdef _WIN32
 #  ifdef UNICODE
 #    undef wcsdup
 #    define wcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__)

+ 2 - 3
lib/mime.c

@@ -48,7 +48,7 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#ifdef WIN32
+#ifdef _WIN32
 # ifndef R_OK
 #  define R_OK 4
 # endif
@@ -311,8 +311,7 @@ static char *escape_string(struct Curl_easy *data,
   table = formtable;
   /* data can be NULL when this function is called indirectly from
      curl_formget(). */
-  if(strategy == MIMESTRATEGY_MAIL ||
-     (data && (data->set.mime_options & CURLMIMEOPT_FORMESCAPE)))
+  if(strategy == MIMESTRATEGY_MAIL || (data && (data->set.mime_formescape)))
     table = mimetable;
 
   Curl_dyn_init(&db, CURL_MAX_INPUT_LENGTH);

+ 1 - 6
lib/mprintf.c

@@ -66,9 +66,7 @@
  * Non-ANSI integer extensions
  */
 
-#if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x520)) || \
-    (defined(__POCC__) && defined(_MSC_VER)) || \
-    (defined(_WIN32_WCE)) || \
+#if (defined(_WIN32_WCE)) || \
     (defined(__MINGW32__)) || \
     (defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64))
 #  define MP_HAVE_INT_EXTENSIONS
@@ -1071,9 +1069,6 @@ static int alloc_addbyter(int output, FILE *data)
   return outc; /* fputc() returns like this on success */
 }
 
-extern int Curl_dyn_vprintf(struct dynbuf *dyn,
-                            const char *format, va_list ap_save);
-
 /* appends the formatted string, returns 0 on success, 1 on error */
 int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
 {

+ 0 - 9
lib/mqtt.c

@@ -616,9 +616,6 @@ static void mqstate(struct Curl_easy *data,
 }
 
 
-/* for the publish packet */
-#define MQTT_HEADER_LEN 5    /* max 5 bytes */
-
 static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done)
 {
   CURLcode result = CURLE_OK;
@@ -677,7 +674,6 @@ MQTT_SUBACK_COMING:
     /* FALLTHROUGH */
   case MQTT_PUB_REMAIN: {
     /* read rest of packet, but no more. Cap to buffer size */
-    struct SingleRequest *k = &data->req;
     size_t rest = mq->npacket;
     if(rest > (size_t)data->set.buffer_size)
       rest = (size_t)data->set.buffer_size;
@@ -693,13 +689,8 @@ MQTT_SUBACK_COMING:
       result = CURLE_PARTIAL_FILE;
       goto end;
     }
-    Curl_debug(data, CURLINFO_DATA_IN, (char *)pkt, (size_t)nread);
 
     mq->npacket -= nread;
-    k->bytecount += nread;
-    result = Curl_pgrsSetDownloadCounter(data, k->bytecount);
-    if(result)
-      goto end;
 
     /* if QoS is set, message contains packet id */
 

+ 121 - 174
lib/multi.c

@@ -55,22 +55,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#ifdef __APPLE__
-
-#define wakeup_write  write
-#define wakeup_read   read
-#define wakeup_close  close
-#define wakeup_create pipe
-
-#else /* __APPLE__ */
-
-#define wakeup_write     swrite
-#define wakeup_read      sread
-#define wakeup_close     sclose
-#define wakeup_create(p) Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, p)
-
-#endif /* __APPLE__ */
-
 /*
   CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97
   to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes.  Still, every
@@ -231,10 +215,6 @@ struct Curl_sh_entry {
   unsigned int readers; /* this many transfers want to read */
   unsigned int writers; /* this many transfers want to write */
 };
-/* bits for 'action' having no bits means this socket is not expecting any
-   action */
-#define SH_READ  1
-#define SH_WRITE 2
 
 /* look up a given socket in the socket hash, skip invalid sockets */
 static struct Curl_sh_entry *sh_getentry(struct Curl_hash *sh,
@@ -416,9 +396,6 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
   Curl_llist_init(&multi->msgsent, NULL);
 
   multi->multiplexing = TRUE;
-
-  /* -1 means it not set by user, use the default value */
-  multi->maxconnects = -1;
   multi->max_concurrent_streams = 100;
 
 #ifdef USE_WINSOCK
@@ -1040,49 +1017,61 @@ static int protocol_getsock(struct Curl_easy *data,
 {
   if(conn->handler->proto_getsock)
     return conn->handler->proto_getsock(data, conn, socks);
-  return Curl_conn_get_select_socks(data, FIRSTSOCKET, socks);
+  return GETSOCK_BLANK;
 }
 
-/* returns bitmapped flags for this handle and its sockets. The 'socks[]'
-   array contains MAX_SOCKSPEREASYHANDLE entries. */
-static int multi_getsock(struct Curl_easy *data,
-                         curl_socket_t *socks)
+/* Initializes `poll_set` with the current socket poll actions needed
+ * for transfer `data`. */
+static void multi_getsock(struct Curl_easy *data,
+                          struct easy_pollset *ps)
 {
-  struct connectdata *conn = data->conn;
   /* The no connection case can happen when this is called from
      curl_multi_remove_handle() => singlesocket() => multi_getsock().
   */
-  if(!conn)
-    return 0;
+  Curl_pollset_reset(data, ps);
+  if(!data->conn)
+    return;
 
   switch(data->mstate) {
   default:
-    return 0;
+    break;
 
   case MSTATE_RESOLVING:
-    return Curl_resolv_getsock(data, socks);
+    Curl_pollset_add_socks2(data, ps, Curl_resolv_getsock);
+    /* connection filters are not involved in this phase */
+    return;
 
   case MSTATE_PROTOCONNECTING:
   case MSTATE_PROTOCONNECT:
-    return protocol_getsock(data, conn, socks);
+    Curl_pollset_add_socks(data, ps, protocol_getsock);
+    break;
 
   case MSTATE_DO:
   case MSTATE_DOING:
-    return doing_getsock(data, conn, socks);
+    Curl_pollset_add_socks(data, ps, doing_getsock);
+    break;
 
   case MSTATE_TUNNELING:
   case MSTATE_CONNECTING:
-    return Curl_conn_get_select_socks(data, FIRSTSOCKET, socks);
+    break;
 
   case MSTATE_DOING_MORE:
-    return domore_getsock(data, conn, socks);
+    Curl_pollset_add_socks(data, ps, domore_getsock);
+    break;
 
   case MSTATE_DID: /* since is set after DO is completed, we switch to
                         waiting for the same as the PERFORMING state */
   case MSTATE_PERFORMING:
-    return Curl_single_getsock(data, conn, socks);
+    Curl_pollset_add_socks(data, ps, Curl_single_getsock);
+    break;
+
+  case MSTATE_RATELIMITING:
+    /* nothing to wait for */
+    return;
   }
 
+  /* Let connection filters add/remove as needed */
+  Curl_conn_adjust_pollset(data, ps);
 }
 
 CURLMcode curl_multi_fdset(struct Curl_multi *multi,
@@ -1094,8 +1083,8 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi,
      and then we must make sure that is done. */
   struct Curl_easy *data;
   int this_max_fd = -1;
-  curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
-  int i;
+  struct easy_pollset ps;
+  unsigned int i;
   (void)exc_fd_set; /* not used */
 
   if(!GOOD_MULTI_HANDLE(multi))
@@ -1104,29 +1093,20 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi,
   if(multi->in_callback)
     return CURLM_RECURSIVE_API_CALL;
 
+  memset(&ps, 0, sizeof(ps));
   for(data = multi->easyp; data; data = data->next) {
-    int bitmap;
-#ifdef __clang_analyzer_
-    /* to prevent "The left operand of '>=' is a garbage value" warnings */
-    memset(sockbunch, 0, sizeof(sockbunch));
-#endif
-    bitmap = multi_getsock(data, sockbunch);
-
-    for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
-      if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) {
-        if(!FDSET_SOCK(sockbunch[i]))
-          /* pretend it doesn't exist */
-          continue;
-        if(bitmap & GETSOCK_READSOCK(i))
-          FD_SET(sockbunch[i], read_fd_set);
-        if(bitmap & GETSOCK_WRITESOCK(i))
-          FD_SET(sockbunch[i], write_fd_set);
-        if((int)sockbunch[i] > this_max_fd)
-          this_max_fd = (int)sockbunch[i];
-      }
-      else {
-        break;
-      }
+    multi_getsock(data, &ps);
+
+    for(i = 0; i < ps.num; i++) {
+      if(!FDSET_SOCK(ps.sockets[i]))
+        /* pretend it doesn't exist */
+        continue;
+      if(ps.actions[i] & CURL_POLL_IN)
+        FD_SET(ps.sockets[i], read_fd_set);
+      if(ps.actions[i] & CURL_POLL_OUT)
+        FD_SET(ps.sockets[i], write_fd_set);
+      if((int)ps.sockets[i] > this_max_fd)
+        this_max_fd = (int)ps.sockets[i];
     }
   }
 
@@ -1162,9 +1142,8 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
                             bool use_wakeup)
 {
   struct Curl_easy *data;
-  curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
-  int bitmap;
-  unsigned int i;
+  struct easy_pollset ps;
+  size_t i;
   unsigned int nfds = 0;
   unsigned int curlfds;
   long timeout_internal;
@@ -1190,17 +1169,10 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
     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) {
-    bitmap = multi_getsock(data, sockbunch);
-
-    for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) {
-      if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) {
-        ++nfds;
-      }
-      else {
-        break;
-      }
-    }
+    multi_getsock(data, &ps);
+    nfds += ps.num;
   }
 
   /* If the internally desired timeout is actually shorter than requested from
@@ -1241,40 +1213,35 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
   if(curlfds) {
     /* Add the curl handles to our pollfds first */
     for(data = multi->easyp; data; data = data->next) {
-      bitmap = multi_getsock(data, sockbunch);
+      multi_getsock(data, &ps);
 
-      for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) {
-        if((bitmap & GETSOCK_MASK_RW(i)) && VALID_SOCK((sockbunch[i]))) {
-          struct pollfd *ufd = &ufds[nfds++];
-#ifdef USE_WINSOCK
-          long mask = 0;
-#endif
-          ufd->fd = sockbunch[i];
-          ufd->events = 0;
-          if(bitmap & GETSOCK_READSOCK(i)) {
+      for(i = 0; i < ps.num; i++) {
+        struct pollfd *ufd = &ufds[nfds++];
 #ifdef USE_WINSOCK
-            mask |= FD_READ|FD_ACCEPT|FD_CLOSE;
+        long mask = 0;
 #endif
-            ufd->events |= POLLIN;
-          }
-          if(bitmap & GETSOCK_WRITESOCK(i)) {
+        ufd->fd = ps.sockets[i];
+        ufd->events = 0;
+        if(ps.actions[i] & CURL_POLL_IN) {
 #ifdef USE_WINSOCK
-            mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
-            reset_socket_fdwrite(sockbunch[i]);
+          mask |= FD_READ|FD_ACCEPT|FD_CLOSE;
 #endif
-            ufd->events |= POLLOUT;
-          }
+          ufd->events |= POLLIN;
+        }
+        if(ps.actions[i] & CURL_POLL_OUT) {
 #ifdef USE_WINSOCK
-          if(WSAEventSelect(sockbunch[i], multi->wsa_event, mask) != 0) {
-            if(ufds_malloc)
-              free(ufds);
-            return CURLM_INTERNAL_ERROR;
-          }
+          mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
+          reset_socket_fdwrite(ps.sockets[i]);
 #endif
+          ufd->events |= POLLOUT;
         }
-        else {
-          break;
+#ifdef USE_WINSOCK
+        if(WSAEventSelect(ps.sockets[i], multi->wsa_event, mask) != 0) {
+          if(ufds_malloc)
+            free(ufds);
+          return CURLM_INTERNAL_ERROR;
         }
+#endif
       }
     }
   }
@@ -1386,21 +1353,16 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
       if(curlfds) {
 
         for(data = multi->easyp; data; data = data->next) {
-          bitmap = multi_getsock(data, sockbunch);
-
-          for(i = 0; i < MAX_SOCKSPEREASYHANDLE; i++) {
-            if(bitmap & (GETSOCK_READSOCK(i) | GETSOCK_WRITESOCK(i))) {
-              wsa_events.lNetworkEvents = 0;
-              if(WSAEnumNetworkEvents(sockbunch[i], NULL, &wsa_events) == 0) {
-                if(ret && !pollrc && wsa_events.lNetworkEvents)
-                  retcode++;
-              }
-              WSAEventSelect(sockbunch[i], multi->wsa_event, 0);
-            }
-            else {
-              /* break on entry not checked for being readable or writable */
-              break;
+          multi_getsock(data, &ps);
+
+          for(i = 0; i < ps.num; i++) {
+            wsa_events.lNetworkEvents = 0;
+            if(WSAEnumNetworkEvents(ps.sockets[i], NULL,
+                                    &wsa_events) == 0) {
+              if(ret && !pollrc && wsa_events.lNetworkEvents)
+                retcode++;
             }
+            WSAEventSelect(ps.sockets[i], multi->wsa_event, 0);
           }
         }
       }
@@ -2021,8 +1983,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
 
       if(dns) {
 #ifdef CURLRES_ASYNCH
-        data->state.async.dns = dns;
-        data->state.async.done = TRUE;
+        conn->resolve_async.dns = dns;
+        conn->resolve_async.done = TRUE;
 #endif
         result = CURLE_OK;
         infof(data, "Hostname '%s' was found in DNS cache", hostname);
@@ -2895,53 +2857,36 @@ CURLMsg *curl_multi_info_read(struct Curl_multi *multi, int *msgs_in_queue)
 static CURLMcode singlesocket(struct Curl_multi *multi,
                               struct Curl_easy *data)
 {
-  curl_socket_t socks[MAX_SOCKSPEREASYHANDLE];
-  int i;
+  struct easy_pollset cur_poll;
+  unsigned int i;
   struct Curl_sh_entry *entry;
   curl_socket_t s;
-  int num;
-  unsigned int curraction;
-  unsigned char actions[MAX_SOCKSPEREASYHANDLE];
   int rc;
 
-  for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++)
-    socks[i] = CURL_SOCKET_BAD;
-
   /* Fill in the 'current' struct with the state as it is now: what sockets to
      supervise and for what actions */
-  curraction = multi_getsock(data, socks);
+  multi_getsock(data, &cur_poll);
 
   /* We have 0 .. N sockets already and we get to know about the 0 .. M
      sockets we should have from now on. Detect the differences, remove no
      longer supervised ones and add new ones */
 
   /* walk over the sockets we got right now */
-  for(i = 0; (i< MAX_SOCKSPEREASYHANDLE) &&
-      (curraction & GETSOCK_MASK_RW(i));
-      i++) {
-    unsigned char action = CURL_POLL_NONE;
-    unsigned char prevaction = 0;
+  for(i = 0; i < cur_poll.num; i++) {
+    unsigned char cur_action = cur_poll.actions[i];
+    unsigned char last_action = 0;
     int comboaction;
-    bool sincebefore = FALSE;
 
-    s = socks[i];
+    s = cur_poll.sockets[i];
 
     /* get it from the hash */
     entry = sh_getentry(&multi->sockhash, s);
-
-    if(curraction & GETSOCK_READSOCK(i))
-      action |= CURL_POLL_IN;
-    if(curraction & GETSOCK_WRITESOCK(i))
-      action |= CURL_POLL_OUT;
-
-    actions[i] = action;
     if(entry) {
       /* check if new for this transfer */
-      int j;
-      for(j = 0; j< data->numsocks; j++) {
-        if(s == data->sockets[j]) {
-          prevaction = data->actions[j];
-          sincebefore = TRUE;
+      unsigned int j;
+      for(j = 0; j< data->last_poll.num; j++) {
+        if(s == data->last_poll.sockets[j]) {
+          last_action = data->last_poll.actions[j];
           break;
         }
       }
@@ -2953,23 +2898,23 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
         /* fatal */
         return CURLM_OUT_OF_MEMORY;
     }
-    if(sincebefore && (prevaction != action)) {
+    if(last_action && (last_action != cur_action)) {
       /* Socket was used already, but different action now */
-      if(prevaction & CURL_POLL_IN)
+      if(last_action & CURL_POLL_IN)
         entry->readers--;
-      if(prevaction & CURL_POLL_OUT)
+      if(last_action & CURL_POLL_OUT)
         entry->writers--;
-      if(action & CURL_POLL_IN)
+      if(cur_action & CURL_POLL_IN)
         entry->readers++;
-      if(action & CURL_POLL_OUT)
+      if(cur_action & CURL_POLL_OUT)
         entry->writers++;
     }
-    else if(!sincebefore) {
-      /* a new user */
+    else if(!last_action) {
+      /* a new transfer using this socket */
       entry->users++;
-      if(action & CURL_POLL_IN)
+      if(cur_action & CURL_POLL_IN)
         entry->readers++;
-      if(action & CURL_POLL_OUT)
+      if(cur_action & CURL_POLL_OUT)
         entry->writers++;
 
       /* add 'data' to the transfer hash on this socket! */
@@ -2980,11 +2925,11 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
       }
     }
 
-    comboaction = (entry->writers? CURL_POLL_OUT : 0) |
+    comboaction = (entry->writers ? CURL_POLL_OUT : 0) |
                    (entry->readers ? CURL_POLL_IN : 0);
 
     /* socket existed before and has the same action set as before */
-    if(sincebefore && ((int)entry->action == comboaction))
+    if(last_action && ((int)entry->action == comboaction))
       /* same, continue */
       continue;
 
@@ -2992,6 +2937,7 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
       set_in_callback(multi, TRUE);
       rc = multi->socket_cb(data, s, comboaction, multi->socket_userp,
                             entry->socketp);
+
       set_in_callback(multi, FALSE);
       if(rc == -1) {
         multi->dead = TRUE;
@@ -3002,16 +2948,15 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
     entry->action = comboaction; /* store the current action state */
   }
 
-  num = i; /* number of sockets */
-
-  /* when we've walked over all the sockets we should have right now, we must
-     make sure to detect sockets that are removed */
-  for(i = 0; i< data->numsocks; i++) {
-    int j;
+  /* Check for last_poll.sockets that no longer appear in cur_poll.sockets.
+   * Need to remove the easy handle from the multi->sockhash->transfers and
+   * remove multi->sockhash entry when this was the last transfer */
+  for(i = 0; i< data->last_poll.num; i++) {
+    unsigned int j;
     bool stillused = FALSE;
-    s = data->sockets[i];
-    for(j = 0; j < num; j++) {
-      if(s == socks[j]) {
+    s = data->last_poll.sockets[i];
+    for(j = 0; j < cur_poll.num; j++) {
+      if(s == cur_poll.sockets[j]) {
         /* this is still supervised */
         stillused = TRUE;
         break;
@@ -3024,7 +2969,7 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
     /* if this is NULL here, the socket has been closed and notified so
        already by Curl_multi_closed() */
     if(entry) {
-      unsigned char oldactions = data->actions[i];
+      unsigned char oldactions = data->last_poll.actions[i];
       /* this socket has been removed. Decrease user count */
       entry->users--;
       if(oldactions & CURL_POLL_OUT)
@@ -3052,11 +2997,10 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
         }
       }
     }
-  } /* for loop over numsocks */
+  } /* for loop over num */
 
-  memcpy(data->sockets, socks, num*sizeof(curl_socket_t));
-  memcpy(data->actions, actions, num*sizeof(char));
-  data->numsocks = num;
+  /* Remember for next time */
+  memcpy(&data->last_poll, &cur_poll, sizeof(data->last_poll));
   return CURLM_OK;
 }
 
@@ -3296,6 +3240,7 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi,
 {
   CURLMcode res = CURLM_OK;
   va_list param;
+  unsigned long uarg;
 
   if(!GOOD_MULTI_HANDLE(multi))
     return CURLM_BAD_HANDLE;
@@ -3328,7 +3273,9 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi,
     multi->timer_userp = va_arg(param, void *);
     break;
   case CURLMOPT_MAXCONNECTS:
-    multi->maxconnects = va_arg(param, long);
+    uarg = va_arg(param, unsigned long);
+    if(uarg <= UINT_MAX)
+      multi->maxconnects = (unsigned int)uarg;
     break;
   case CURLMOPT_MAX_HOST_CONNECTIONS:
     multi->max_host_connections = va_arg(param, long);
@@ -3350,9 +3297,9 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi,
   case CURLMOPT_MAX_CONCURRENT_STREAMS:
     {
       long streams = va_arg(param, long);
-      if(streams < 1)
+      if((streams < 1) || (streams > INT_MAX))
         streams = 100;
-      multi->max_concurrent_streams = curlx_sltoui(streams);
+      multi->max_concurrent_streams = (unsigned int)streams;
     }
     break;
   default:
@@ -3782,11 +3729,11 @@ struct Curl_easy **curl_multi_get_handles(struct Curl_multi *multi)
   struct Curl_easy **a = malloc(sizeof(struct Curl_easy *) *
                                 (multi->num_easy + 1));
   if(a) {
-    int i = 0;
+    unsigned int i = 0;
     struct Curl_easy *e = multi->easyp;
     while(e) {
       DEBUGASSERT(i < multi->num_easy);
-      if(!e->internal)
+      if(!e->state.internal)
         a[i++] = e;
       e = e->next;
     }

+ 7 - 8
lib/multihandle.h

@@ -93,9 +93,9 @@ struct Curl_multi {
   struct Curl_easy *easyp;
   struct Curl_easy *easylp; /* last node */
 
-  int num_easy; /* amount of entries in the linked list above. */
-  int num_alive; /* amount of easy handles that are added but have not yet
-                    reached COMPLETE state */
+  unsigned int num_easy; /* amount of entries in the linked list above. */
+  unsigned int num_alive; /* amount of easy handles that are added but have
+                             not yet reached COMPLETE state */
 
   struct Curl_llist msglist; /* a list of messages from completed transfers */
 
@@ -136,9 +136,6 @@ struct Curl_multi {
   /* Shared connection cache (bundles)*/
   struct conncache conn_cache;
 
-  long maxconnects; /* if >0, a fixed limit of the maximum number of entries
-                       we're allowed to grow the connection cache to */
-
   long max_host_connections; /* if >0, a fixed limit of the maximum number
                                 of connections per host */
 
@@ -150,8 +147,6 @@ struct Curl_multi {
   void *timer_userp;
   struct curltime timer_lastcall; /* the fixed time for the timeout for the
                                     previous callback */
-  unsigned int max_concurrent_streams;
-
 #ifdef USE_WINSOCK
   WSAEVENT wsa_event; /* winsock event used for waits */
 #else
@@ -160,6 +155,10 @@ struct Curl_multi {
                                    0 is used for read, 1 is used for write */
 #endif
 #endif
+  unsigned int max_concurrent_streams;
+  unsigned int maxconnects; /* if >0, a fixed limit of the maximum number of
+                               entries we're allowed to grow the connection
+                               cache to */
 #define IPV6_UNKNOWN 0
 #define IPV6_DEAD    1
 #define IPV6_WORKS   2

+ 1 - 1
lib/netrc.c

@@ -327,7 +327,7 @@ int Curl_parsenetrc(const char *host, char **loginp, char **passwordp,
     }
     retcode = parsenetrc(host, loginp, passwordp, filealloc);
     free(filealloc);
-#ifdef WIN32
+#ifdef _WIN32
     if(retcode == NETRC_FILE_MISSING) {
       /* fallback to the old-style "_netrc" file */
       filealloc = curl_maprintf("%s%s_netrc", home, DIR_CHAR);

+ 27 - 27
lib/openldap.c

@@ -319,31 +319,12 @@ static CURLcode oldap_setup_connection(struct Curl_easy *data,
 {
   CURLcode result;
   LDAPURLDesc *lud;
-  struct ldapconninfo *li;
+  (void)conn;
 
   /* Early URL syntax check. */
   result = oldap_url_parse(data, &lud);
   ldap_free_urldesc(lud);
 
-  if(!result) {
-    li = calloc(1, sizeof(struct ldapconninfo));
-    if(!li)
-      result = CURLE_OUT_OF_MEMORY;
-    else {
-      li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
-      conn->proto.ldapc = li;
-      connkeep(conn, "OpenLDAP default");
-
-      /* Initialize the SASL storage */
-      Curl_sasl_init(&li->sasl, data, &saslldap);
-
-      /* Clear the TLS upgraded flag */
-      conn->bits.tls_upgraded = FALSE;
-
-      result = oldap_parse_login_options(conn);
-    }
-  }
-
   return result;
 }
 
@@ -537,7 +518,7 @@ static CURLcode oldap_perform_starttls(struct Curl_easy *data)
 static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
 {
   struct connectdata *conn = data->conn;
-  struct ldapconninfo *li = conn->proto.ldapc;
+  struct ldapconninfo *li;
   static const int version = LDAP_VERSION3;
   int rc;
   char *hosturl;
@@ -547,6 +528,26 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
 
   (void)done;
 
+  DEBUGASSERT(!conn->proto.ldapc);
+  li = calloc(1, sizeof(struct ldapconninfo));
+  if(!li)
+    return CURLE_OUT_OF_MEMORY;
+  else {
+    CURLcode result;
+    li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
+    conn->proto.ldapc = li;
+
+    /* Initialize the SASL storage */
+    Curl_sasl_init(&li->sasl, data, &saslldap);
+
+    /* Clear the TLS upgraded flag */
+    conn->bits.tls_upgraded = FALSE;
+
+    result = oldap_parse_login_options(conn);
+    if(result)
+      return result;
+  }
+
   hosturl = aprintf("ldap%s://%s:%d",
                     conn->handler->flags & PROTOPT_SSL? "s": "",
                     conn->host.name, conn->remote_port);
@@ -886,6 +887,11 @@ static CURLcode oldap_do(struct Curl_easy *data, bool *done)
 
   result = oldap_url_parse(data, &lud);
   if(!result) {
+    Sockbuf *sb;
+    /* re-install the libcurl SSL handlers into the sockbuf. */
+    ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
+    ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
+
     rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope,
                          lud->lud_filter, lud->lud_attrs, 0,
                          NULL, NULL, NULL, 0, &msgid);
@@ -947,18 +953,12 @@ static CURLcode client_write(struct Curl_easy *data,
     if(!len && plen && prefix[plen - 1] == ' ')
       plen--;
     result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen);
-    if(!result)
-      data->req.bytecount += plen;
   }
   if(!result && value) {
     result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len);
-    if(!result)
-      data->req.bytecount += len;
   }
   if(!result && suffix) {
     result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen);
-    if(!result)
-      data->req.bytecount += slen;
   }
   return result;
 }

+ 1 - 1
lib/pop3.c

@@ -1088,7 +1088,7 @@ static CURLcode pop3_init(struct Curl_easy *data)
   CURLcode result = CURLE_OK;
   struct POP3 *pop3;
 
-  pop3 = data->req.p.pop3 = calloc(sizeof(struct POP3), 1);
+  pop3 = data->req.p.pop3 = calloc(1, sizeof(struct POP3));
   if(!pop3)
     result = CURLE_OUT_OF_MEMORY;
 

+ 1 - 7
lib/progress.c

@@ -304,7 +304,7 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize,
    * 'actual' is the time in milliseconds it took to actually download the
    * last 'size' bytes.
    */
-  actual = Curl_timediff(now, start);
+  actual = Curl_timediff_ceil(now, start);
   if(actual < minimum) {
     /* if it downloaded the data faster than the limit, make it wait the
        difference */
@@ -319,12 +319,6 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize,
  */
 CURLcode Curl_pgrsSetDownloadCounter(struct Curl_easy *data, curl_off_t size)
 {
-  if(data->set.max_filesize && (size > data->set.max_filesize)) {
-    failf(data, "Exceeded the maximum allowed file size "
-          "(%" CURL_FORMAT_CURL_OFF_T ")",
-          data->set.max_filesize);
-    return CURLE_FILESIZE_EXCEEDED;
-  }
   data->progress.downloaded = size;
   return CURLE_OK;
 }

+ 14 - 14
lib/rand.c

@@ -32,10 +32,6 @@
 #ifdef HAVE_ARPA_INET_H
 #include <arpa/inet.h>
 #endif
-#ifdef HAVE_ARC4RANDOM
-/* Some platforms might have the prototype missing (ubuntu + libressl) */
-uint32_t arc4random(void);
-#endif
 
 #include <curl/curl.h>
 #include "urldata.h"
@@ -50,7 +46,7 @@ uint32_t arc4random(void);
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#ifdef WIN32
+#ifdef _WIN32
 
 #if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600
 #  define HAVE_WIN_BCRYPTGENRANDOM
@@ -105,7 +101,6 @@ CURLcode Curl_win32_random(unsigned char *entropy, size_t length)
 
 static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
 {
-  unsigned int r;
   CURLcode result = CURLE_OK;
   static unsigned int randseed;
   static bool seeded = FALSE;
@@ -138,7 +133,7 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
 
   /* ---- non-cryptographic version following ---- */
 
-#ifdef WIN32
+#ifdef _WIN32
   if(!seeded) {
     result = Curl_win32_random((unsigned char *)rnd, sizeof(*rnd));
     if(result != CURLE_NOT_BUILT_IN)
@@ -146,12 +141,14 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
   }
 #endif
 
-#ifdef HAVE_ARC4RANDOM
-  *rnd = (unsigned int)arc4random();
-  return CURLE_OK;
+#if defined(HAVE_ARC4RANDOM) && !defined(USE_OPENSSL)
+  if(!seeded) {
+    *rnd = (unsigned int)arc4random();
+    return CURLE_OK;
+  }
 #endif
 
-#if defined(RANDOM_FILE) && !defined(WIN32)
+#if defined(RANDOM_FILE) && !defined(_WIN32)
   if(!seeded) {
     /* if there's a random file to read a seed from, use it */
     int fd = open(RANDOM_FILE, O_RDONLY);
@@ -175,9 +172,12 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
     seeded = TRUE;
   }
 
-  /* Return an unsigned 32-bit pseudo-random number. */
-  r = randseed = randseed * 1103515245 + 12345;
-  *rnd = (r << 16) | ((r >> 16) & 0xFFFF);
+  {
+    unsigned int r;
+    /* Return an unsigned 32-bit pseudo-random number. */
+    r = randseed = randseed * 1103515245 + 12345;
+    *rnd = (r << 16) | ((r >> 16) & 0xFFFF);
+  }
   return CURLE_OK;
 }
 

+ 1 - 1
lib/rand.h

@@ -41,7 +41,7 @@ CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd,
 CURLcode Curl_rand_alnum(struct Curl_easy *data, unsigned char *rnd,
                          size_t num);
 
-#ifdef WIN32
+#ifdef _WIN32
 /* Random generator shared between the Schannel vtls and Curl_rand*()
    functions */
 CURLcode Curl_win32_random(unsigned char *entropy, size_t length);

+ 1 - 1
lib/rename.c

@@ -40,7 +40,7 @@
 /* return 0 on success, 1 on error */
 int Curl_rename(const char *oldpath, const char *newpath)
 {
-#ifdef WIN32
+#ifdef _WIN32
   /* rename() on Windows doesn't overwrite, so we can't use it here.
      MoveFileEx() will overwrite and is usually atomic, however it fails
      when there are open handles to the file. */

+ 230 - 129
lib/rtsp.c

@@ -45,8 +45,8 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#define RTP_PKT_LENGTH(p)  ((((int)((unsigned char)((p)[2]))) << 8) | \
-                             ((int)((unsigned char)((p)[3]))))
+#define RTP_PKT_LENGTH(p) ((((unsigned int)((unsigned char)((p)[2]))) << 8) | \
+                            ((unsigned int)((unsigned char)((p)[3]))))
 
 /* protocol-specific functions set up to be called by the main engine */
 static CURLcode rtsp_do(struct Curl_easy *data, bool *done);
@@ -59,14 +59,19 @@ static int rtsp_getsock_do(struct Curl_easy *data,
 
 /*
  * Parse and write out any available RTP data.
- *
- * nread: amount of data left after k->str. will be modified if RTP
- *        data is parsed and k->str is moved up
- * readmore: whether or not the RTP parser needs more data right away
+ * @param data     the transfer
+ * @param conn     the connection
+ * @param buf      data read from connection
+ * @param blen     amount of data in buf
+ * @param consumed out, number of blen consumed
+ * @param readmore out, TRUE iff complete buf was consumed and more data
+ *                 is needed
  */
 static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
                                    struct connectdata *conn,
-                                   ssize_t *nread,
+                                   const char *buf,
+                                   size_t blen,
+                                   size_t *pconsumed,
                                    bool *readmore);
 
 static CURLcode rtsp_setup_connection(struct Curl_easy *data,
@@ -88,7 +93,7 @@ static int rtsp_getsock_do(struct Curl_easy *data, struct connectdata *conn,
 }
 
 static
-CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len);
+CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len);
 static
 CURLcode rtsp_parse_transport(struct Curl_easy *data, char *transport);
 
@@ -585,153 +590,249 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
   return result;
 }
 
-
-static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
-                                   struct connectdata *conn,
-                                   ssize_t *nread,
-                                   bool *readmore) {
-  struct SingleRequest *k = &data->req;
+static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
+                                     struct connectdata *conn,
+                                     const char *buf,
+                                     size_t blen,
+                                     bool in_body,
+                                     size_t *pconsumed)
+{
   struct rtsp_conn *rtspc = &(conn->proto.rtspc);
-  unsigned char *rtp_channel_mask = data->state.rtp_channel_mask;
+  CURLcode result = CURLE_OK;
 
-  char *rtp; /* moving pointer to rtp data */
-  ssize_t rtp_dataleft; /* how much data left to parse in this round */
-  CURLcode result;
-  bool interleaved = false;
-  size_t skip_size = 0;
+  *pconsumed = 0;
+  while(blen) {
+    switch(rtspc->state) {
 
-  if(Curl_dyn_len(&rtspc->buf)) {
-    /* There was some leftover data the last time. Append new buffers */
-    if(Curl_dyn_addn(&rtspc->buf, k->str, *nread))
-      return CURLE_OUT_OF_MEMORY;
-    rtp = Curl_dyn_ptr(&rtspc->buf);
-    rtp_dataleft = Curl_dyn_len(&rtspc->buf);
-  }
-  else {
-    /* Just parse the request buffer directly */
-    rtp = k->str;
-    rtp_dataleft = *nread;
-  }
-
-  while(rtp_dataleft > 0) {
-    if(rtp[0] == '$') {
-      if(rtp_dataleft > 4) {
-        unsigned char rtp_channel;
-        int rtp_length;
-        int idx;
-        int off;
-
-        /* Parse the header */
-        /* The channel identifier immediately follows and is 1 byte */
-        rtp_channel = (unsigned char)rtp[1];
-        idx = rtp_channel / 8;
-        off = rtp_channel % 8;
-        if(!(rtp_channel_mask[idx] & (1 << off))) {
-          /* invalid channel number, maybe not an RTP packet */
-          rtp++;
-          rtp_dataleft--;
-          skip_size++;
-          continue;
+    case RTP_PARSE_SKIP: {
+      DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 0);
+      if(in_body && buf[0] != '$') {
+        /* in BODY and no valid start, do not consume and return */
+        goto out;
+      }
+      while(blen && buf[0] != '$') {
+        if(!in_body && buf[0] == 'R' &&
+           data->set.rtspreq != RTSPREQ_RECEIVE) {
+          if(strncmp(buf, "RTSP/", (blen < 5) ? blen : 5) == 0) {
+            /* This could be the next response, no consume and return */
+            if(*pconsumed) {
+              DEBUGF(infof(data, "RTP rtsp_filter_rtp[SKIP] RTSP/ prefix, "
+                           "skipping %zd bytes of junk", *pconsumed));
+            }
+            rtspc->state = RTP_PARSE_SKIP;
+            rtspc->in_header = TRUE;
+            goto out;
+          }
         }
-        if(skip_size > 0) {
-          DEBUGF(infof(data, "Skip the malformed interleaved data %lu "
-                       "bytes", skip_size));
+        /* junk, consume without buffering */
+        *pconsumed += 1;
+        ++buf;
+        --blen;
+      }
+      if(blen && buf[0] == '$') {
+        /* possible start of an RTP message, buffer */
+        if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
+          result = CURLE_OUT_OF_MEMORY;
+          goto out;
         }
-        skip_size = 0;
-        rtspc->rtp_channel = rtp_channel;
-
-        /* The length is two bytes */
-        rtp_length = RTP_PKT_LENGTH(rtp);
+        *pconsumed += 1;
+        ++buf;
+        --blen;
+        rtspc->state = RTP_PARSE_CHANNEL;
+      }
+      break;
+    }
 
-        if(rtp_dataleft < rtp_length + 4) {
-          /* Need more - incomplete payload */
-          *readmore = TRUE;
-          break;
+    case RTP_PARSE_CHANNEL: {
+      int idx = ((unsigned char)buf[0]) / 8;
+      int off = ((unsigned char)buf[0]) % 8;
+      DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 1);
+      if(!(data->state.rtp_channel_mask[idx] & (1 << off))) {
+        /* invalid channel number, junk or BODY data */
+        rtspc->state = RTP_PARSE_SKIP;
+        if(in_body) {
+          /* we do not consume this byte, it is BODY data */
+          DEBUGF(infof(data, "RTSP: invalid RTP channel %d in BODY, "
+                       "treating as BODY data", idx));
+          if(*pconsumed == 0) {
+            /* We did not consume the initial '$' in our buffer, but had
+             * it from an earlier call. We cannot un-consume it and have
+             * to write it directly as BODY data */
+            result = Curl_client_write(data, CLIENTWRITE_BODY,
+                                       Curl_dyn_ptr(&rtspc->buf), 1);
+            Curl_dyn_free(&rtspc->buf);
+            if(result)
+              goto out;
+          }
+          else {
+            /* un-consume the '$' and leave */
+            Curl_dyn_free(&rtspc->buf);
+            *pconsumed -= 1;
+            --buf;
+            ++blen;
+            goto out;
+          }
         }
-        interleaved = true;
-        /* We have the full RTP interleaved packet
-         * Write out the header including the leading '$' */
-        DEBUGF(infof(data, "RTP write channel %d rtp_length %d",
-                     rtspc->rtp_channel, rtp_length));
-        result = rtp_client_write(data, &rtp[0], rtp_length + 4);
-        if(result) {
-          *readmore = FALSE;
-          return result;
+        else {
+          /* not BODY, forget the junk '$'. Do not consume this byte,
+           * it might be a start */
+          infof(data, "RTSP: invalid RTP channel %d, skipping", idx);
+          Curl_dyn_free(&rtspc->buf);
         }
+        break;
+      }
+      /* a valid channel, so we expect this to be a real RTP message */
+      rtspc->rtp_channel = (unsigned char)buf[0];
+      if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
+        result = CURLE_OUT_OF_MEMORY;
+        goto out;
+      }
+      *pconsumed += 1;
+      ++buf;
+      --blen;
+      rtspc->state = RTP_PARSE_LEN;
+      break;
+    }
 
-        /* Move forward in the buffer */
-        rtp_dataleft -= rtp_length + 4;
-        rtp += rtp_length + 4;
+    case RTP_PARSE_LEN: {
+      size_t rtp_len = Curl_dyn_len(&rtspc->buf);
+      const char *rtp_buf;
+      DEBUGASSERT(rtp_len >= 2 && rtp_len < 4);
+      if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
+        result = CURLE_OUT_OF_MEMORY;
+        goto out;
+      }
+      *pconsumed += 1;
+      ++buf;
+      --blen;
+      if(rtp_len == 2)
+        break;
+      rtp_buf = Curl_dyn_ptr(&rtspc->buf);
+      rtspc->rtp_len = RTP_PKT_LENGTH(rtp_buf) + 4;
+      rtspc->state = RTP_PARSE_DATA;
+      break;
+    }
 
-        if(data->set.rtspreq == RTSPREQ_RECEIVE) {
-          /* If we are in a passive receive, give control back
-           * to the app as often as we can.
-           */
-          k->keepon &= ~KEEP_RECV;
+    case RTP_PARSE_DATA: {
+      size_t rtp_len = Curl_dyn_len(&rtspc->buf);
+      size_t needed;
+      DEBUGASSERT(rtp_len < rtspc->rtp_len);
+      needed = rtspc->rtp_len - rtp_len;
+      if(needed <= blen) {
+        if(Curl_dyn_addn(&rtspc->buf, buf, needed)) {
+          result = CURLE_OUT_OF_MEMORY;
+          goto out;
         }
+        *pconsumed += needed;
+        buf += needed;
+        blen -= needed;
+        /* complete RTP message in buffer */
+        DEBUGF(infof(data, "RTP write channel %d rtp_len %zu",
+                     rtspc->rtp_channel, rtspc->rtp_len));
+        result = rtp_client_write(data, Curl_dyn_ptr(&rtspc->buf),
+                                  rtspc->rtp_len);
+        Curl_dyn_free(&rtspc->buf);
+        rtspc->state = RTP_PARSE_SKIP;
+        if(result)
+          goto out;
       }
       else {
-        /* Need more - incomplete header */
-        *readmore = TRUE;
-        break;
-      }
-    }
-    else {
-      /* If the following data begins with 'RTSP/', which might be an RTSP
-         message, we should stop skipping the data. */
-      /* If `k-> headerline> 0 && !interleaved` is true, we are maybe in the
-         middle of an RTSP message. It is difficult to determine this, so we
-         stop skipping. */
-      size_t prefix_len = (rtp_dataleft < 5) ? rtp_dataleft : 5;
-      if((k->headerline > 0 && !interleaved) ||
-         strncmp(rtp, "RTSP/", prefix_len) == 0) {
-        if(skip_size > 0) {
-          DEBUGF(infof(data, "Skip the malformed interleaved data %lu "
-                       "bytes", skip_size));
+        if(Curl_dyn_addn(&rtspc->buf, buf, blen)) {
+          result = CURLE_OUT_OF_MEMORY;
+          goto out;
         }
-        break; /* maybe is an RTSP message */
+        *pconsumed += blen;
+        buf += blen;
+        blen = 0;
       }
-      /* Skip incorrect data util the next RTP packet or RTSP message */
-      do {
-        rtp++;
-        rtp_dataleft--;
-        skip_size++;
-      } while(rtp_dataleft > 0 && rtp[0] != '$' && rtp[0] != 'R');
+      break;
+    }
+
+    default:
+      DEBUGASSERT(0);
+      return CURLE_RECV_ERROR;
     }
   }
+out:
+  return result;
+}
+
+static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
+                                   struct connectdata *conn,
+                                   const char *buf,
+                                   size_t blen,
+                                   size_t *pconsumed,
+                                   bool *readmore)
+{
+  struct rtsp_conn *rtspc = &(conn->proto.rtspc);
+  CURLcode result = CURLE_OK;
+  size_t consumed = 0;
+  bool in_body;
 
-  if(rtp_dataleft && rtp[0] == '$') {
-    DEBUGF(infof(data, "RTP Rewinding %zd %s", rtp_dataleft,
-                 *readmore ? "(READMORE)" : ""));
+  if(!data->req.header)
+    rtspc->in_header = FALSE;
+  in_body = (data->req.headerline && !rtspc->in_header) &&
+            (data->req.size >= 0) &&
+            (data->req.bytecount < data->req.size);
 
-    /* Store the incomplete RTP packet for a "rewind" */
-    if(!Curl_dyn_len(&rtspc->buf)) {
-      /* nothing was stored, add this data */
-      if(Curl_dyn_addn(&rtspc->buf, rtp, rtp_dataleft))
-        return CURLE_OUT_OF_MEMORY;
-    }
-    else {
-      /* keep the remainder */
-      Curl_dyn_tail(&rtspc->buf, rtp_dataleft);
-    }
+  *readmore = FALSE;
+  *pconsumed = 0;
+  if(!blen) {
+    goto out;
+  }
 
-    /* As far as the transfer is concerned, this data is consumed */
-    *nread = 0;
-    return CURLE_OK;
+  /* If header parsing is not onging, extract RTP messages */
+  if(!rtspc->in_header) {
+    result = rtsp_filter_rtp(data, conn, buf, blen, in_body, &consumed);
+    if(result)
+      goto out;
+    *pconsumed += consumed;
+    buf += consumed;
+    blen -= consumed;
   }
-  /* Fix up k->str to point just after the last RTP packet */
-  k->str += *nread - rtp_dataleft;
 
-  *nread = rtp_dataleft;
+  /* we want to parse headers, do so */
+  if(data->req.header && blen) {
+    rtspc->in_header = TRUE;
+    result = Curl_http_readwrite_headers(data, conn, buf, blen,
+                                         &consumed);
+    if(result)
+      goto out;
+
+    *pconsumed += consumed;
+    buf += consumed;
+    blen -= consumed;
+
+    if(!data->req.header)
+      rtspc->in_header = FALSE;
+
+    if(!rtspc->in_header) {
+      /* If header parsing is done and data left, extract RTP messages */
+      in_body = (data->req.headerline && !rtspc->in_header) &&
+                (data->req.size >= 0) &&
+                (data->req.bytecount < data->req.size);
+      result = rtsp_filter_rtp(data, conn, buf, blen, in_body, &consumed);
+      if(result)
+        goto out;
+      *pconsumed += consumed;
+    }
+  }
 
-  /* If we get here, we have finished with the leftover/merge buffer */
-  Curl_dyn_free(&rtspc->buf);
+  if(rtspc->state != RTP_PARSE_SKIP)
+    *readmore = TRUE;
 
-  return CURLE_OK;
+out:
+  if(!*readmore && data->set.rtspreq == RTSPREQ_RECEIVE) {
+    /* In special mode RECEIVE, we just process one chunk of network
+     * data, so we stop the transfer here, if we have no incomplete
+     * RTP message pending. */
+    data->req.keepon &= ~KEEP_RECV;
+  }
+  return result;
 }
 
 static
-CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len)
+CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len)
 {
   size_t wrote;
   curl_write_callback writeit;
@@ -756,7 +857,7 @@ CURLcode rtp_client_write(struct Curl_easy *data, char *ptr, size_t len)
   }
 
   Curl_set_in_callback(data, true);
-  wrote = writeit(ptr, 1, len, user_ptr);
+  wrote = writeit((char *)ptr, 1, len, user_ptr);
   Curl_set_in_callback(data, false);
 
   if(CURL_WRITEFUNC_PAUSE == wrote) {

+ 9 - 0
lib/rtsp.h

@@ -39,6 +39,12 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header);
 
 #endif /* CURL_DISABLE_RTSP */
 
+typedef enum {
+  RTP_PARSE_SKIP,
+  RTP_PARSE_CHANNEL,
+  RTP_PARSE_LEN,
+  RTP_PARSE_DATA
+} rtp_parse_st;
 /*
  * RTSP Connection data
  *
@@ -47,6 +53,9 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, char *header);
 struct rtsp_conn {
   struct dynbuf buf;
   int rtp_channel;
+  size_t rtp_len;
+  rtp_parse_st state;
+  BIT(in_header);
 };
 
 /****************************************************************************

+ 1 - 1
lib/select.c

@@ -76,7 +76,7 @@ int Curl_wait_ms(timediff_t timeout_ms)
   }
 #if defined(MSDOS)
   delay(timeout_ms);
-#elif defined(WIN32)
+#elif defined(_WIN32)
   /* prevent overflow, timeout_ms is typecast to ULONG/DWORD. */
 #if TIMEDIFF_T_MAX >= ULONG_MAX
   if(timeout_ms >= ULONG_MAX)

+ 255 - 71
lib/sendf.c

@@ -50,6 +50,7 @@
 #include "strdup.h"
 #include "http2.h"
 #include "headers.h"
+#include "progress.h"
 #include "ws.h"
 
 /* The last 3 #include files should be in this order */
@@ -57,6 +58,9 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
+
+static CURLcode do_init_stack(struct Curl_easy *data);
+
 #if defined(CURL_DO_LINEEND_CONV) && !defined(CURL_DISABLE_FTP)
 /*
  * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF
@@ -385,17 +389,17 @@ static CURLcode chop_write(struct Curl_easy *data,
    the future to leave the original data alone.
  */
 CURLcode Curl_client_write(struct Curl_easy *data,
-                           int type,
-                           char *ptr,
-                           size_t len)
+                           int type, char *buf, size_t blen)
 {
+  CURLcode result;
+
 #if !defined(CURL_DISABLE_FTP) && defined(CURL_DO_LINEEND_CONV)
   /* FTP data may need conversion. */
   if((type & CLIENTWRITE_BODY) &&
      (data->conn->handler->protocol & PROTO_FAMILY_FTP) &&
      data->conn->proto.ftpc.transfertype == 'A') {
     /* convert end-of-line markers */
-    len = convert_lineends(data, ptr, len);
+    blen = convert_lineends(data, buf, blen);
   }
 #endif
   /* it is one of those, at least */
@@ -405,14 +409,14 @@ CURLcode Curl_client_write(struct Curl_easy *data,
   /* INFO is only INFO */
   DEBUGASSERT(!(type & CLIENTWRITE_INFO) || (type == CLIENTWRITE_INFO));
 
-  if(type == CLIENTWRITE_BODY) {
-    if(data->req.ignorebody)
-      return CURLE_OK;
-
-    if(data->req.writer_stack && !data->set.http_ce_skip)
-      return Curl_unencode_write(data, data->req.writer_stack, ptr, len);
+  if(!data->req.writer_stack) {
+    result = do_init_stack(data);
+    if(result)
+      return result;
+    DEBUGASSERT(data->req.writer_stack);
   }
-  return chop_write(data, type, FALSE, ptr, len);
+
+  return Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen);
 }
 
 CURLcode Curl_client_unpause(struct Curl_easy *data)
@@ -449,12 +453,12 @@ CURLcode Curl_client_unpause(struct Curl_easy *data)
 
 void Curl_client_cleanup(struct Curl_easy *data)
 {
-  struct contenc_writer *writer = data->req.writer_stack;
+  struct Curl_cwriter *writer = data->req.writer_stack;
   size_t i;
 
   while(writer) {
-    data->req.writer_stack = writer->downstream;
-    writer->handler->close_writer(data, writer);
+    data->req.writer_stack = writer->next;
+    writer->cwt->do_close(data, writer);
     free(writer);
     writer = data->req.writer_stack;
   }
@@ -463,61 +467,222 @@ void Curl_client_cleanup(struct Curl_easy *data)
     Curl_dyn_free(&data->state.tempwrite[i].b);
   }
   data->state.tempcount = 0;
+  data->req.bytecount = 0;
+  data->req.headerline = 0;
+}
 
+/* Write data using an unencoding writer stack. "nbytes" is not
+   allowed to be 0. */
+CURLcode Curl_cwriter_write(struct Curl_easy *data,
+                             struct Curl_cwriter *writer, int type,
+                             const char *buf, size_t nbytes)
+{
+  if(!nbytes)
+    return CURLE_OK;
+  if(!writer)
+    return CURLE_WRITE_ERROR;
+  return writer->cwt->do_write(data, writer, type, buf, nbytes);
 }
 
-/* Real client writer: no downstream. */
-static CURLcode client_cew_init(struct Curl_easy *data,
-                                struct contenc_writer *writer)
+CURLcode Curl_cwriter_def_init(struct Curl_easy *data,
+                               struct Curl_cwriter *writer)
 {
-  (void) data;
+  (void)data;
   (void)writer;
   return CURLE_OK;
 }
 
-static CURLcode client_cew_write(struct Curl_easy *data,
-                                 struct contenc_writer *writer,
-                                 const char *buf, size_t nbytes)
+CURLcode Curl_cwriter_def_write(struct Curl_easy *data,
+                                struct Curl_cwriter *writer, int type,
+                                const char *buf, size_t nbytes)
 {
-  (void)writer;
-  if(!nbytes || data->req.ignorebody)
-    return CURLE_OK;
-  return chop_write(data, CLIENTWRITE_BODY, FALSE, (char *)buf, nbytes);
+  return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
 }
 
-static void client_cew_close(struct Curl_easy *data,
-                             struct contenc_writer *writer)
+void Curl_cwriter_def_close(struct Curl_easy *data,
+                            struct Curl_cwriter *writer)
 {
   (void) data;
   (void) writer;
 }
 
-static const struct content_encoding client_cew = {
+/* Real client writer to installed callbacks. */
+static CURLcode cw_client_write(struct Curl_easy *data,
+                                struct Curl_cwriter *writer, int type,
+                                const char *buf, size_t nbytes)
+{
+  (void)writer;
+  if(!nbytes)
+    return CURLE_OK;
+  return chop_write(data, type, FALSE, (char *)buf, nbytes);
+}
+
+static const struct Curl_cwtype cw_client = {
+  "client",
   NULL,
+  Curl_cwriter_def_init,
+  cw_client_write,
+  Curl_cwriter_def_close,
+  sizeof(struct Curl_cwriter)
+};
+
+static size_t get_max_body_write_len(struct Curl_easy *data, curl_off_t limit)
+{
+  if(limit != -1) {
+    /* How much more are we allowed to write? */
+    curl_off_t remain_diff;
+    remain_diff = limit - data->req.bytecount;
+    if(remain_diff < 0) {
+      /* already written too much! */
+      return 0;
+    }
+#if SIZEOF_CURL_OFF_T > SIZEOF_SIZE_T
+    else if(remain_diff > SSIZE_T_MAX) {
+      return SIZE_T_MAX;
+    }
+#endif
+    else {
+      return (size_t)remain_diff;
+    }
+  }
+  return SIZE_T_MAX;
+}
+
+/* Download client writer in phase CURL_CW_PROTOCOL that
+ * sees the "real" download body data. */
+static CURLcode cw_download_write(struct Curl_easy *data,
+                                  struct Curl_cwriter *writer, int type,
+                                  const char *buf, size_t nbytes)
+{
+  CURLcode result;
+  size_t nwrite, excess_len = 0;
+  const char *excess_data = NULL;
+
+  if(!(type & CLIENTWRITE_BODY)) {
+    if((type & CLIENTWRITE_CONNECT) && data->set.suppress_connect_headers)
+      return CURLE_OK;
+    return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+  }
+
+  nwrite = nbytes;
+  if(-1 != data->req.maxdownload) {
+    size_t wmax = get_max_body_write_len(data, data->req.maxdownload);
+    if(nwrite > wmax) {
+      excess_len = nbytes - wmax;
+      nwrite = wmax;
+      excess_data = buf + nwrite;
+    }
+
+    if(nwrite == wmax) {
+      data->req.download_done = TRUE;
+    }
+  }
+
+  if(data->set.max_filesize) {
+    size_t wmax = get_max_body_write_len(data, data->set.max_filesize);
+    if(nwrite > wmax) {
+      nwrite = wmax;
+    }
+  }
+
+  data->req.bytecount += nwrite;
+  ++data->req.bodywrites;
+  if(!data->req.ignorebody && nwrite) {
+    result = Curl_cwriter_write(data, writer->next, type, buf, nwrite);
+    if(result)
+      return result;
+  }
+  result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
+  if(result)
+    return result;
+
+  if(excess_len) {
+    if(data->conn->handler->readwrite) {
+      /* RTSP hack moved from transfer loop to here */
+      bool readmore = FALSE; /* indicates data is incomplete, need more */
+      size_t consumed = 0;
+      result = data->conn->handler->readwrite(data, data->conn,
+                                              excess_data, excess_len,
+                                              &consumed, &readmore);
+      if(result)
+        return result;
+      DEBUGASSERT(consumed <= excess_len);
+      excess_len -= consumed;
+      if(readmore) {
+        data->req.download_done = FALSE;
+        data->req.keepon |= KEEP_RECV; /* we're not done reading */
+      }
+    }
+    if(excess_len && !data->req.ignorebody) {
+      infof(data,
+            "Excess found writing body:"
+            " excess = %zu"
+            ", size = %" CURL_FORMAT_CURL_OFF_T
+            ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
+            ", bytecount = %" CURL_FORMAT_CURL_OFF_T,
+            excess_len, data->req.size, data->req.maxdownload,
+            data->req.bytecount);
+      connclose(data->conn, "excess found in a read");
+    }
+  }
+  else if(nwrite < nbytes) {
+    failf(data, "Exceeded the maximum allowed file size "
+          "(%" CURL_FORMAT_CURL_OFF_T ") with %"
+          CURL_FORMAT_CURL_OFF_T " bytes",
+          data->set.max_filesize, data->req.bytecount);
+    return CURLE_FILESIZE_EXCEEDED;
+  }
+
+  return CURLE_OK;
+}
+
+static const struct Curl_cwtype cw_download = {
+  "download",
   NULL,
-  client_cew_init,
-  client_cew_write,
-  client_cew_close,
-  sizeof(struct contenc_writer)
+  Curl_cwriter_def_init,
+  cw_download_write,
+  Curl_cwriter_def_close,
+  sizeof(struct Curl_cwriter)
+};
+
+/* RAW client writer in phase CURL_CW_RAW that
+ * enabled tracing of raw data. */
+static CURLcode cw_raw_write(struct Curl_easy *data,
+                             struct Curl_cwriter *writer, int type,
+                             const char *buf, size_t nbytes)
+{
+  if(type & CLIENTWRITE_BODY && data->set.verbose && !data->req.ignorebody) {
+    Curl_debug(data, CURLINFO_DATA_IN, (char *)buf, nbytes);
+  }
+  return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
+}
+
+static const struct Curl_cwtype cw_raw = {
+  "raw",
+  NULL,
+  Curl_cwriter_def_init,
+  cw_raw_write,
+  Curl_cwriter_def_close,
+  sizeof(struct Curl_cwriter)
 };
 
 /* Create an unencoding writer stage using the given handler. */
-CURLcode Curl_client_create_writer(struct contenc_writer **pwriter,
+CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter,
                                    struct Curl_easy *data,
-                                   const struct content_encoding *ce_handler,
-                                   int order)
+                                   const struct Curl_cwtype *cwt,
+                                   Curl_cwriter_phase phase)
 {
-  struct contenc_writer *writer;
+  struct Curl_cwriter *writer;
   CURLcode result = CURLE_OUT_OF_MEMORY;
 
-  DEBUGASSERT(ce_handler->writersize >= sizeof(struct contenc_writer));
-  writer = (struct contenc_writer *) calloc(1, ce_handler->writersize);
+  DEBUGASSERT(cwt->cwriter_size >= sizeof(struct Curl_cwriter));
+  writer = (struct Curl_cwriter *) calloc(1, cwt->cwriter_size);
   if(!writer)
     goto out;
 
-  writer->handler = ce_handler;
-  writer->order = order;
-  result = ce_handler->init_writer(data, writer);
+  writer->cwt = cwt;
+  writer->phase = phase;
+  result = cwt->do_init(data, writer);
 
 out:
   *pwriter = result? NULL : writer;
@@ -526,55 +691,74 @@ out:
   return result;
 }
 
-void Curl_client_free_writer(struct Curl_easy *data,
-                             struct contenc_writer *writer)
+void Curl_cwriter_free(struct Curl_easy *data,
+                             struct Curl_cwriter *writer)
 {
   if(writer) {
-    writer->handler->close_writer(data, writer);
+    writer->cwt->do_close(data, writer);
     free(writer);
   }
 }
 
-/* allow no more than 5 "chained" compression steps */
-#define MAX_ENCODE_STACK 5
+size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase)
+{
+  struct Curl_cwriter *w;
+  size_t n = 0;
 
+  for(w = data->req.writer_stack; w; w = w->next) {
+    if(w->phase == phase)
+      ++n;
+  }
+  return n;
+}
 
-static CURLcode init_writer_stack(struct Curl_easy *data)
+static CURLcode do_init_stack(struct Curl_easy *data)
 {
+  struct Curl_cwriter *writer;
+  CURLcode result;
+
   DEBUGASSERT(!data->req.writer_stack);
-  return Curl_client_create_writer(&data->req.writer_stack,
-                                   data, &client_cew, 0);
+  result = Curl_cwriter_create(&data->req.writer_stack,
+                               data, &cw_client, CURL_CW_CLIENT);
+  if(result)
+    return result;
+
+  result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL);
+  if(result)
+    return result;
+  result = Curl_cwriter_add(data, writer);
+  if(result) {
+    Curl_cwriter_free(data, writer);
+  }
+
+  result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW);
+  if(result)
+    return result;
+  result = Curl_cwriter_add(data, writer);
+  if(result) {
+    Curl_cwriter_free(data, writer);
+  }
+  return result;
 }
 
-CURLcode Curl_client_add_writer(struct Curl_easy *data,
-                                struct contenc_writer *writer)
+CURLcode Curl_cwriter_add(struct Curl_easy *data,
+                          struct Curl_cwriter *writer)
 {
   CURLcode result;
+  struct Curl_cwriter **anchor = &data->req.writer_stack;
 
-  if(!data->req.writer_stack) {
-    result = init_writer_stack(data);
+  if(!*anchor) {
+    result = do_init_stack(data);
     if(result)
       return result;
   }
 
-  if(data->req.writer_stack_depth++ >= MAX_ENCODE_STACK) {
-    failf(data, "Reject response due to more than %u content encodings",
-          MAX_ENCODE_STACK);
-    return CURLE_BAD_CONTENT_ENCODING;
-  }
-
-  /* Stack the unencoding stage. */
-  if(writer->order >= data->req.writer_stack->order) {
-    writer->downstream = data->req.writer_stack;
-    data->req.writer_stack = writer;
-  }
-  else {
-    struct contenc_writer *w = data->req.writer_stack;
-    while(w->downstream && writer->order < w->downstream->order)
-      w = w->downstream;
-    writer->downstream = w->downstream;
-    w->downstream = writer;
-  }
+  /* Insert the writer as first in its phase.
+   * Skip existing writers of lower phases. */
+  while(*anchor && (*anchor)->phase < writer->phase)
+    anchor = &((*anchor)->next);
+  writer->next = *anchor;
+  *anchor = writer;
   return CURLE_OK;
 }
 

+ 103 - 24
lib/sendf.h

@@ -50,43 +50,122 @@
 #define CLIENTWRITE_1XX     (1<<5) /* a 1xx response related HEADER */
 #define CLIENTWRITE_TRAILER (1<<6) /* a trailer HEADER */
 
+/**
+ * Write `len` bytes at `prt` to the client. `type` indicates what
+ * kind of data is being written.
+ */
 CURLcode Curl_client_write(struct Curl_easy *data, int type, char *ptr,
                            size_t len) WARN_UNUSED_RESULT;
 
+/**
+ * For a paused transfer, there might be buffered data held back.
+ * Attempt to flush this data to the client. This *may* trigger
+ * another pause of the transfer.
+ */
 CURLcode Curl_client_unpause(struct Curl_easy *data);
+
+/**
+ * Free all resources related to client writing.
+ */
 void Curl_client_cleanup(struct Curl_easy *data);
 
-struct contenc_writer {
-  const struct content_encoding *handler;  /* Encoding handler. */
-  struct contenc_writer *downstream;  /* Downstream writer. */
-  unsigned int order; /* Ordering within writer stack. */
+/**
+ * Client Writers - a chain passing transfer BODY data to the client.
+ * Main application: HTTP and related protocols
+ * Other uses: monitoring of download progress
+ *
+ * Writers in the chain are order by their `phase`. First come all
+ * writers in CURL_CW_RAW, followed by any in CURL_CW_TRANSFER_DECODE,
+ * followed by any in CURL_CW_PROTOCOL, etc.
+ *
+ * When adding a writer, it is inserted as first in its phase. This means
+ * the order of adding writers of the same phase matters, but writers for
+ * different phases may be added in any order.
+ *
+ * Writers which do modify the BODY data written are expected to be of
+ * phases TRANSFER_DECODE or CONTENT_DECODE. The other phases are intended
+ * for monitoring writers. Which do *not* modify the data but gather
+ * statistics or update progress reporting.
+ */
+
+/* Phase a writer operates at. */
+typedef enum {
+  CURL_CW_RAW,  /* raw data written, before any decoding */
+  CURL_CW_TRANSFER_DECODE, /* remove transfer-encodings */
+  CURL_CW_PROTOCOL, /* after transfer, but before content decoding */
+  CURL_CW_CONTENT_DECODE, /* remove content-encodings */
+  CURL_CW_CLIENT  /* data written to client */
+} Curl_cwriter_phase;
+
+/* Client Writer Type, provides the implementation */
+struct Curl_cwtype {
+  const char *name;        /* writer name. */
+  const char *alias;       /* writer name alias, maybe NULL. */
+  CURLcode (*do_init)(struct Curl_easy *data,
+                      struct Curl_cwriter *writer);
+  CURLcode (*do_write)(struct Curl_easy *data,
+                       struct Curl_cwriter *writer, int type,
+                       const char *buf, size_t nbytes);
+  void (*do_close)(struct Curl_easy *data,
+                   struct Curl_cwriter *writer);
+  size_t cwriter_size;  /* sizeof() allocated struct Curl_cwriter */
 };
 
-/* Content encoding writer. */
-struct content_encoding {
-  const char *name;        /* Encoding name. */
-  const char *alias;       /* Encoding name alias. */
-  CURLcode (*init_writer)(struct Curl_easy *data,
-                          struct contenc_writer *writer);
-  CURLcode (*unencode_write)(struct Curl_easy *data,
-                             struct contenc_writer *writer,
-                             const char *buf, size_t nbytes);
-  void (*close_writer)(struct Curl_easy *data,
-                       struct contenc_writer *writer);
-  size_t writersize;
+/* Client writer instance */
+struct Curl_cwriter {
+  const struct Curl_cwtype *cwt;  /* type implementation */
+  struct Curl_cwriter *next;  /* Downstream writer. */
+  Curl_cwriter_phase phase; /* phase at which it operates */
 };
 
+/**
+ * Create a new cwriter instance with given type and phase. Is not
+ * inserted into the writer chain by this call.
+ * Invokes `writer->do_init()`.
+ */
+CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter,
+                             struct Curl_easy *data,
+                             const struct Curl_cwtype *ce_handler,
+                             Curl_cwriter_phase phase);
 
-CURLcode Curl_client_create_writer(struct contenc_writer **pwriter,
-                                   struct Curl_easy *data,
-                                   const struct content_encoding *ce_handler,
-                                   int order);
+/**
+ * Free a cwriter instance.
+ * Invokes `writer->do_close()`.
+ */
+void Curl_cwriter_free(struct Curl_easy *data,
+                       struct Curl_cwriter *writer);
 
-void Curl_client_free_writer(struct Curl_easy *data,
-                             struct contenc_writer *writer);
+/**
+ * Count the number of writers installed of the given phase.
+ */
+size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase);
 
-CURLcode Curl_client_add_writer(struct Curl_easy *data,
-                                struct contenc_writer *writer);
+/**
+ * Adds a writer to the transfer's writer chain.
+ * The writers `phase` determines where in the chain it is inserted.
+ */
+CURLcode Curl_cwriter_add(struct Curl_easy *data,
+                          struct Curl_cwriter *writer);
+
+/**
+ * Convenience method for calling `writer->do_write()` that
+ * checks for NULL writer.
+ */
+CURLcode Curl_cwriter_write(struct Curl_easy *data,
+                            struct Curl_cwriter *writer, int type,
+                            const char *buf, size_t nbytes);
+
+/**
+ * Default implementations for do_init, do_write, do_close that
+ * do nothing and pass the data through.
+ */
+CURLcode Curl_cwriter_def_init(struct Curl_easy *data,
+                               struct Curl_cwriter *writer);
+CURLcode Curl_cwriter_def_write(struct Curl_easy *data,
+                                struct Curl_cwriter *writer, int type,
+                                const char *buf, size_t nbytes);
+void Curl_cwriter_def_close(struct Curl_easy *data,
+                            struct Curl_cwriter *writer);
 
 
 /* internal read-function, does plain socket, SSL and krb4 */

+ 91 - 129
lib/setopt.c

@@ -50,6 +50,7 @@
 #include "multiif.h"
 #include "altsvc.h"
 #include "hsts.h"
+#include "tftp.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -171,7 +172,7 @@ static CURLcode protocol2num(const char *str, curl_prot_t *val)
     str = strchr(str, ',');
     tlen = str? (size_t) (str - token): strlen(token);
     if(tlen) {
-      const struct Curl_handler *h = Curl_builtin_scheme(token, tlen);
+      const struct Curl_handler *h = Curl_getn_scheme_handler(token, tlen);
 
       if(!h)
         return CURLE_UNSUPPORTED_PROTOCOL;
@@ -261,43 +262,43 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      * Set the absolute number of maximum simultaneous alive connection that
      * libcurl is allowed to have.
      */
-    arg = va_arg(param, long);
-    if(arg < 0)
+    uarg = va_arg(param, unsigned long);
+    if(uarg > UINT_MAX)
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.maxconnects = arg;
+    data->set.maxconnects = (unsigned int)uarg;
     break;
   case CURLOPT_FORBID_REUSE:
     /*
      * When this transfer is done, it must not be left to be reused by a
      * subsequent transfer but shall be closed immediately.
      */
-    data->set.reuse_forbid = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.reuse_forbid = (0 != va_arg(param, long));
     break;
   case CURLOPT_FRESH_CONNECT:
     /*
      * This transfer shall not use a previously cached connection but
      * should be made with a fresh new connect!
      */
-    data->set.reuse_fresh = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.reuse_fresh = (0 != va_arg(param, long));
     break;
   case CURLOPT_VERBOSE:
     /*
      * Verbose means infof() calls that give a lot of information about
      * the connection and transfer procedures as well as internal choices.
      */
-    data->set.verbose = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.verbose = (0 != va_arg(param, long));
     break;
   case CURLOPT_HEADER:
     /*
      * Set to include the header in the general data output stream.
      */
-    data->set.include_header = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.include_header = (0 != va_arg(param, long));
     break;
   case CURLOPT_NOPROGRESS:
     /*
      * Shut off the internal supported progress meter
      */
-    data->set.hide_progress = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.hide_progress = (0 != va_arg(param, long));
     if(data->set.hide_progress)
       data->progress.flags |= PGRS_HIDE;
     else
@@ -307,7 +308,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     /*
      * Do not include the body part in the output data stream.
      */
-    data->set.opt_no_body = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.opt_no_body = (0 != va_arg(param, long));
 #ifndef CURL_DISABLE_HTTP
     if(data->set.opt_no_body)
       /* in HTTP lingo, no body means using the HEAD request... */
@@ -321,11 +322,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      * Don't output the >=400 error code HTML-page, but instead only
      * return error.
      */
-    data->set.http_fail_on_error = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.http_fail_on_error = (0 != va_arg(param, long));
     break;
   case CURLOPT_KEEP_SENDING_ON_ERROR:
-    data->set.http_keep_sending_on_error = (0 != va_arg(param, long)) ?
-      TRUE : FALSE;
+    data->set.http_keep_sending_on_error = (0 != va_arg(param, long));
     break;
   case CURLOPT_UPLOAD:
   case CURLOPT_PUT:
@@ -353,7 +353,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      * Try to get the file time of the remote document. The time will
      * later (possibly) become available using curl_easy_getinfo().
      */
-    data->set.get_filetime = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.get_filetime = (0 != va_arg(param, long));
     break;
   case CURLOPT_SERVER_RESPONSE_TIMEOUT:
     /*
@@ -379,7 +379,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      * TFTP option that specifies the block size to use for data transmission.
      */
     arg = va_arg(param, long);
-    if(arg < 0)
+    if(arg > TFTP_BLKSIZE_MAX || arg < TFTP_BLKSIZE_MIN)
       return CURLE_BAD_FUNCTION_ARGUMENT;
     data->set.tftp_blksize = arg;
     break;
@@ -409,7 +409,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      *
      * Transfer using ASCII (instead of BINARY).
      */
-    data->set.prefer_ascii = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.prefer_ascii = (0 != va_arg(param, long));
     break;
   case CURLOPT_TIMECONDITION:
     /*
@@ -577,7 +577,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     /*
      * Switch on automatic referer that gets set if curl follows locations.
      */
-    data->set.http_auto_referer = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.http_auto_referer = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_ACCEPT_ENCODING:
@@ -592,28 +592,23 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      */
     argptr = va_arg(param, char *);
     if(argptr && !*argptr) {
-      argptr = Curl_all_content_encodings();
-      if(!argptr)
-        result = CURLE_OUT_OF_MEMORY;
-      else {
-        result = Curl_setstropt(&data->set.str[STRING_ENCODING], argptr);
-        free(argptr);
-      }
+      char all[256];
+      Curl_all_content_encodings(all, sizeof(all));
+      result = Curl_setstropt(&data->set.str[STRING_ENCODING], all);
     }
     else
       result = Curl_setstropt(&data->set.str[STRING_ENCODING], argptr);
     break;
 
   case CURLOPT_TRANSFER_ENCODING:
-    data->set.http_transfer_encoding = (0 != va_arg(param, long)) ?
-      TRUE : FALSE;
+    data->set.http_transfer_encoding = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_FOLLOWLOCATION:
     /*
      * Follow Location: header hints on an HTTP-server.
      */
-    data->set.http_follow_location = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.http_follow_location = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_UNRESTRICTED_AUTH:
@@ -621,8 +616,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      * Send authentication (user+password) when following locations, even when
      * hostname changed.
      */
-    data->set.allow_auth_to_other_hosts =
-      (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.allow_auth_to_other_hosts = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_MAXREDIRS:
@@ -736,7 +730,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      * Set header option.
      */
     arg = va_arg(param, long);
-    data->set.sep_headers = (bool)((arg & CURLHEADER_SEPARATE)? TRUE: FALSE);
+    data->set.sep_headers = !!(arg & CURLHEADER_SEPARATE);
     break;
 
 #if !defined(CURL_DISABLE_COOKIES)
@@ -760,18 +754,18 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
         return CURLE_BAD_FUNCTION_ARGUMENT;
       /* append the cookie file name to the list of file names, and deal with
          them later */
-      cl = curl_slist_append(data->set.cookielist, argptr);
+      cl = curl_slist_append(data->state.cookielist, argptr);
       if(!cl) {
-        curl_slist_free_all(data->set.cookielist);
-        data->set.cookielist = NULL;
+        curl_slist_free_all(data->state.cookielist);
+        data->state.cookielist = NULL;
         return CURLE_OUT_OF_MEMORY;
       }
-      data->set.cookielist = cl; /* store the list for later use */
+      data->state.cookielist = cl; /* store the list for later use */
     }
     else {
       /* clear the list of cookie files */
-      curl_slist_free_all(data->set.cookielist);
-      data->set.cookielist = NULL;
+      curl_slist_free_all(data->state.cookielist);
+      data->state.cookielist = NULL;
 
       if(!data->share || !data->share->cookies) {
         /* throw away all existing cookies if this isn't a shared cookie
@@ -811,17 +805,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      * prevent the forthcoming read-cookies-from-file actions to accept
      * cookies that are marked as being session cookies, as they belong to a
      * previous session.
-     *
-     * In the original Netscape cookie spec, "session cookies" are cookies
-     * with no expire date set. RFC2109 describes the same action if no
-     * 'Max-Age' is set and RFC2965 includes the RFC2109 description and adds
-     * a 'Discard' action that can enforce the discard even for cookies that
-     * have a Max-Age.
-     *
-     * We run mostly with the original cookie spec, as hardly anyone implements
-     * anything else.
      */
-    data->set.cookiesession = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.cookiesession = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_COOKIELIST:
@@ -956,7 +941,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     if(arg)
       return CURLE_BAD_FUNCTION_ARGUMENT;
 #else
-    data->set.http09_allowed = arg ? TRUE : FALSE;
+    data->set.http09_allowed = !!arg;
 #endif
     break;
 
@@ -997,8 +982,9 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
 
   case CURLOPT_MIME_OPTIONS:
-    data->set.mime_options = (unsigned int)va_arg(param, long);
-    break;
+    arg = va_arg(param, long);
+    data->set.mime_formescape = !!(arg & CURLMIMEOPT_FORMESCAPE);
+  break;
 # endif
 #endif
 
@@ -1018,8 +1004,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
 
     /* the DIGEST_IE bit is only used to set a special marker, for all the
        rest we need to handle it as normal DIGEST */
-    data->state.authhost.iestyle =
-      (bool)((auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE);
+    data->state.authhost.iestyle = !!(auth & CURLAUTH_DIGEST_IE);
 
     if(auth & CURLAUTH_DIGEST_IE) {
       auth |= CURLAUTH_DIGEST; /* set standard digest bit */
@@ -1072,8 +1057,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     /*
      * Tunnel operations through the proxy instead of normal proxy use
      */
-    data->set.tunnel_thru_httpproxy = (0 != va_arg(param, long)) ?
-      TRUE : FALSE;
+    data->set.tunnel_thru_httpproxy = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_PROXYPORT:
@@ -1102,8 +1086,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
 
     /* the DIGEST_IE bit is only used to set a special marker, for all the
        rest we need to handle it as normal DIGEST */
-    data->state.authproxy.iestyle =
-      (bool)((auth & CURLAUTH_DIGEST_IE) ? TRUE : FALSE);
+    data->state.authproxy.iestyle = !!(auth & CURLAUTH_DIGEST_IE);
 
     if(auth & CURLAUTH_DIGEST_IE) {
       auth |= CURLAUTH_DIGEST; /* set standard digest bit */
@@ -1203,7 +1186,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     /*
      * Set flag for NEC SOCK5 support
      */
-    data->set.socks5_gssapi_nec = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.socks5_gssapi_nec = (0 != va_arg(param, long));
     break;
 #endif
 #ifndef CURL_DISABLE_PROXY
@@ -1251,7 +1234,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      * An option that changes the command to one that asks for a list only, no
      * file info details. Used for FTP, POP3 and SFTP.
      */
-    data->set.list_only = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.list_only = (0 != va_arg(param, long));
     break;
 #endif
   case CURLOPT_APPEND:
@@ -1259,7 +1242,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      * We want to upload and append to an existing file. Used for FTP and
      * SFTP.
      */
-    data->set.remote_append = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.remote_append = (0 != va_arg(param, long));
     break;
 
 #ifndef CURL_DISABLE_FTP
@@ -1270,7 +1253,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     arg = va_arg(param, long);
     if((arg < CURLFTPMETHOD_DEFAULT) || (arg >= CURLFTPMETHOD_LAST))
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.ftp_filemethod = (unsigned char)(curl_ftpfile)arg;
+    data->set.ftp_filemethod = (unsigned char)arg;
     break;
   case CURLOPT_FTPPORT:
     /*
@@ -1278,26 +1261,26 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      */
     result = Curl_setstropt(&data->set.str[STRING_FTPPORT],
                             va_arg(param, char *));
-    data->set.ftp_use_port = (data->set.str[STRING_FTPPORT]) ? TRUE : FALSE;
+    data->set.ftp_use_port = !!(data->set.str[STRING_FTPPORT]);
     break;
 
   case CURLOPT_FTP_USE_EPRT:
-    data->set.ftp_use_eprt = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.ftp_use_eprt = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_FTP_USE_EPSV:
-    data->set.ftp_use_epsv = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.ftp_use_epsv = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_FTP_USE_PRET:
-    data->set.ftp_use_pret = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.ftp_use_pret = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_FTP_SSL_CCC:
     arg = va_arg(param, long);
     if((arg < CURLFTPSSL_CCC_NONE) || (arg >= CURLFTPSSL_CCC_LAST))
       return CURLE_BAD_FUNCTION_ARGUMENT;
-    data->set.ftp_ccc = (unsigned char)(curl_ftpccc)arg;
+    data->set.ftp_ccc = (unsigned char)arg;
     break;
 
   case CURLOPT_FTP_SKIP_PASV_IP:
@@ -1305,7 +1288,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the
      * bypass of the IP address in PASV responses.
      */
-    data->set.ftp_skip_ip = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.ftp_skip_ip = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_FTP_ACCOUNT:
@@ -1333,7 +1316,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      */
     result = Curl_setstropt(&data->set.str[STRING_KRB_LEVEL],
                             va_arg(param, char *));
-    data->set.krb = (data->set.str[STRING_KRB_LEVEL]) ? TRUE : FALSE;
+    data->set.krb = !!(data->set.str[STRING_KRB_LEVEL]);
     break;
 #endif
 #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH)
@@ -1867,14 +1850,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     /*
      * Kludgy option to enable CRLF conversions. Subject for removal.
      */
-    data->set.crlf = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.crlf = (0 != va_arg(param, long));
     break;
 #ifndef CURL_DISABLE_PROXY
   case CURLOPT_HAPROXYPROTOCOL:
     /*
      * Set to send the HAProxy Proxy Protocol header
      */
-    data->set.haproxyprotocol = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.haproxyprotocol = (0 != va_arg(param, long));
     break;
   case CURLOPT_HAPROXY_CLIENT_IP:
     /*
@@ -1926,22 +1909,17 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     /*
      * Enable peer SSL verifying.
      */
-    data->set.ssl.primary.verifypeer = (0 != va_arg(param, long)) ?
-      TRUE : FALSE;
+    data->set.ssl.primary.verifypeer = (0 != va_arg(param, long));
 
     /* Update the current connection ssl_config. */
-    if(data->conn) {
-      data->conn->ssl_config.verifypeer =
-        data->set.ssl.primary.verifypeer;
-    }
+    Curl_ssl_conn_config_update(data, FALSE);
     break;
 #ifndef CURL_DISABLE_DOH
   case CURLOPT_DOH_SSL_VERIFYPEER:
     /*
      * Enable peer SSL verifying for DoH.
      */
-    data->set.doh_verifypeer = (0 != va_arg(param, long)) ?
-      TRUE : FALSE;
+    data->set.doh_verifypeer = (0 != va_arg(param, long));
     break;
 #endif
 #ifndef CURL_DISABLE_PROXY
@@ -1953,10 +1931,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
       (0 != va_arg(param, long))?TRUE:FALSE;
 
     /* Update the current connection proxy_ssl_config. */
-    if(data->conn) {
-      data->conn->proxy_ssl_config.verifypeer =
-        data->set.proxy_ssl.primary.verifypeer;
-    }
+    Curl_ssl_conn_config_update(data, TRUE);
     break;
 #endif
   case CURLOPT_SSL_VERIFYHOST:
@@ -1968,13 +1943,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     /* Obviously people are not reading documentation and too many thought
        this argument took a boolean when it wasn't and misused it.
        Treat 1 and 2 the same */
-    data->set.ssl.primary.verifyhost = (bool)((arg & 3) ? TRUE : FALSE);
+    data->set.ssl.primary.verifyhost = !!(arg & 3);
 
     /* Update the current connection ssl_config. */
-    if(data->conn) {
-      data->conn->ssl_config.verifyhost =
-        data->set.ssl.primary.verifyhost;
-    }
+    Curl_ssl_conn_config_update(data, FALSE);
     break;
 #ifndef CURL_DISABLE_DOH
   case CURLOPT_DOH_SSL_VERIFYHOST:
@@ -1984,7 +1956,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     arg = va_arg(param, long);
 
     /* Treat both 1 and 2 as TRUE */
-    data->set.doh_verifyhost = (bool)((arg & 3) ? TRUE : FALSE);
+    data->set.doh_verifyhost = !!(arg & 3);
     break;
 #endif
 #ifndef CURL_DISABLE_PROXY
@@ -1996,12 +1968,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
 
     /* Treat both 1 and 2 as TRUE */
     data->set.proxy_ssl.primary.verifyhost = (bool)((arg & 3)?TRUE:FALSE);
-
     /* Update the current connection proxy_ssl_config. */
-    if(data->conn) {
-      data->conn->proxy_ssl_config.verifyhost =
-        data->set.proxy_ssl.primary.verifyhost;
-    }
+    Curl_ssl_conn_config_update(data, TRUE);
     break;
 #endif
   case CURLOPT_SSL_VERIFYSTATUS:
@@ -2013,14 +1981,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
       break;
     }
 
-    data->set.ssl.primary.verifystatus = (0 != va_arg(param, long)) ?
-      TRUE : FALSE;
+    data->set.ssl.primary.verifystatus = (0 != va_arg(param, long));
 
     /* Update the current connection ssl_config. */
-    if(data->conn) {
-      data->conn->ssl_config.verifystatus =
-        data->set.ssl.primary.verifystatus;
-    }
+    Curl_ssl_conn_config_update(data, FALSE);
     break;
 #ifndef CURL_DISABLE_DOH
   case CURLOPT_DOH_SSL_VERIFYSTATUS:
@@ -2032,8 +1996,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
       break;
     }
 
-    data->set.doh_verifystatus = (0 != va_arg(param, long)) ?
-      TRUE : FALSE;
+    data->set.doh_verifystatus = (0 != va_arg(param, long));
     break;
 #endif
   case CURLOPT_SSL_CTX_FUNCTION:
@@ -2067,12 +2030,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
       break;
     }
 
-    data->set.ssl.falsestart = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.ssl.falsestart = (0 != va_arg(param, long));
     break;
   case CURLOPT_CERTINFO:
 #ifdef USE_SSL
     if(Curl_ssl_supports(data, SSLSUPP_CERTINFO))
-      data->set.ssl.certinfo = (0 != va_arg(param, long)) ? TRUE : FALSE;
+      data->set.ssl.certinfo = (0 != va_arg(param, long));
     else
 #endif
       result = CURLE_NOT_BUILT_IN;
@@ -2118,14 +2081,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      * Specify entire PEM of the CA certificate
      */
 #ifdef USE_SSL
-    if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB))
+    if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) {
       result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO],
                                va_arg(param, struct curl_blob *));
+      break;
+    }
     else
 #endif
       return CURLE_NOT_BUILT_IN;
-
-    break;
 #ifndef CURL_DISABLE_PROXY
   case CURLOPT_PROXY_CAINFO:
     /*
@@ -2141,13 +2104,14 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      * Specify entire PEM of the CA certificate
      */
 #ifdef USE_SSL
-    if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB))
+    if(Curl_ssl_supports(data, SSLSUPP_CAINFO_BLOB)) {
       result = Curl_setblobopt(&data->set.blobs[BLOB_CAINFO_PROXY],
                                va_arg(param, struct curl_blob *));
+      break;
+    }
     else
 #endif
       return CURLE_NOT_BUILT_IN;
-    break;
 #endif
   case CURLOPT_CAPATH:
     /*
@@ -2278,7 +2242,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      * The application asks not to set any signal() or alarm() handlers,
      * even when using a timeout.
      */
-    data->set.no_signal = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.no_signal = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_SHARE:
@@ -2453,11 +2417,11 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      * Enable or disable TCP_NODELAY, which will disable/enable the Nagle
      * algorithm
      */
-    data->set.tcp_nodelay = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.tcp_nodelay = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_IGNORE_CONTENT_LENGTH:
-    data->set.ignorecl = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.ignorecl = (0 != va_arg(param, long));
     break;
 
   case CURLOPT_CONNECT_ONLY:
@@ -2532,8 +2496,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
 
   case CURLOPT_SSL_SESSIONID_CACHE:
-    data->set.ssl.primary.sessionid = (0 != va_arg(param, long)) ?
-      TRUE : FALSE;
+    data->set.ssl.primary.sessionid = (0 != va_arg(param, long));
 #ifndef CURL_DISABLE_PROXY
     data->set.proxy_ssl.primary.sessionid = data->set.ssl.primary.sessionid;
 #endif
@@ -2622,7 +2585,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      * disable libcurl transfer encoding is used
      */
 #ifndef USE_HYPER
-    data->set.http_te_skip = (0 == va_arg(param, long)) ? TRUE : FALSE;
+    data->set.http_te_skip = (0 == va_arg(param, long));
     break;
 #else
     return CURLE_NOT_BUILT_IN; /* hyper doesn't support */
@@ -2632,7 +2595,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     /*
      * raw data passed to the application when content encoding is used
      */
-    data->set.http_ce_skip = (0 == va_arg(param, long)) ? TRUE : FALSE;
+    data->set.http_ce_skip = (0 == va_arg(param, long));
     break;
 
 #if !defined(CURL_DISABLE_FTP) || defined(USE_SSH)
@@ -2733,7 +2696,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
   case CURLOPT_MAIL_RCPT_ALLOWFAILS:
     /* allow RCPT TO command to fail for some recipients */
-    data->set.mail_rcpt_allowfails = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.mail_rcpt_allowfails = (0 != va_arg(param, long));
     break;
 #endif
 
@@ -2745,7 +2708,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
 
   case CURLOPT_SASL_IR:
     /* Enable/disable SASL initial response */
-    data->set.sasl_ir = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.sasl_ir = (0 != va_arg(param, long));
     break;
 #ifndef CURL_DISABLE_RTSP
   case CURLOPT_RTSP_REQUEST:
@@ -2859,7 +2822,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
 #endif
 #ifndef CURL_DISABLE_FTP
   case CURLOPT_WILDCARDMATCH:
-    data->set.wildcard_enabled = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.wildcard_enabled = (0 != va_arg(param, long));
     break;
   case CURLOPT_CHUNK_BGN_FUNCTION:
     data->set.chunk_bgn = va_arg(param, curl_chunk_bgn_callback);
@@ -2942,7 +2905,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
 #endif
   case CURLOPT_TCP_KEEPALIVE:
-    data->set.tcp_keepalive = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.tcp_keepalive = (0 != va_arg(param, long));
     break;
   case CURLOPT_TCP_KEEPIDLE:
     arg = va_arg(param, long);
@@ -2971,7 +2934,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
   case CURLOPT_SSL_ENABLE_NPN:
     break;
   case CURLOPT_SSL_ENABLE_ALPN:
-    data->set.ssl_enable_alpn = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.ssl_enable_alpn = (0 != va_arg(param, long));
     break;
 #ifdef USE_UNIX_SOCKETS
   case CURLOPT_UNIX_SOCKET_PATH:
@@ -2987,10 +2950,10 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
 #endif
 
   case CURLOPT_PATH_AS_IS:
-    data->set.path_as_is = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.path_as_is = (0 != va_arg(param, long));
     break;
   case CURLOPT_PIPEWAIT:
-    data->set.pipewait = (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.pipewait = (0 != va_arg(param, long));
     break;
   case CURLOPT_STREAM_WEIGHT:
 #if defined(USE_HTTP2) || defined(USE_HTTP3)
@@ -3025,12 +2988,11 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
 #ifndef CURL_DISABLE_SHUFFLE_DNS
   case CURLOPT_DNS_SHUFFLE_ADDRESSES:
-    data->set.dns_shuffle_addresses = (0 != va_arg(param, long)) ? TRUE:FALSE;
+    data->set.dns_shuffle_addresses = (0 != va_arg(param, long));
     break;
 #endif
   case CURLOPT_DISALLOW_USERNAME_IN_URL:
-    data->set.disallow_username_in_url =
-      (0 != va_arg(param, long)) ? TRUE : FALSE;
+    data->set.disallow_username_in_url = (0 != va_arg(param, long));
     break;
 #ifndef CURL_DISABLE_DOH
   case CURLOPT_DOH_URL:
@@ -3095,18 +3057,18 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
       /* this needs to build a list of file names to read from, so that it can
          read them later, as we might get a shared HSTS handle to load them
          into */
-      h = curl_slist_append(data->set.hstslist, argptr);
+      h = curl_slist_append(data->state.hstslist, argptr);
       if(!h) {
-        curl_slist_free_all(data->set.hstslist);
-        data->set.hstslist = NULL;
+        curl_slist_free_all(data->state.hstslist);
+        data->state.hstslist = NULL;
         return CURLE_OUT_OF_MEMORY;
       }
-      data->set.hstslist = h; /* store the list for later use */
+      data->state.hstslist = h; /* store the list for later use */
     }
     else {
       /* clear the list of HSTS files */
-      curl_slist_free_all(data->set.hstslist);
-      data->set.hstslist = NULL;
+      curl_slist_free_all(data->state.hstslist);
+      data->state.hstslist = NULL;
       if(!data->share || !data->share->hsts)
         /* throw away the HSTS cache unless shared */
         Curl_hsts_cleanup(&data->hsts);

+ 2 - 11
lib/setup-win32.h

@@ -53,14 +53,14 @@
 #  ifndef NOGDI
 #    define NOGDI
 #  endif
-#  include <winerror.h>
-#  include <windows.h>
 #  ifdef HAVE_WINSOCK2_H
 #    include <winsock2.h>
 #    ifdef HAVE_WS2TCPIP_H
 #      include <ws2tcpip.h>
 #    endif
 #  endif
+#  include <windows.h>
+#  include <winerror.h>
 #  include <tchar.h>
 #  ifdef UNICODE
      typedef wchar_t *(*curl_wcsdup_callback)(const wchar_t *str);
@@ -96,18 +96,12 @@
 #ifndef _WIN32_WINNT_WS03
 #define _WIN32_WINNT_WS03           0x0502   /* Windows Server 2003 */
 #endif
-#ifndef _WIN32_WINNT_WIN6
-#define _WIN32_WINNT_WIN6           0x0600   /* Windows Vista */
-#endif
 #ifndef _WIN32_WINNT_VISTA
 #define _WIN32_WINNT_VISTA          0x0600   /* Windows Vista */
 #endif
 #ifndef _WIN32_WINNT_WS08
 #define _WIN32_WINNT_WS08           0x0600   /* Windows Server 2008 */
 #endif
-#ifndef _WIN32_WINNT_LONGHORN
-#define _WIN32_WINNT_LONGHORN       0x0600   /* Windows Vista */
-#endif
 #ifndef _WIN32_WINNT_WIN7
 #define _WIN32_WINNT_WIN7           0x0601   /* Windows 7 */
 #endif
@@ -117,9 +111,6 @@
 #ifndef _WIN32_WINNT_WINBLUE
 #define _WIN32_WINNT_WINBLUE        0x0603   /* Windows 8.1 */
 #endif
-#ifndef _WIN32_WINNT_WINTHRESHOLD
-#define _WIN32_WINNT_WINTHRESHOLD   0x0A00   /* Windows 10 */
-#endif
 #ifndef _WIN32_WINNT_WIN10
 #define _WIN32_WINNT_WIN10          0x0A00   /* Windows 10 */
 #endif

+ 1 - 9
lib/share.h

@@ -31,14 +31,6 @@
 #include "urldata.h"
 #include "conncache.h"
 
-/* SalfordC says "A structure member may not be volatile". Hence:
- */
-#ifdef __SALFORDC__
-#define CURL_VOLATILE
-#else
-#define CURL_VOLATILE volatile
-#endif
-
 #define CURL_GOOD_SHARE 0x7e117a1e
 #define GOOD_SHARE_HANDLE(x) ((x) && (x)->magic == CURL_GOOD_SHARE)
 
@@ -46,7 +38,7 @@
 struct Curl_share {
   unsigned int magic; /* CURL_GOOD_SHARE */
   unsigned int specifier;
-  CURL_VOLATILE unsigned int dirty;
+  volatile unsigned int dirty;
 
   curl_lock_function lockfunc;
   curl_unlock_function unlockfunc;

+ 1 - 8
lib/smb.c

@@ -27,7 +27,7 @@
 
 #if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE)
 
-#ifdef WIN32
+#ifdef _WIN32
 #define getpid GetCurrentProcessId
 #endif
 
@@ -1047,14 +1047,7 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
         break;
       }
     }
-    data->req.bytecount += len;
     data->req.offset += len;
-    result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
-    if(result) {
-      req->result = result;
-      next_state = SMB_CLOSE;
-      break;
-    }
     next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD;
     break;
 

+ 1 - 1
lib/smtp.c

@@ -1320,7 +1320,7 @@ static CURLcode smtp_init(struct Curl_easy *data)
   CURLcode result = CURLE_OK;
   struct SMTP *smtp;
 
-  smtp = data->req.p.smtp = calloc(sizeof(struct SMTP), 1);
+  smtp = data->req.p.smtp = calloc(1, sizeof(struct SMTP));
   if(!smtp)
     result = CURLE_OUT_OF_MEMORY;
 

+ 3 - 3
lib/socketpair.c

@@ -28,7 +28,7 @@
 #include "rand.h"
 
 #if !defined(HAVE_SOCKETPAIR) && !defined(CURL_DISABLE_SOCKETPAIR)
-#ifdef WIN32
+#ifdef _WIN32
 /*
  * This is a socketpair() implementation for Windows.
  */
@@ -50,7 +50,7 @@
 #ifndef INADDR_LOOPBACK
 #define INADDR_LOOPBACK 0x7f000001
 #endif /* !INADDR_LOOPBACK */
-#endif /* !WIN32 */
+#endif /* !_WIN32 */
 
 #include "nonblock.h" /* for curlx_nonblock */
 #include "timeval.h"  /* needed before select.h */
@@ -87,7 +87,7 @@ int Curl_socketpair(int domain, int type, int protocol,
 
   socks[0] = socks[1] = CURL_SOCKET_BAD;
 
-#if defined(WIN32) || defined(__CYGWIN__)
+#if defined(_WIN32) || defined(__CYGWIN__)
   /* don't set SO_REUSEADDR on Windows */
   (void)reuse;
 #ifdef SO_EXCLUSIVEADDRUSE

+ 17 - 0
lib/socketpair.h

@@ -25,6 +25,23 @@
  ***************************************************************************/
 
 #include "curl_setup.h"
+
+#ifdef HAVE_PIPE
+
+#define wakeup_write  write
+#define wakeup_read   read
+#define wakeup_close  close
+#define wakeup_create pipe
+
+#else /* HAVE_PIPE */
+
+#define wakeup_write     swrite
+#define wakeup_read      sread
+#define wakeup_close     sclose
+#define wakeup_create(p) Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, p)
+
+#endif /* HAVE_PIPE */
+
 #ifndef HAVE_SOCKETPAIR
 #include <curl/curl.h>
 

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