Bläddra i källkod

Merge topic 'update-curl'

4eff1f4ac2 curl: Remove curlu library not needed for building within CMake
188c065e5a Merge branch 'upstream-curl' into update-curl
e2ab2da70a curl 2023-07-26 (50490c06)
1345c21275 curl: Update script to get curl 8.2.1

Acked-by: Kitware Robot <[email protected]>
Acked-by: buildbot <[email protected]>
Merge-request: !8678
Brad King 2 år sedan
förälder
incheckning
1fd423d368
93 ändrade filer med 2466 tillägg och 1711 borttagningar
  1. 1 1
      Utilities/Scripts/update-curl.bash
  2. 2 2
      Utilities/cmcurl/CMake/FindNGTCP2.cmake
  3. 5 5
      Utilities/cmcurl/CMakeLists.txt
  4. 12 4
      Utilities/cmcurl/include/curl/curl.h
  5. 4 4
      Utilities/cmcurl/include/curl/curlver.h
  6. 21 26
      Utilities/cmcurl/include/curl/system.h
  7. 1 0
      Utilities/cmcurl/include/curl/typecheck-gcc.h
  8. 6 6
      Utilities/cmcurl/include/curl/websockets.h
  9. 19 0
      Utilities/cmcurl/lib/CMakeLists.txt
  10. 3 0
      Utilities/cmcurl/lib/Makefile.inc
  11. 3 2
      Utilities/cmcurl/lib/altsvc.c
  12. 1 0
      Utilities/cmcurl/lib/amigaos.c
  13. 3 3
      Utilities/cmcurl/lib/base64.c
  14. 24 5
      Utilities/cmcurl/lib/bufq.c
  15. 9 2
      Utilities/cmcurl/lib/c-hyper.c
  16. 62 62
      Utilities/cmcurl/lib/cf-h1-proxy.c
  17. 329 202
      Utilities/cmcurl/lib/cf-h2-proxy.c
  18. 8 3
      Utilities/cmcurl/lib/cf-haproxy.c
  19. 6 6
      Utilities/cmcurl/lib/cf-https-connect.c
  20. 43 34
      Utilities/cmcurl/lib/cf-socket.c
  21. 7 7
      Utilities/cmcurl/lib/cfilters.c
  22. 2 2
      Utilities/cmcurl/lib/cfilters.h
  23. 2 1
      Utilities/cmcurl/lib/conncache.h
  24. 3 3
      Utilities/cmcurl/lib/connect.c
  25. 5 4
      Utilities/cmcurl/lib/cookie.c
  26. 3 3
      Utilities/cmcurl/lib/curl_config.h.cmake
  27. 2 4
      Utilities/cmcurl/lib/curl_log.c
  28. 7 24
      Utilities/cmcurl/lib/curl_log.h
  29. 57 1
      Utilities/cmcurl/lib/curl_memory.h
  30. 1 0
      Utilities/cmcurl/lib/curl_printf.h
  31. 9 9
      Utilities/cmcurl/lib/curl_sasl.c
  32. 2 1
      Utilities/cmcurl/lib/curl_setup.h
  33. 6 0
      Utilities/cmcurl/lib/curl_setup_once.h
  34. 0 2
      Utilities/cmcurl/lib/dynbuf.h
  35. 13 5
      Utilities/cmcurl/lib/easy.c
  36. 4 0
      Utilities/cmcurl/lib/easy_lock.h
  37. 5 2
      Utilities/cmcurl/lib/easyoptions.c
  38. 7 7
      Utilities/cmcurl/lib/fopen.c
  39. 65 65
      Utilities/cmcurl/lib/ftp.c
  40. 7 0
      Utilities/cmcurl/lib/getinfo.c
  41. 7 23
      Utilities/cmcurl/lib/hostip.c
  42. 3 2
      Utilities/cmcurl/lib/hsts.c
  43. 17 15
      Utilities/cmcurl/lib/http.c
  44. 51 78
      Utilities/cmcurl/lib/http1.c
  45. 2 2
      Utilities/cmcurl/lib/http1.h
  46. 204 128
      Utilities/cmcurl/lib/http2.c
  47. 2 2
      Utilities/cmcurl/lib/http_proxy.c
  48. 55 41
      Utilities/cmcurl/lib/imap.c
  49. 1 1
      Utilities/cmcurl/lib/krb5.c
  50. 8 0
      Utilities/cmcurl/lib/ldap.c
  51. 62 0
      Utilities/cmcurl/lib/macos.c
  52. 38 0
      Utilities/cmcurl/lib/macos.h
  53. 8 8
      Utilities/cmcurl/lib/mime.c
  54. 1 1
      Utilities/cmcurl/lib/mqtt.c
  55. 34 23
      Utilities/cmcurl/lib/multi.c
  56. 22 22
      Utilities/cmcurl/lib/pop3.c
  57. 0 2
      Utilities/cmcurl/lib/sendf.c
  58. 10 1
      Utilities/cmcurl/lib/setopt.c
  59. 193 2
      Utilities/cmcurl/lib/smb.c
  60. 0 197
      Utilities/cmcurl/lib/smb.h
  61. 22 22
      Utilities/cmcurl/lib/smtp.c
  62. 9 10
      Utilities/cmcurl/lib/socks.c
  63. 2 3
      Utilities/cmcurl/lib/telnet.c
  64. 15 1
      Utilities/cmcurl/lib/timeval.c
  65. 10 6
      Utilities/cmcurl/lib/transfer.c
  66. 22 16
      Utilities/cmcurl/lib/url.c
  67. 52 41
      Utilities/cmcurl/lib/urlapi.c
  68. 13 3
      Utilities/cmcurl/lib/urldata.h
  69. 2 2
      Utilities/cmcurl/lib/version.c
  70. 3 2
      Utilities/cmcurl/lib/vquic/curl_msh3.c
  71. 263 166
      Utilities/cmcurl/lib/vquic/curl_ngtcp2.c
  72. 22 30
      Utilities/cmcurl/lib/vquic/curl_quiche.c
  73. 3 3
      Utilities/cmcurl/lib/vquic/vquic.c
  74. 13 13
      Utilities/cmcurl/lib/vssh/libssh2.c
  75. 1 1
      Utilities/cmcurl/lib/vssh/wolfssh.c
  76. 21 11
      Utilities/cmcurl/lib/vtls/bearssl.c
  77. 8 6
      Utilities/cmcurl/lib/vtls/gskit.c
  78. 28 14
      Utilities/cmcurl/lib/vtls/gtls.c
  79. 21 11
      Utilities/cmcurl/lib/vtls/mbedtls.c
  80. 55 26
      Utilities/cmcurl/lib/vtls/nss.c
  81. 49 32
      Utilities/cmcurl/lib/vtls/openssl.c
  82. 22 12
      Utilities/cmcurl/lib/vtls/rustls.c
  83. 37 27
      Utilities/cmcurl/lib/vtls/schannel.c
  84. 0 116
      Utilities/cmcurl/lib/vtls/schannel.h
  85. 142 0
      Utilities/cmcurl/lib/vtls/schannel_int.h
  86. 2 2
      Utilities/cmcurl/lib/vtls/schannel_verify.c
  87. 29 14
      Utilities/cmcurl/lib/vtls/sectransp.c
  88. 13 14
      Utilities/cmcurl/lib/vtls/vtls.c
  89. 2 1
      Utilities/cmcurl/lib/vtls/vtls_int.h
  90. 56 17
      Utilities/cmcurl/lib/vtls/wolfssl.c
  91. 8 2
      Utilities/cmcurl/lib/warnless.c
  92. 4 6
      Utilities/cmcurl/lib/warnless.h
  93. 30 26
      Utilities/cmcurl/lib/ws.c

+ 1 - 1
Utilities/Scripts/update-curl.bash

@@ -8,7 +8,7 @@ readonly name="curl"
 readonly ownership="Curl Upstream <[email protected]>"
 readonly subtree="Utilities/cmcurl"
 readonly repo="https://github.com/curl/curl.git"
-readonly tag="curl-8_1_2"
+readonly tag="curl-8_2_1"
 readonly shortlog=false
 readonly paths="
   CMake/*

+ 2 - 2
Utilities/cmcurl/CMake/FindNGTCP2.cmake

@@ -31,7 +31,7 @@ Find the ngtcp2 library
 This module accepts optional COMPONENTS to control the crypto library (these are
 mutually exclusive)::
 
-  OpenSSL:  Use libngtcp2_crypto_openssl
+  OpenSSL:  Use libngtcp2_crypto_quictls
   GnuTLS:   Use libngtcp2_crypto_gnutls
 
 Result Variables
@@ -71,7 +71,7 @@ endif()
 if(NGTCP2_FIND_COMPONENTS)
   set(NGTCP2_CRYPTO_BACKEND "")
   foreach(component IN LISTS NGTCP2_FIND_COMPONENTS)
-    if(component MATCHES "^(BoringSSL|OpenSSL|wolfSSL|GnuTLS)")
+    if(component MATCHES "^(BoringSSL|quictls|wolfSSL|GnuTLS)")
       if(NGTCP2_CRYPTO_BACKEND)
         message(FATAL_ERROR "NGTCP2: Only one crypto library can be selected")
       endif()

+ 5 - 5
Utilities/cmcurl/CMakeLists.txt

@@ -242,6 +242,8 @@ endif()
 
 include_directories(${CURL_SOURCE_DIR}/include)
 
+set(CMAKE_UNITY_BUILD_BATCH_SIZE 0)
+
 option(CURL_WERROR "Turn compiler warnings into errors" OFF)
 option(PICKY_COMPILER "Enable picky compiler options" ON)
 option(BUILD_CURL_EXE "Set to ON to build curl executable." ON)
@@ -689,7 +691,7 @@ endif()
 option(CURL_BROTLI "Set to ON to enable building curl with brotli support." OFF)
 set(HAVE_BROTLI OFF)
 if(CURL_BROTLI)
-  find_package(Brotli QUIET)
+  find_package(Brotli REQUIRED)
   if(BROTLI_FOUND)
     set(HAVE_BROTLI ON)
     list(APPEND CURL_LIBS ${BROTLI_LIBRARIES})
@@ -768,7 +770,7 @@ if(USE_NGTCP2)
     elseif(HAVE_BORINGSSL)
       find_package(NGTCP2 REQUIRED BoringSSL)
     else()
-      find_package(NGTCP2 REQUIRED OpenSSL)
+      find_package(NGTCP2 REQUIRED quictls)
     endif()
     CheckQuicSupportInOpenSSL()
   elseif(USE_GNUTLS)
@@ -839,9 +841,7 @@ if(NOT CURL_DISABLE_LDAP)
   endif()
 
   # Now that we know, we're not using windows LDAP...
-  if(USE_WIN32_LDAP)
-    check_include_file_concat("winldap.h" HAVE_WINLDAP_H)
-  else()
+  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)

+ 12 - 4
Utilities/cmcurl/include/curl/curl.h

@@ -93,7 +93,7 @@
     defined(__CYGWIN__) || defined(AMIGA) || defined(__NuttX__) || \
    (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) || \
    (defined(__MidnightBSD_version) && (__MidnightBSD_version < 100000)) || \
-    defined(__sun__) || defined(__serenity__)
+    defined(__sun__) || defined(__serenity__) || defined(__vxworks__)
 #include <sys/select.h>
 #endif
 
@@ -781,7 +781,7 @@ typedef enum {
   CURLPROXY_HTTP_1_0 = 1,   /* added in 7.19.4, force to use CONNECT
                                HTTP/1.0  */
   CURLPROXY_HTTPS = 2,  /* HTTPS but stick to HTTP/1 added in 7.52.0 */
-  CURLPROXY_HTTPS2 = 3, /* HTTPS and attempt HTTP/2 added in 8.1.0 */
+  CURLPROXY_HTTPS2 = 3, /* HTTPS and attempt HTTP/2 added in 8.2.0 */
   CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already
                            in 7.10 */
   CURLPROXY_SOCKS5 = 5, /* added in 7.10 */
@@ -2113,7 +2113,7 @@ typedef enum {
   CURLOPT(CURLOPT_SASL_AUTHZID, CURLOPTTYPE_STRINGPOINT, 289),
 
   /* allow RCPT TO command to fail for some recipients */
-  CURLOPT(CURLOPT_MAIL_RCPT_ALLLOWFAILS, CURLOPTTYPE_LONG, 290),
+  CURLOPT(CURLOPT_MAIL_RCPT_ALLOWFAILS, CURLOPTTYPE_LONG, 290),
 
   /* the private SSL-certificate as a "blob" */
   CURLOPT(CURLOPT_SSLCERT_BLOB, CURLOPTTYPE_BLOB, 291),
@@ -2207,6 +2207,9 @@ typedef enum {
   /* Can leak things, gonna exit() soon */
   CURLOPT(CURLOPT_QUICK_EXIT, CURLOPTTYPE_LONG, 322),
 
+  /* set a specific client IP for HAProxy PROXY protocol header? */
+  CURLOPT(CURLOPT_HAPROXY_CLIENT_IP, CURLOPTTYPE_STRINGPOINT, 323),
+
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 
@@ -2235,6 +2238,9 @@ typedef enum {
 /* */
 #define CURLOPT_FTP_RESPONSE_TIMEOUT CURLOPT_SERVER_RESPONSE_TIMEOUT
 
+/* Added in 8.2.0 */
+#define CURLOPT_MAIL_RCPT_ALLLOWFAILS CURLOPT_MAIL_RCPT_ALLOWFAILS
+
 #else
 /* This is set if CURL_NO_OLDIES is defined at compile-time */
 #undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */
@@ -2918,7 +2924,9 @@ typedef enum {
   CURLINFO_REFERER          = CURLINFO_STRING + 60,
   CURLINFO_CAINFO           = CURLINFO_STRING + 61,
   CURLINFO_CAPATH           = CURLINFO_STRING + 62,
-  CURLINFO_LASTONE          = 62
+  CURLINFO_XFER_ID          = CURLINFO_OFF_T + 63,
+  CURLINFO_CONN_ID          = CURLINFO_OFF_T + 64,
+  CURLINFO_LASTONE          = 64
 } CURLINFO;
 
 /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as

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

@@ -32,13 +32,13 @@
 
 /* This is the version number of the libcurl package from which this header
    file origins: */
-#define LIBCURL_VERSION "8.1.2"
+#define LIBCURL_VERSION "8.2.1"
 
 /* The numeric version number is also available "in parts" by using these
    defines: */
 #define LIBCURL_VERSION_MAJOR 8
-#define LIBCURL_VERSION_MINOR 1
-#define LIBCURL_VERSION_PATCH 2
+#define LIBCURL_VERSION_MINOR 2
+#define LIBCURL_VERSION_PATCH 1
 
 /* This is the numeric version of the libcurl version number, meant for easier
    parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will
@@ -59,7 +59,7 @@
    CURL_VERSION_BITS() macro since curl's own configure script greps for it
    and needs it to contain the full number.
 */
-#define LIBCURL_VERSION_NUM 0x080102
+#define LIBCURL_VERSION_NUM 0x080201
 
 /*
  * This is the date and time when the full source package was created. The

+ 21 - 26
Utilities/cmcurl/include/curl/system.h

@@ -237,33 +237,28 @@
 #  define CURL_PULL_SYS_SOCKET_H     1
 
 #elif defined(__MVS__)
-#  if defined(__IBMC__) || defined(__IBMCPP__)
-#    if defined(_ILP32)
-#    elif defined(_LP64)
-#    endif
-#    if defined(_LONG_LONG)
-#      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(_LP64)
-#      define CURL_TYPEOF_CURL_OFF_T     long
-#      define CURL_FORMAT_CURL_OFF_T     "ld"
-#      define CURL_FORMAT_CURL_OFF_TU    "lu"
-#      define CURL_SUFFIX_CURL_OFF_T     L
-#      define CURL_SUFFIX_CURL_OFF_TU    UL
-#    else
-#      define CURL_TYPEOF_CURL_OFF_T     long
-#      define CURL_FORMAT_CURL_OFF_T     "ld"
-#      define CURL_FORMAT_CURL_OFF_TU    "lu"
-#      define CURL_SUFFIX_CURL_OFF_T     L
-#      define CURL_SUFFIX_CURL_OFF_TU    UL
-#    endif
-#    define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
-#    define CURL_PULL_SYS_TYPES_H      1
-#    define CURL_PULL_SYS_SOCKET_H     1
+#  if defined(_LONG_LONG)
+#    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(_LP64)
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
+#  else
+#    define CURL_TYPEOF_CURL_OFF_T     long
+#    define CURL_FORMAT_CURL_OFF_T     "ld"
+#    define CURL_FORMAT_CURL_OFF_TU    "lu"
+#    define CURL_SUFFIX_CURL_OFF_T     L
+#    define CURL_SUFFIX_CURL_OFF_TU    UL
 #  endif
+#  define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
+#  define CURL_PULL_SYS_TYPES_H      1
+#  define CURL_PULL_SYS_SOCKET_H     1
 
 #elif defined(__370__)
 #  if defined(__IBMC__) || defined(__IBMCPP__)

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

@@ -280,6 +280,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
    (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER ||                             \
    (option) == CURLOPT_FTPPORT ||                                             \
    (option) == CURLOPT_HSTS ||                                                \
+   (option) == CURLOPT_HAPROXY_CLIENT_IP ||                                   \
    (option) == CURLOPT_INTERFACE ||                                           \
    (option) == CURLOPT_ISSUERCERT ||                                          \
    (option) == CURLOPT_KEYPASSWD ||                                           \

+ 6 - 6
Utilities/cmcurl/include/curl/websockets.h

@@ -54,13 +54,13 @@ struct curl_ws_frame {
  */
 CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen,
                                   size_t *recv,
-                                  struct curl_ws_frame **metap);
+                                  const struct curl_ws_frame **metap);
 
-/* sendflags for curl_ws_send() */
+/* flags for curl_ws_send() */
 #define CURLWS_PONG       (1<<6)
 
 /*
- * NAME curl_easy_send()
+ * NAME curl_ws_send()
  *
  * DESCRIPTION
  *
@@ -69,13 +69,13 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen,
  */
 CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer,
                                   size_t buflen, size_t *sent,
-                                  curl_off_t framesize,
-                                  unsigned int sendflags);
+                                  curl_off_t fragsize,
+                                  unsigned int flags);
 
 /* bits for the CURLOPT_WS_OPTIONS bitmask: */
 #define CURLWS_RAW_MODE (1<<0)
 
-CURL_EXTERN struct curl_ws_frame *curl_ws_meta(CURL *curl);
+CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *curl);
 
 #ifdef  __cplusplus
 }

+ 19 - 0
Utilities/cmcurl/lib/CMakeLists.txt

@@ -89,16 +89,35 @@ add_library(
   ${CMAKE_CURL_SSL_DLLS}
   )
 
+if(0) # This code not needed for building within CMake.
+add_library(
+  curlu # special libcurlu library just for unittests
+  STATIC
+  EXCLUDE_FROM_ALL
+  ${HHEADERS} ${CSOURCES}
+)
+target_compile_definitions(curlu PUBLIC UNITTESTS CURL_STATICLIB)
+endif()
+
 add_library(
   ${PROJECT_NAME}::${LIB_NAME}
   ALIAS ${LIB_NAME}
   )
 
+if(ENABLE_CURLDEBUG)
+  # We must compile memdebug.c separately to avoid memdebug.h redefinitions
+  # being applied to memdebug.c itself.
+  set_source_files_properties(memdebug.c PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
+endif()
+
 if(NOT BUILD_SHARED_LIBS)
     set_target_properties(${LIB_NAME} PROPERTIES INTERFACE_COMPILE_DEFINITIONS CURL_STATICLIB)
 endif()
 
 target_link_libraries(${LIB_NAME} PRIVATE ${CURL_LIBS})
+if(0) # This code not needed for building within CMake.
+target_link_libraries(curlu PRIVATE ${CURL_LIBS})
+endif()
 
 if(0) # This code not needed for building within CMake.
 transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")

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

@@ -72,6 +72,7 @@ LIB_VTLS_HFILES =           \
   vtls/openssl.h            \
   vtls/rustls.h             \
   vtls/schannel.h           \
+  vtls/schannel_int.h       \
   vtls/sectransp.h          \
   vtls/vtls.h               \
   vtls/vtls_int.h           \
@@ -179,6 +180,7 @@ LIB_CFILES =         \
   krb5.c             \
   ldap.c             \
   llist.c            \
+  macos.c            \
   md4.c              \
   md5.c              \
   memdebug.c         \
@@ -315,6 +317,7 @@ LIB_HFILES =         \
   inet_ntop.h        \
   inet_pton.h        \
   llist.h            \
+  macos.h            \
   memdebug.h         \
   mime.h             \
   mqtt.h             \

+ 3 - 2
Utilities/cmcurl/lib/altsvc.c

@@ -424,7 +424,7 @@ static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid,
 #ifdef DEBUGBUILD
 /* to play well with debug builds, we can *set* a fixed time this will
    return */
-static time_t debugtime(void *unused)
+static time_t altsvc_debugtime(void *unused)
 {
   char *timestr = getenv("CURL_TIME");
   (void)unused;
@@ -434,7 +434,8 @@ static time_t debugtime(void *unused)
   }
   return time(NULL);
 }
-#define time(x) debugtime(x)
+#undef time
+#define time(x) altsvc_debugtime(x)
 #endif
 
 #define ISNEWLINE(x) (((x) == '\n') || (x) == '\r')

+ 1 - 0
Utilities/cmcurl/lib/amigaos.c

@@ -178,6 +178,7 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname,
 #endif /* CURLRES_AMIGA */
 
 #ifdef USE_AMISSL
+#include <signal.h>
 int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds,
                       fd_set *errorfds, struct timeval *timeout)
 {

+ 3 - 3
Utilities/cmcurl/lib/base64.c

@@ -43,7 +43,7 @@
 
 /* ---- Base64 Encoding/Decoding Table --- */
 /* Padding character string starts at offset 64. */
-static const char base64[]=
+static const char base64encdec[]=
   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
 
 /* The Base 64 encoding with a URL and filename safe alphabet, RFC 4648
@@ -120,7 +120,7 @@ CURLcode Curl_base64_decode(const char *src,
   /* replaces
   {
     unsigned char c;
-    const unsigned char *p = (const unsigned char *)base64;
+    const unsigned char *p = (const unsigned char *)base64encdec;
     for(c = 0; *p; c++, p++)
       lookup[*p] = c;
   }
@@ -264,7 +264,7 @@ static CURLcode base64_encode(const char *table64,
 CURLcode Curl_base64_encode(const char *inputbuff, size_t insize,
                             char **outptr, size_t *outlen)
 {
-  return base64_encode(base64, inputbuff, insize, outptr, outlen);
+  return base64_encode(base64encdec, inputbuff, insize, outptr, outlen);
 }
 
 /*

+ 24 - 5
Utilities/cmcurl/lib/bufq.c

@@ -418,7 +418,8 @@ ssize_t Curl_bufq_write(struct bufq *q,
       break;
     }
     n = chunk_append(tail, buf, len);
-    DEBUGASSERT(n);
+    if(!n)
+      break;
     nwritten += n;
     buf += n;
     len -= n;
@@ -528,6 +529,14 @@ ssize_t Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer,
       }
       break;
     }
+    if(!chunk_written) {
+      if(!nwritten) {
+        /* treat as blocked */
+        *err = CURLE_AGAIN;
+        nwritten = -1;
+      }
+      break;
+    }
     Curl_bufq_skip(q, (size_t)chunk_written);
     nwritten += chunk_written;
   }
@@ -551,7 +560,8 @@ ssize_t Curl_bufq_write_pass(struct bufq *q,
           /* real error, fail */
           return -1;
         }
-        /* would block */
+        /* would block, bufq is full, give up */
+        break;
       }
     }
 
@@ -562,16 +572,25 @@ ssize_t Curl_bufq_write_pass(struct bufq *q,
         /* real error, fail */
         return -1;
       }
-      /* no room in bufq, bail out */
-      goto out;
+      /* no room in bufq */
+      break;
     }
+    /* edge case of writer returning 0 (and len is >0)
+     * break or we might enter an infinite loop here */
+    if(n == 0)
+      break;
+
     /* Maybe only part of `data` has been added, continue to loop */
     buf += (size_t)n;
     len -= (size_t)n;
     nwritten += (size_t)n;
   }
 
-out:
+  if(!nwritten && len) {
+    *err = CURLE_AGAIN;
+    return -1;
+  }
+  *err = CURLE_OK;
   return nwritten;
 }
 

+ 9 - 2
Utilities/cmcurl/lib/c-hyper.c

@@ -71,9 +71,11 @@ size_t Curl_hyper_recv(void *userp, hyper_context *ctx,
   DEBUGASSERT(conn);
   (void)ctx;
 
+  DEBUGF(infof(data, "Curl_hyper_recv(%zu)", buflen));
   result = Curl_read(data, conn->sockfd, (char *)buf, buflen, &nread);
   if(result == CURLE_AGAIN) {
     /* would block, register interest */
+    DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> EAGAIN", buflen));
     if(data->hyp.read_waker)
       hyper_waker_free(data->hyp.read_waker);
     data->hyp.read_waker = hyper_context_waker(ctx);
@@ -87,6 +89,7 @@ size_t Curl_hyper_recv(void *userp, hyper_context *ctx,
     failf(data, "Curl_read failed");
     return HYPER_IO_ERROR;
   }
+  DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> %zd", buflen, nread));
   return (size_t)nread;
 }
 
@@ -98,8 +101,12 @@ size_t Curl_hyper_send(void *userp, hyper_context *ctx,
   CURLcode result;
   ssize_t nwrote;
 
+  DEBUGF(infof(data, "Curl_hyper_send(%zu)", buflen));
   result = Curl_write(data, conn->sockfd, (void *)buf, buflen, &nwrote);
+  if(!result && !nwrote)
+    result = CURLE_AGAIN;
   if(result == CURLE_AGAIN) {
+    DEBUGF(infof(data, "Curl_hyper_send(%zu) -> EAGAIN", buflen));
     /* would block, register interest */
     if(data->hyp.write_waker)
       hyper_waker_free(data->hyp.write_waker);
@@ -114,6 +121,7 @@ size_t Curl_hyper_send(void *userp, hyper_context *ctx,
     failf(data, "Curl_write failed");
     return HYPER_IO_ERROR;
   }
+  DEBUGF(infof(data, "Curl_hyper_send(%zu) -> %zd", buflen, nwrote));
   return (size_t)nwrote;
 }
 
@@ -433,8 +441,7 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
       break;
     }
     else if(t != HYPER_TASK_RESPONSE) {
-      *didwhat = KEEP_RECV;
-      break;
+      continue;
     }
     /* HYPER_TASK_RESPONSE */
 

+ 62 - 62
Utilities/cmcurl/lib/cf-h1-proxy.c

@@ -54,16 +54,16 @@
 
 
 typedef enum {
-    TUNNEL_INIT,     /* init/default/no tunnel state */
-    TUNNEL_CONNECT,  /* CONNECT request is being send */
-    TUNNEL_RECEIVE,  /* CONNECT answer is being received */
-    TUNNEL_RESPONSE, /* CONNECT response received completely */
-    TUNNEL_ESTABLISHED,
-    TUNNEL_FAILED
-} tunnel_state;
+    H1_TUNNEL_INIT,     /* init/default/no tunnel state */
+    H1_TUNNEL_CONNECT,  /* CONNECT request is being send */
+    H1_TUNNEL_RECEIVE,  /* CONNECT answer is being received */
+    H1_TUNNEL_RESPONSE, /* CONNECT response received completely */
+    H1_TUNNEL_ESTABLISHED,
+    H1_TUNNEL_FAILED
+} h1_tunnel_state;
 
 /* struct for HTTP CONNECT tunneling */
-struct tunnel_state {
+struct h1_tunnel_state {
   int sockindex;
   const char *hostname;
   int remote_port;
@@ -78,23 +78,23 @@ struct tunnel_state {
     KEEPON_IGNORE
   } keepon;
   curl_off_t cl; /* size of content to read and ignore */
-  tunnel_state tunnel_state;
+  h1_tunnel_state tunnel_state;
   BIT(chunked_encoding);
   BIT(close_connection);
 };
 
 
-static bool tunnel_is_established(struct tunnel_state *ts)
+static bool tunnel_is_established(struct h1_tunnel_state *ts)
 {
-  return ts && (ts->tunnel_state == TUNNEL_ESTABLISHED);
+  return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED);
 }
 
-static bool tunnel_is_failed(struct tunnel_state *ts)
+static bool tunnel_is_failed(struct h1_tunnel_state *ts)
 {
-  return ts && (ts->tunnel_state == TUNNEL_FAILED);
+  return ts && (ts->tunnel_state == H1_TUNNEL_FAILED);
 }
 
-static CURLcode tunnel_reinit(struct tunnel_state *ts,
+static CURLcode tunnel_reinit(struct h1_tunnel_state *ts,
                               struct connectdata *conn,
                               struct Curl_easy *data)
 {
@@ -102,7 +102,7 @@ static CURLcode tunnel_reinit(struct tunnel_state *ts,
   DEBUGASSERT(ts);
   Curl_dyn_reset(&ts->rcvbuf);
   Curl_dyn_reset(&ts->req);
-  ts->tunnel_state = TUNNEL_INIT;
+  ts->tunnel_state = H1_TUNNEL_INIT;
   ts->keepon = KEEPON_CONNECT;
   ts->cl = 0;
   ts->close_connection = FALSE;
@@ -124,12 +124,12 @@ static CURLcode tunnel_reinit(struct tunnel_state *ts,
   return CURLE_OK;
 }
 
-static CURLcode tunnel_init(struct tunnel_state **pts,
+static CURLcode tunnel_init(struct h1_tunnel_state **pts,
                             struct Curl_easy *data,
                             struct connectdata *conn,
                             int sockindex)
 {
-  struct tunnel_state *ts;
+  struct h1_tunnel_state *ts;
   CURLcode result;
 
   if(conn->handler->flags & PROTOPT_NOTCPPROXY) {
@@ -157,16 +157,16 @@ static CURLcode tunnel_init(struct tunnel_state **pts,
   return tunnel_reinit(ts, conn, data);
 }
 
-static void tunnel_go_state(struct Curl_cfilter *cf,
-                            struct tunnel_state *ts,
-                            tunnel_state new_state,
-                            struct Curl_easy *data)
+static void h1_tunnel_go_state(struct Curl_cfilter *cf,
+                               struct h1_tunnel_state *ts,
+                               h1_tunnel_state new_state,
+                               struct Curl_easy *data)
 {
   if(ts->tunnel_state == new_state)
     return;
   /* leaving this one */
   switch(ts->tunnel_state) {
-  case TUNNEL_CONNECT:
+  case H1_TUNNEL_CONNECT:
     data->req.ignorebody = FALSE;
     break;
   default:
@@ -174,36 +174,36 @@ static void tunnel_go_state(struct Curl_cfilter *cf,
   }
   /* entering this one */
   switch(new_state) {
-  case TUNNEL_INIT:
+  case H1_TUNNEL_INIT:
     DEBUGF(LOG_CF(data, cf, "new tunnel state 'init'"));
     tunnel_reinit(ts, cf->conn, data);
     break;
 
-  case TUNNEL_CONNECT:
+  case H1_TUNNEL_CONNECT:
     DEBUGF(LOG_CF(data, cf, "new tunnel state 'connect'"));
-    ts->tunnel_state = TUNNEL_CONNECT;
+    ts->tunnel_state = H1_TUNNEL_CONNECT;
     ts->keepon = KEEPON_CONNECT;
     Curl_dyn_reset(&ts->rcvbuf);
     break;
 
-  case TUNNEL_RECEIVE:
+  case H1_TUNNEL_RECEIVE:
     DEBUGF(LOG_CF(data, cf, "new tunnel state 'receive'"));
-    ts->tunnel_state = TUNNEL_RECEIVE;
+    ts->tunnel_state = H1_TUNNEL_RECEIVE;
     break;
 
-  case TUNNEL_RESPONSE:
+  case H1_TUNNEL_RESPONSE:
     DEBUGF(LOG_CF(data, cf, "new tunnel state 'response'"));
-    ts->tunnel_state = TUNNEL_RESPONSE;
+    ts->tunnel_state = H1_TUNNEL_RESPONSE;
     break;
 
-  case TUNNEL_ESTABLISHED:
+  case H1_TUNNEL_ESTABLISHED:
     DEBUGF(LOG_CF(data, cf, "new tunnel state 'established'"));
     infof(data, "CONNECT phase completed");
     data->state.authproxy.done = TRUE;
     data->state.authproxy.multipass = FALSE;
     /* FALLTHROUGH */
-  case TUNNEL_FAILED:
-    if(new_state == TUNNEL_FAILED)
+  case H1_TUNNEL_FAILED:
+    if(new_state == H1_TUNNEL_FAILED)
       DEBUGF(LOG_CF(data, cf, "new tunnel state 'failed'"));
     ts->tunnel_state = new_state;
     Curl_dyn_reset(&ts->rcvbuf);
@@ -225,9 +225,9 @@ static void tunnel_go_state(struct Curl_cfilter *cf,
 static void tunnel_free(struct Curl_cfilter *cf,
                         struct Curl_easy *data)
 {
-  struct tunnel_state *ts = cf->ctx;
+  struct h1_tunnel_state *ts = cf->ctx;
   if(ts) {
-    tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
+    h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
     Curl_dyn_free(&ts->rcvbuf);
     Curl_dyn_free(&ts->req);
     free(ts);
@@ -270,7 +270,7 @@ static CURLcode CONNECT_host(struct Curl_easy *data,
 #ifndef USE_HYPER
 static CURLcode start_CONNECT(struct Curl_cfilter *cf,
                               struct Curl_easy *data,
-                              struct tunnel_state *ts)
+                              struct h1_tunnel_state *ts)
 {
   struct connectdata *conn = cf->conn;
   char *hostheader = NULL;
@@ -351,7 +351,7 @@ out:
 
 static CURLcode send_CONNECT(struct Curl_easy *data,
                              struct connectdata *conn,
-                             struct tunnel_state *ts,
+                             struct h1_tunnel_state *ts,
                              bool *done)
 {
   struct SingleRequest *k = &data->req;
@@ -399,7 +399,7 @@ out:
 
 static CURLcode on_resp_header(struct Curl_cfilter *cf,
                                struct Curl_easy *data,
-                               struct tunnel_state *ts,
+                               struct h1_tunnel_state *ts,
                                const char *header)
 {
   CURLcode result = CURLE_OK;
@@ -475,7 +475,7 @@ static CURLcode on_resp_header(struct Curl_cfilter *cf,
 
 static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
-                                  struct tunnel_state *ts,
+                                  struct h1_tunnel_state *ts,
                                   bool *done)
 {
   CURLcode result = CURLE_OK;
@@ -671,7 +671,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
 /* The Hyper version of CONNECT */
 static CURLcode start_CONNECT(struct Curl_cfilter *cf,
                               struct Curl_easy *data,
-                              struct tunnel_state *ts)
+                              struct h1_tunnel_state *ts)
 {
   struct connectdata *conn = cf->conn;
   struct hyptransfer *h = &data->hyp;
@@ -882,7 +882,7 @@ error:
 
 static CURLcode send_CONNECT(struct Curl_easy *data,
                              struct connectdata *conn,
-                             struct tunnel_state *ts,
+                             struct h1_tunnel_state *ts,
                              bool *done)
 {
   struct hyptransfer *h = &data->hyp;
@@ -919,7 +919,7 @@ error:
 
 static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
-                                  struct tunnel_state *ts,
+                                  struct h1_tunnel_state *ts,
                                   bool *done)
 {
   struct hyptransfer *h = &data->hyp;
@@ -949,9 +949,9 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
 
 #endif /* USE_HYPER */
 
-static CURLcode CONNECT(struct Curl_cfilter *cf,
-                        struct Curl_easy *data,
-                        struct tunnel_state *ts)
+static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
+                           struct Curl_easy *data,
+                           struct h1_tunnel_state *ts)
 {
   struct connectdata *conn = cf->conn;
   CURLcode result;
@@ -973,25 +973,25 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
     }
 
     switch(ts->tunnel_state) {
-    case TUNNEL_INIT:
+    case H1_TUNNEL_INIT:
       /* Prepare the CONNECT request and make a first attempt to send. */
       DEBUGF(LOG_CF(data, cf, "CONNECT start"));
       result = start_CONNECT(cf, data, ts);
       if(result)
         goto out;
-      tunnel_go_state(cf, ts, TUNNEL_CONNECT, data);
+      h1_tunnel_go_state(cf, ts, H1_TUNNEL_CONNECT, data);
       /* FALLTHROUGH */
 
-    case TUNNEL_CONNECT:
+    case H1_TUNNEL_CONNECT:
       /* see that the request is completely sent */
       DEBUGF(LOG_CF(data, cf, "CONNECT send"));
       result = send_CONNECT(data, cf->conn, ts, &done);
       if(result || !done)
         goto out;
-      tunnel_go_state(cf, ts, TUNNEL_RECEIVE, data);
+      h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data);
       /* FALLTHROUGH */
 
-    case TUNNEL_RECEIVE:
+    case H1_TUNNEL_RECEIVE:
       /* read what is there */
       DEBUGF(LOG_CF(data, cf, "CONNECT receive"));
       result = recv_CONNECT_resp(cf, data, ts, &done);
@@ -1003,10 +1003,10 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
       if(result || !done)
         goto out;
       /* got it */
-      tunnel_go_state(cf, ts, TUNNEL_RESPONSE, data);
+      h1_tunnel_go_state(cf, ts, H1_TUNNEL_RESPONSE, data);
       /* FALLTHROUGH */
 
-    case TUNNEL_RESPONSE:
+    case H1_TUNNEL_RESPONSE:
       DEBUGF(LOG_CF(data, cf, "CONNECT response"));
       if(data->req.newurl) {
         /* not the "final" response, we need to do a follow up request.
@@ -1028,7 +1028,7 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
         }
         else {
           /* staying on this connection, reset state */
-          tunnel_go_state(cf, ts, TUNNEL_INIT, data);
+          h1_tunnel_go_state(cf, ts, H1_TUNNEL_INIT, data);
         }
       }
       break;
@@ -1039,25 +1039,25 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
 
   } while(data->req.newurl);
 
-  DEBUGASSERT(ts->tunnel_state == TUNNEL_RESPONSE);
+  DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE);
   if(data->info.httpproxycode/100 != 2) {
     /* a non-2xx response and we have no next url to try. */
     Curl_safefree(data->req.newurl);
     /* failure, close this connection to avoid re-use */
     streamclose(conn, "proxy CONNECT failure");
-    tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
+    h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
     failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode);
     return CURLE_RECV_ERROR;
   }
   /* 2xx response, SUCCESS! */
-  tunnel_go_state(cf, ts, TUNNEL_ESTABLISHED, data);
+  h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data);
   infof(data, "CONNECT tunnel established, response %d",
         data->info.httpproxycode);
   result = CURLE_OK;
 
 out:
   if(result)
-    tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
+    h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
   return result;
 }
 
@@ -1066,7 +1066,7 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
                                     bool blocking, bool *done)
 {
   CURLcode result;
-  struct tunnel_state *ts = cf->ctx;
+  struct h1_tunnel_state *ts = cf->ctx;
 
   if(cf->connected) {
     *done = TRUE;
@@ -1074,7 +1074,7 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
   }
 
   DEBUGF(LOG_CF(data, cf, "connect"));
-  result = cf->next->cft->connect(cf->next, data, blocking, done);
+  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
   if(result || !*done)
     return result;
 
@@ -1089,7 +1089,7 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
   /* TODO: can we do blocking? */
   /* We want "seamless" operations through HTTP proxy tunnel */
 
-  result = CONNECT(cf, data, ts);
+  result = H1_CONNECT(cf, data, ts);
   if(result)
     goto out;
   Curl_safefree(data->state.aptr.proxyuserpwd);
@@ -1107,7 +1107,7 @@ static int cf_h1_proxy_get_select_socks(struct Curl_cfilter *cf,
                                         struct Curl_easy *data,
                                         curl_socket_t *socks)
 {
-  struct tunnel_state *ts = cf->ctx;
+  struct h1_tunnel_state *ts = cf->ctx;
   int fds;
 
   fds = cf->next->cft->get_select_socks(cf->next, data, socks);
@@ -1143,10 +1143,10 @@ static void cf_h1_proxy_close(struct Curl_cfilter *cf,
   DEBUGF(LOG_CF(data, cf, "close"));
   cf->connected = FALSE;
   if(cf->ctx) {
-    tunnel_go_state(cf, cf->ctx, TUNNEL_INIT, data);
+    h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data);
   }
   if(cf->next)
-    cf->next->cft->close(cf->next, data);
+    cf->next->cft->do_close(cf->next, data);
 }
 
 

+ 329 - 202
Utilities/cmcurl/lib/cf-h2-proxy.c

@@ -44,26 +44,25 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#define H2_NW_CHUNK_SIZE  (128*1024)
-#define H2_NW_RECV_CHUNKS   1
-#define H2_NW_SEND_CHUNKS   1
+#define H2_CHUNK_SIZE  (16*1024)
 
-#define HTTP2_HUGE_WINDOW_SIZE (32 * 1024 * 1024) /* 32 MB */
+#define PROXY_HTTP2_HUGE_WINDOW_SIZE (100 * 1024 * 1024)
+#define H2_TUNNEL_WINDOW_SIZE        (10 * 1024 * 1024)
+
+#define PROXY_H2_NW_RECV_CHUNKS  (H2_TUNNEL_WINDOW_SIZE / H2_CHUNK_SIZE)
+#define PROXY_H2_NW_SEND_CHUNKS   1
+
+#define H2_TUNNEL_RECV_CHUNKS   (H2_TUNNEL_WINDOW_SIZE / H2_CHUNK_SIZE)
+#define H2_TUNNEL_SEND_CHUNKS   ((128 * 1024) / H2_CHUNK_SIZE)
 
-#define H2_TUNNEL_WINDOW_SIZE (1024 * 1024)
-#define H2_TUNNEL_CHUNK_SIZE   (32 * 1024)
-#define H2_TUNNEL_RECV_CHUNKS \
-          (H2_TUNNEL_WINDOW_SIZE / H2_TUNNEL_CHUNK_SIZE)
-#define H2_TUNNEL_SEND_CHUNKS \
-          (H2_TUNNEL_WINDOW_SIZE / H2_TUNNEL_CHUNK_SIZE)
 
 typedef enum {
-    TUNNEL_INIT,     /* init/default/no tunnel state */
-    TUNNEL_CONNECT,  /* CONNECT request is being send */
-    TUNNEL_RESPONSE, /* CONNECT response received completely */
-    TUNNEL_ESTABLISHED,
-    TUNNEL_FAILED
-} tunnel_state;
+    H2_TUNNEL_INIT,     /* init/default/no tunnel state */
+    H2_TUNNEL_CONNECT,  /* CONNECT request is being send */
+    H2_TUNNEL_RESPONSE, /* CONNECT response received completely */
+    H2_TUNNEL_ESTABLISHED,
+    H2_TUNNEL_FAILED
+} h2_tunnel_state;
 
 struct tunnel_stream {
   struct http_resp *resp;
@@ -72,10 +71,11 @@ struct tunnel_stream {
   char *authority;
   int32_t stream_id;
   uint32_t error;
-  tunnel_state state;
-  bool has_final_response;
-  bool closed;
-  bool reset;
+  size_t upload_blocked_len;
+  h2_tunnel_state state;
+  BIT(has_final_response);
+  BIT(closed);
+  BIT(reset);
 };
 
 static CURLcode tunnel_stream_init(struct Curl_cfilter *cf,
@@ -85,11 +85,11 @@ static CURLcode tunnel_stream_init(struct Curl_cfilter *cf,
   int port;
   bool ipv6_ip = cf->conn->bits.ipv6_ip;
 
-  ts->state = TUNNEL_INIT;
+  ts->state = H2_TUNNEL_INIT;
   ts->stream_id = -1;
-  Curl_bufq_init2(&ts->recvbuf, H2_TUNNEL_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS,
+  Curl_bufq_init2(&ts->recvbuf, H2_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS,
                   BUFQ_OPT_SOFT_LIMIT);
-  Curl_bufq_init(&ts->sendbuf, H2_TUNNEL_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS);
+  Curl_bufq_init(&ts->sendbuf, H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS);
 
   if(cf->conn->bits.conn_to_host)
     hostname = cf->conn->conn_to_host.name;
@@ -123,13 +123,13 @@ static void tunnel_stream_clear(struct tunnel_stream *ts)
   Curl_bufq_free(&ts->sendbuf);
   Curl_safefree(ts->authority);
   memset(ts, 0, sizeof(*ts));
-  ts->state = TUNNEL_INIT;
+  ts->state = H2_TUNNEL_INIT;
 }
 
-static void tunnel_go_state(struct Curl_cfilter *cf,
-                            struct tunnel_stream *ts,
-                            tunnel_state new_state,
-                            struct Curl_easy *data)
+static void h2_tunnel_go_state(struct Curl_cfilter *cf,
+                               struct tunnel_stream *ts,
+                               h2_tunnel_state new_state,
+                               struct Curl_easy *data)
 {
   (void)cf;
 
@@ -137,7 +137,7 @@ static void tunnel_go_state(struct Curl_cfilter *cf,
     return;
   /* leaving this one */
   switch(ts->state) {
-  case TUNNEL_CONNECT:
+  case H2_TUNNEL_CONNECT:
     data->req.ignorebody = FALSE;
     break;
   default:
@@ -145,29 +145,29 @@ static void tunnel_go_state(struct Curl_cfilter *cf,
   }
   /* entering this one */
   switch(new_state) {
-  case TUNNEL_INIT:
+  case H2_TUNNEL_INIT:
     DEBUGF(LOG_CF(data, cf, "new tunnel state 'init'"));
     tunnel_stream_clear(ts);
     break;
 
-  case TUNNEL_CONNECT:
+  case H2_TUNNEL_CONNECT:
     DEBUGF(LOG_CF(data, cf, "new tunnel state 'connect'"));
-    ts->state = TUNNEL_CONNECT;
+    ts->state = H2_TUNNEL_CONNECT;
     break;
 
-  case TUNNEL_RESPONSE:
+  case H2_TUNNEL_RESPONSE:
     DEBUGF(LOG_CF(data, cf, "new tunnel state 'response'"));
-    ts->state = TUNNEL_RESPONSE;
+    ts->state = H2_TUNNEL_RESPONSE;
     break;
 
-  case TUNNEL_ESTABLISHED:
+  case H2_TUNNEL_ESTABLISHED:
     DEBUGF(LOG_CF(data, cf, "new tunnel state 'established'"));
     infof(data, "CONNECT phase completed");
     data->state.authproxy.done = TRUE;
     data->state.authproxy.multipass = FALSE;
     /* FALLTHROUGH */
-  case TUNNEL_FAILED:
-    if(new_state == TUNNEL_FAILED)
+  case H2_TUNNEL_FAILED:
+    if(new_state == H2_TUNNEL_FAILED)
       DEBUGF(LOG_CF(data, cf, "new tunnel state 'failed'"));
     ts->state = new_state;
     /* If a proxy-authorization header was used for the proxy, then we should
@@ -191,9 +191,11 @@ struct cf_h2_proxy_ctx {
   int32_t last_stream_id;
   BIT(conn_closed);
   BIT(goaway);
+  BIT(nw_out_blocked);
 };
 
 /* How to access `call_data` from a cf_h2 filter */
+#undef CF_CTX_CALL_DATA
 #define CF_CTX_CALL_DATA(cf)  \
   ((struct cf_h2_proxy_ctx *)(cf)->ctx)->call_data
 
@@ -219,35 +221,54 @@ static void cf_h2_proxy_ctx_free(struct cf_h2_proxy_ctx *ctx)
   }
 }
 
-static ssize_t nw_in_reader(void *reader_ctx,
-                              unsigned char *buf, size_t buflen,
-                              CURLcode *err)
+static void drain_tunnel(struct Curl_cfilter *cf,
+                         struct Curl_easy *data,
+                         struct tunnel_stream *tunnel)
+{
+  unsigned char bits;
+
+  (void)cf;
+  bits = CURL_CSELECT_IN;
+  if(!tunnel->closed && !tunnel->reset && tunnel->upload_blocked_len)
+    bits |= CURL_CSELECT_OUT;
+  if(data->state.dselect_bits != bits) {
+    DEBUGF(LOG_CF(data, cf, "[h2sid=%d] DRAIN dselect_bits=%x",
+                  tunnel->stream_id, bits));
+    data->state.dselect_bits = bits;
+    Curl_expire(data, 0, EXPIRE_RUN_NOW);
+  }
+}
+
+static ssize_t proxy_nw_in_reader(void *reader_ctx,
+                                  unsigned char *buf, size_t buflen,
+                                  CURLcode *err)
 {
   struct Curl_cfilter *cf = reader_ctx;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nread;
 
   nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err);
-  DEBUGF(LOG_CF(data, cf, "nw_in recv(len=%zu) -> %zd, %d",
+  DEBUGF(LOG_CF(data, cf, "nw_in_reader(len=%zu) -> %zd, %d",
          buflen, nread, *err));
   return nread;
 }
 
-static ssize_t nw_out_writer(void *writer_ctx,
-                             const unsigned char *buf, size_t buflen,
-                             CURLcode *err)
+static ssize_t proxy_h2_nw_out_writer(void *writer_ctx,
+                                      const unsigned char *buf, size_t buflen,
+                                      CURLcode *err)
 {
   struct Curl_cfilter *cf = writer_ctx;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nwritten;
 
   nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, err);
-  DEBUGF(LOG_CF(data, cf, "nw_out send(len=%zu) -> %zd", buflen, nwritten));
+  DEBUGF(LOG_CF(data, cf, "nw_out_writer(len=%zu) -> %zd, %d",
+                buflen, nwritten, *err));
   return nwritten;
 }
 
-static int h2_client_new(struct Curl_cfilter *cf,
-                         nghttp2_session_callbacks *cbs)
+static int proxy_h2_client_new(struct Curl_cfilter *cf,
+                               nghttp2_session_callbacks *cbs)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   nghttp2_option *o;
@@ -271,15 +292,18 @@ static int h2_client_new(struct Curl_cfilter *cf,
 static ssize_t on_session_send(nghttp2_session *h2,
                               const uint8_t *buf, size_t blen,
                               int flags, void *userp);
-static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
-                         void *userp);
-static int on_stream_close(nghttp2_session *session, int32_t stream_id,
-                           uint32_t error_code, void *userp);
-static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
-                     const uint8_t *name, size_t namelen,
-                     const uint8_t *value, size_t valuelen,
-                     uint8_t flags,
-                     void *userp);
+static int proxy_h2_on_frame_recv(nghttp2_session *session,
+                                  const nghttp2_frame *frame,
+                                  void *userp);
+static int proxy_h2_on_stream_close(nghttp2_session *session,
+                                    int32_t stream_id,
+                                    uint32_t error_code, void *userp);
+static int proxy_h2_on_header(nghttp2_session *session,
+                              const nghttp2_frame *frame,
+                              const uint8_t *name, size_t namelen,
+                              const uint8_t *value, size_t valuelen,
+                              uint8_t flags,
+                              void *userp);
 static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags,
                                 int32_t stream_id,
                                 const uint8_t *mem, size_t len, void *userp);
@@ -298,8 +322,8 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf,
   DEBUGASSERT(!ctx->h2);
   memset(&ctx->tunnel, 0, sizeof(ctx->tunnel));
 
-  Curl_bufq_init(&ctx->inbufq, H2_NW_CHUNK_SIZE, H2_NW_RECV_CHUNKS);
-  Curl_bufq_init(&ctx->outbufq, H2_NW_CHUNK_SIZE, H2_NW_SEND_CHUNKS);
+  Curl_bufq_init(&ctx->inbufq, H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS);
+  Curl_bufq_init(&ctx->outbufq, H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS);
 
   if(tunnel_stream_init(cf, &ctx->tunnel))
     goto out;
@@ -311,14 +335,16 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf,
   }
 
   nghttp2_session_callbacks_set_send_callback(cbs, on_session_send);
-  nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv);
+  nghttp2_session_callbacks_set_on_frame_recv_callback(
+    cbs, proxy_h2_on_frame_recv);
   nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
     cbs, tunnel_recv_callback);
-  nghttp2_session_callbacks_set_on_stream_close_callback(cbs, on_stream_close);
-  nghttp2_session_callbacks_set_on_header_callback(cbs, on_header);
+  nghttp2_session_callbacks_set_on_stream_close_callback(
+    cbs, proxy_h2_on_stream_close);
+  nghttp2_session_callbacks_set_on_header_callback(cbs, proxy_h2_on_header);
 
   /* The nghttp2 session is not yet setup, do it */
-  rc = h2_client_new(cf, cbs);
+  rc = proxy_h2_client_new(cf, cbs);
   if(rc) {
     failf(data, "Couldn't initialize nghttp2");
     goto out;
@@ -343,7 +369,7 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf,
   }
 
   rc = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE, 0,
-                                             HTTP2_HUGE_WINDOW_SIZE);
+                                             PROXY_HTTP2_HUGE_WINDOW_SIZE);
   if(rc) {
     failf(data, "nghttp2_session_set_local_window_size() failed: %s(%d)",
           nghttp2_strerror(rc), rc);
@@ -362,27 +388,35 @@ out:
   return result;
 }
 
-static CURLcode nw_out_flush(struct Curl_cfilter *cf,
-                             struct Curl_easy *data)
+static int should_close_session(struct cf_h2_proxy_ctx *ctx)
+{
+  return !nghttp2_session_want_read(ctx->h2) &&
+    !nghttp2_session_want_write(ctx->h2);
+}
+
+static CURLcode proxy_h2_nw_out_flush(struct Curl_cfilter *cf,
+                                      struct Curl_easy *data)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
-  size_t buflen = Curl_bufq_len(&ctx->outbufq);
   ssize_t nwritten;
   CURLcode result;
 
   (void)data;
-  if(!buflen)
+  if(Curl_bufq_is_empty(&ctx->outbufq))
     return CURLE_OK;
 
-  DEBUGF(LOG_CF(data, cf, "h2 conn flush %zu bytes", buflen));
-  nwritten = Curl_bufq_pass(&ctx->outbufq, nw_out_writer, cf, &result);
+  nwritten = Curl_bufq_pass(&ctx->outbufq, proxy_h2_nw_out_writer, cf,
+                            &result);
   if(nwritten < 0) {
+    if(result == CURLE_AGAIN) {
+      DEBUGF(LOG_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN",
+                    Curl_bufq_len(&ctx->outbufq)));
+      ctx->nw_out_blocked = 1;
+    }
     return result;
   }
-  if((size_t)nwritten < buflen) {
-    return CURLE_AGAIN;
-  }
-  return CURLE_OK;
+  DEBUGF(LOG_CF(data, cf, "nw send buffer flushed"));
+  return Curl_bufq_is_empty(&ctx->outbufq)? CURLE_OK: CURLE_AGAIN;
 }
 
 /*
@@ -390,9 +424,9 @@ static CURLcode nw_out_flush(struct Curl_cfilter *cf,
  * This function returns 0 if it succeeds, or -1 and error code will
  * be assigned to *err.
  */
-static int h2_process_pending_input(struct Curl_cfilter *cf,
-                                    struct Curl_easy *data,
-                                    CURLcode *err)
+static int proxy_h2_process_pending_input(struct Curl_cfilter *cf,
+                                          struct Curl_easy *data,
+                                          CURLcode *err)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   const unsigned char *buf;
@@ -422,19 +456,11 @@ static int h2_process_pending_input(struct Curl_cfilter *cf,
     }
   }
 
-  if(nghttp2_session_check_request_allowed(ctx->h2) == 0) {
-    /* No more requests are allowed in the current session, so
-       the connection may not be reused. This is set when a
-       GOAWAY frame has been received or when the limit of stream
-       identifiers has been reached. */
-    connclose(cf->conn, "http/2: No new requests allowed");
-  }
-
   return 0;
 }
 
-static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
-                                    struct Curl_easy *data)
+static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf,
+                                          struct Curl_easy *data)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
@@ -442,9 +468,9 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
 
   /* Process network input buffer fist */
   if(!Curl_bufq_is_empty(&ctx->inbufq)) {
-    DEBUGF(LOG_CF(data, cf, "Process %zd bytes in connection buffer",
+    DEBUGF(LOG_CF(data, cf, "Process %zu bytes in connection buffer",
                   Curl_bufq_len(&ctx->inbufq)));
-    if(h2_process_pending_input(cf, data, &result) < 0)
+    if(proxy_h2_process_pending_input(cf, data, &result) < 0)
       return result;
   }
 
@@ -455,8 +481,8 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
         Curl_bufq_is_empty(&ctx->inbufq) && /* and we consumed our input */
         !Curl_bufq_is_full(&ctx->tunnel.recvbuf)) {
 
-    nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result);
-    DEBUGF(LOG_CF(data, cf, "read %zd bytes nw data -> %zd, %d",
+    nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result);
+    DEBUGF(LOG_CF(data, cf, "read %zu bytes nw data -> %zd, %d",
                   Curl_bufq_len(&ctx->inbufq), nread, result));
     if(nread < 0) {
       if(result != CURLE_AGAIN) {
@@ -470,7 +496,7 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
       break;
     }
 
-    if(h2_process_pending_input(cf, data, &result))
+    if(proxy_h2_process_pending_input(cf, data, &result))
       return result;
   }
 
@@ -481,25 +507,22 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
   return CURLE_OK;
 }
 
-/*
- * Check if there's been an update in the priority /
- * dependency settings and if so it submits a PRIORITY frame with the updated
- * info.
- * Flush any out data pending in the network buffer.
- */
-static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
-                                  struct Curl_easy *data)
+static CURLcode proxy_h2_progress_egress(struct Curl_cfilter *cf,
+                                         struct Curl_easy *data)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   int rv = 0;
 
-  rv = nghttp2_session_send(ctx->h2);
+  ctx->nw_out_blocked = 0;
+  while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2))
+    rv = nghttp2_session_send(ctx->h2);
+
   if(nghttp2_is_fatal(rv)) {
     DEBUGF(LOG_CF(data, cf, "nghttp2_session_send error (%s)%d",
                   nghttp2_strerror(rv), rv));
     return CURLE_SEND_ERROR;
   }
-  return nw_out_flush(cf, data);
+  return proxy_h2_nw_out_flush(cf, data);
 }
 
 static ssize_t on_session_send(nghttp2_session *h2,
@@ -517,7 +540,7 @@ static ssize_t on_session_send(nghttp2_session *h2,
   DEBUGASSERT(data);
 
   nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen,
-                                  nw_out_writer, cf, &result);
+                                  proxy_h2_nw_out_writer, cf, &result);
   if(nwritten < 0) {
     if(result == CURLE_AGAIN) {
       return NGHTTP2_ERR_WOULDBLOCK;
@@ -532,8 +555,9 @@ static ssize_t on_session_send(nghttp2_session *h2,
   return nwritten;
 }
 
-static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
-                         void *userp)
+static int proxy_h2_on_frame_recv(nghttp2_session *session,
+                                  const nghttp2_frame *frame,
+                                  void *userp)
 {
   struct Curl_cfilter *cf = userp;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
@@ -616,11 +640,12 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
   return 0;
 }
 
-static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
-                     const uint8_t *name, size_t namelen,
-                     const uint8_t *value, size_t valuelen,
-                     uint8_t flags,
-                     void *userp)
+static int proxy_h2_on_header(nghttp2_session *session,
+                              const nghttp2_frame *frame,
+                              const uint8_t *name, size_t namelen,
+                              const uint8_t *value, size_t valuelen,
+                              uint8_t flags,
+                              void *userp)
 {
   struct Curl_cfilter *cf = userp;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
@@ -752,8 +777,9 @@ static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags,
   return 0;
 }
 
-static int on_stream_close(nghttp2_session *session, int32_t stream_id,
-                           uint32_t error_code, void *userp)
+static int proxy_h2_on_stream_close(nghttp2_session *session,
+                                    int32_t stream_id,
+                                    uint32_t error_code, void *userp)
 {
   struct Curl_cfilter *cf = userp;
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
@@ -765,7 +791,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
   if(stream_id != ctx->tunnel.stream_id)
     return 0;
 
-  DEBUGF(LOG_CF(data, cf, "[h2sid=%u] on_stream_close, %s (err %d)",
+  DEBUGF(LOG_CF(data, cf, "[h2sid=%u] proxy_h2_on_stream_close, %s (err %d)",
                 stream_id, nghttp2_http2_strerror(error_code), error_code));
   ctx->tunnel.closed = TRUE;
   ctx->tunnel.error = error_code;
@@ -773,15 +799,15 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
   return 0;
 }
 
-static CURLcode h2_submit(int32_t *pstream_id,
-                          struct Curl_cfilter *cf,
-                          struct Curl_easy *data,
-                          nghttp2_session *h2,
-                          struct httpreq *req,
-                          const nghttp2_priority_spec *pri_spec,
-                          void *stream_user_data,
-                          nghttp2_data_source_read_callback read_callback,
-                          void *read_ctx)
+static CURLcode proxy_h2_submit(int32_t *pstream_id,
+                                struct Curl_cfilter *cf,
+                                struct Curl_easy *data,
+                                nghttp2_session *h2,
+                                struct httpreq *req,
+                                const nghttp2_priority_spec *pri_spec,
+                                void *stream_user_data,
+                               nghttp2_data_source_read_callback read_callback,
+                                void *read_ctx)
 {
   struct dynhds h2_headers;
   nghttp2_nv *nva = NULL;
@@ -881,8 +907,8 @@ static CURLcode submit_CONNECT(struct Curl_cfilter *cf,
   if(result)
     goto out;
 
-  result = h2_submit(&ts->stream_id, cf, data, ctx->h2, req,
-                     NULL, ts, tunnel_send_callback, cf);
+  result = proxy_h2_submit(&ts->stream_id, cf, data, ctx->h2, req,
+                           NULL, ts, tunnel_send_callback, cf);
   if(result) {
     DEBUGF(LOG_CF(data, cf, "send: nghttp2_submit_request error (%s)%u",
                   nghttp2_strerror(ts->stream_id), ts->stream_id));
@@ -907,7 +933,7 @@ static CURLcode inspect_response(struct Curl_cfilter *cf,
   DEBUGASSERT(ts->resp);
   if(ts->resp->status/100 == 2) {
     infof(data, "CONNECT tunnel established, response %d", ts->resp->status);
-    tunnel_go_state(cf, ts, TUNNEL_ESTABLISHED, data);
+    h2_tunnel_go_state(cf, ts, H2_TUNNEL_ESTABLISHED, data);
     return CURLE_OK;
   }
 
@@ -928,7 +954,7 @@ static CURLcode inspect_response(struct Curl_cfilter *cf,
     if(data->req.newurl) {
       /* Inidicator that we should try again */
       Curl_safefree(data->req.newurl);
-      tunnel_go_state(cf, ts, TUNNEL_INIT, data);
+      h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data);
       return CURLE_OK;
     }
   }
@@ -937,9 +963,9 @@ static CURLcode inspect_response(struct Curl_cfilter *cf,
   return CURLE_RECV_ERROR;
 }
 
-static CURLcode CONNECT(struct Curl_cfilter *cf,
-                        struct Curl_easy *data,
-                        struct tunnel_stream *ts)
+static CURLcode H2_CONNECT(struct Curl_cfilter *cf,
+                           struct Curl_easy *data,
+                           struct tunnel_stream *ts)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   CURLcode result = CURLE_OK;
@@ -948,27 +974,27 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
   DEBUGASSERT(ts->authority);
   do {
     switch(ts->state) {
-    case TUNNEL_INIT:
+    case H2_TUNNEL_INIT:
       /* Prepare the CONNECT request and make a first attempt to send. */
       DEBUGF(LOG_CF(data, cf, "CONNECT start for %s", ts->authority));
       result = submit_CONNECT(cf, data, ts);
       if(result)
         goto out;
-      tunnel_go_state(cf, ts, TUNNEL_CONNECT, data);
+      h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data);
       /* FALLTHROUGH */
 
-    case TUNNEL_CONNECT:
+    case H2_TUNNEL_CONNECT:
       /* see that the request is completely sent */
-      result = h2_progress_ingress(cf, data);
+      result = proxy_h2_progress_ingress(cf, data);
       if(!result)
-        result = h2_progress_egress(cf, data);
-      if(result) {
-        tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
+        result = proxy_h2_progress_egress(cf, data);
+      if(result && result != CURLE_AGAIN) {
+        h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data);
         break;
       }
 
       if(ts->has_final_response) {
-        tunnel_go_state(cf, ts, TUNNEL_RESPONSE, data);
+        h2_tunnel_go_state(cf, ts, H2_TUNNEL_RESPONSE, data);
       }
       else {
         result = CURLE_OK;
@@ -976,28 +1002,28 @@ static CURLcode CONNECT(struct Curl_cfilter *cf,
       }
       /* FALLTHROUGH */
 
-    case TUNNEL_RESPONSE:
+    case H2_TUNNEL_RESPONSE:
       DEBUGASSERT(ts->has_final_response);
       result = inspect_response(cf, data, ts);
       if(result)
         goto out;
       break;
 
-    case TUNNEL_ESTABLISHED:
+    case H2_TUNNEL_ESTABLISHED:
       return CURLE_OK;
 
-    case TUNNEL_FAILED:
+    case H2_TUNNEL_FAILED:
       return CURLE_RECV_ERROR;
 
     default:
       break;
     }
 
-  } while(ts->state == TUNNEL_INIT);
+  } while(ts->state == H2_TUNNEL_INIT);
 
 out:
   if(result || ctx->tunnel.closed)
-    tunnel_go_state(cf, ts, TUNNEL_FAILED, data);
+    h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data);
   return result;
 }
 
@@ -1043,10 +1069,10 @@ static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf,
   /* for the secondary socket (FTP), use the "connect to host"
    * but ignore the "connect to port" (use the secondary port)
    */
-  result = CONNECT(cf, data, ts);
+  result = H2_CONNECT(cf, data, ts);
 
 out:
-  *done = (result == CURLE_OK) && (ts->state == TUNNEL_ESTABLISHED);
+  *done = (result == CURLE_OK) && (ts->state == H2_TUNNEL_ESTABLISHED);
   cf->connected = *done;
   CF_DATA_RESTORE(cf, save);
   return result;
@@ -1082,7 +1108,7 @@ static bool cf_h2_proxy_data_pending(struct Curl_cfilter *cf,
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   if((ctx && !Curl_bufq_is_empty(&ctx->inbufq)) ||
-     (ctx && ctx->tunnel.state == TUNNEL_ESTABLISHED &&
+     (ctx && ctx->tunnel.state == H2_TUNNEL_ESTABLISHED &&
       !Curl_bufq_is_empty(&ctx->tunnel.recvbuf)))
     return TRUE;
   return cf->next? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
@@ -1188,14 +1214,14 @@ static ssize_t cf_h2_proxy_recv(struct Curl_cfilter *cf,
   struct cf_call_data save;
   CURLcode result;
 
-  if(ctx->tunnel.state != TUNNEL_ESTABLISHED) {
+  if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) {
     *err = CURLE_RECV_ERROR;
     return -1;
   }
   CF_DATA_SAVE(save, cf, data);
 
   if(Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) {
-    *err = h2_progress_ingress(cf, data);
+    *err = proxy_h2_progress_ingress(cf, data);
     if(*err)
       goto out;
   }
@@ -1208,13 +1234,19 @@ static ssize_t cf_h2_proxy_recv(struct Curl_cfilter *cf,
     nghttp2_session_consume(ctx->h2, ctx->tunnel.stream_id, (size_t)nread);
   }
 
-  result = h2_progress_egress(cf, data);
-  if(result) {
+  result = proxy_h2_progress_egress(cf, data);
+  if(result && result != CURLE_AGAIN) {
     *err = result;
     nread = -1;
   }
 
 out:
+  if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) &&
+     (nread >= 0 || *err == CURLE_AGAIN)) {
+    /* data pending and no fatal error to report. Need to trigger
+     * draining to avoid stalling when no socket events happen. */
+    drain_tunnel(cf, data, &ctx->tunnel);
+  }
   DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_recv(len=%zu) -> %zd %d",
                 ctx->tunnel.stream_id, len, nread, *err));
   CF_DATA_RESTORE(cf, save);
@@ -1223,93 +1255,188 @@ out:
 
 static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf,
                                 struct Curl_easy *data,
-                                const void *mem, size_t len, CURLcode *err)
+                                const void *buf, size_t len, CURLcode *err)
 {
   struct cf_h2_proxy_ctx *ctx = cf->ctx;
   struct cf_call_data save;
-  ssize_t nwritten = -1;
-  const unsigned char *buf = mem;
-  size_t start_len = len;
   int rv;
+  ssize_t nwritten;
+  CURLcode result;
+  int blocked = 0;
 
-  if(ctx->tunnel.state != TUNNEL_ESTABLISHED) {
+  if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) {
     *err = CURLE_SEND_ERROR;
     return -1;
   }
   CF_DATA_SAVE(save, cf, data);
 
-  while(len) {
+  if(ctx->tunnel.closed) {
+    nwritten = -1;
+    *err = CURLE_SEND_ERROR;
+    goto out;
+  }
+  else if(ctx->tunnel.upload_blocked_len) {
+    /* the data in `buf` has alread been submitted or added to the
+     * buffers, but have been EAGAINed on the last invocation. */
+    DEBUGASSERT(len >= ctx->tunnel.upload_blocked_len);
+    if(len < ctx->tunnel.upload_blocked_len) {
+      /* Did we get called again with a smaller `len`? This should not
+       * happend. We are not prepared to handle that. */
+      failf(data, "HTTP/2 proxy, send again with decreased length");
+      *err = CURLE_HTTP2;
+      nwritten = -1;
+      goto out;
+    }
+    nwritten = (ssize_t)ctx->tunnel.upload_blocked_len;
+    ctx->tunnel.upload_blocked_len = 0;
+  }
+  else {
     nwritten = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, err);
-    if(nwritten <= 0) {
-      if(*err && *err != CURLE_AGAIN) {
-        DEBUGF(LOG_CF(data, cf, "error adding data to tunnel sendbuf: %d",
-               *err));
-        nwritten = -1;
+    if(nwritten < 0) {
+      if(*err != CURLE_AGAIN)
         goto out;
-      }
-      /* blocked */
       nwritten = 0;
     }
-    else {
-      DEBUGASSERT((size_t)nwritten <= len);
-      buf += (size_t)nwritten;
-      len -= (size_t)nwritten;
-    }
+  }
 
-    /* resume the tunnel stream and let the h2 session send, which
-     * triggers reading from tunnel.sendbuf */
+  if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) {
+    /* req body data is buffered, resume the potentially suspended stream */
     rv = nghttp2_session_resume_data(ctx->h2, ctx->tunnel.stream_id);
     if(nghttp2_is_fatal(rv)) {
       *err = CURLE_SEND_ERROR;
       nwritten = -1;
       goto out;
     }
-    *err = h2_progress_egress(cf, data);
-    if(*err) {
-      nwritten = -1;
-      goto out;
-    }
-
-    if(!nwritten && Curl_bufq_is_full(&ctx->tunnel.sendbuf)) {
-      size_t rwin;
-      /* we could not add to the buffer and after session processing,
-       * it is still full. */
-      rwin = nghttp2_session_get_stream_remote_window_size(
-                                        ctx->h2, ctx->tunnel.stream_id);
-      DEBUGF(LOG_CF(data, cf, "cf_send: tunnel win %u/%zu",
-             nghttp2_session_get_remote_window_size(ctx->h2), rwin));
-      if(rwin == 0) {
-        /* We cannot upload more as the stream's remote window size
-         * is 0. We need to receive WIN_UPDATEs before we can continue.
-         */
-        data->req.keepon |= KEEP_SEND_HOLD;
-        DEBUGF(LOG_CF(data, cf, "pausing send as remote flow "
-               "window is exhausted"));
-      }
-      break;
-    }
   }
 
-  nwritten = start_len - len;
-  if(nwritten > 0) {
-    *err = CURLE_OK;
+  /* Call the nghttp2 send loop and flush to write ALL buffered data,
+   * headers and/or request body completely out to the network */
+  result = proxy_h2_progress_egress(cf, data);
+  if(result == CURLE_AGAIN) {
+    blocked = 1;
   }
-  else if(ctx->tunnel.closed) {
+  else if(result) {
+    *err = result;
     nwritten = -1;
-    *err = CURLE_SEND_ERROR;
+    goto out;
   }
-  else {
-    nwritten = -1;
+  else if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) {
+    /* although we wrote everything that nghttp2 wants to send now,
+     * there is data left in our stream send buffer unwritten. This may
+     * be due to the stream's HTTP/2 flow window being exhausted. */
+    blocked = 1;
+  }
+
+  if(blocked) {
+    /* Unable to send all data, due to connection blocked or H2 window
+     * exhaustion. Data is left in our stream buffer, or nghttp2's internal
+     * frame buffer or our network out buffer. */
+    size_t rwin = nghttp2_session_get_stream_remote_window_size(
+                    ctx->h2, ctx->tunnel.stream_id);
+    if(rwin == 0) {
+      /* H2 flow window exhaustion.
+       * FIXME: there is no way to HOLD all transfers that use this
+       * proxy connection AND to UNHOLD all of them again when the
+       * window increases.
+       * We *could* iterate over all data on this conn maybe? */
+      DEBUGF(LOG_CF(data, cf, "[h2sid=%d] remote flow "
+             "window is exhausted", ctx->tunnel.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. */
+    ctx->tunnel.upload_blocked_len = nwritten;
+    DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) BLOCK: win %u/%zu "
+           "blocked_len=%zu",
+           ctx->tunnel.stream_id, len,
+           nghttp2_session_get_remote_window_size(ctx->h2), rwin,
+           nwritten));
     *err = CURLE_AGAIN;
+    nwritten = -1;
+    goto out;
+  }
+  else if(should_close_session(ctx)) {
+    /* nghttp2 thinks this session is done. If the stream has not been
+     * closed, this is an error state for out transfer */
+    if(ctx->tunnel.closed) {
+      *err = CURLE_SEND_ERROR;
+      nwritten = -1;
+    }
+    else {
+      DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session"));
+      *err = CURLE_HTTP2;
+      nwritten = -1;
+    }
   }
 
 out:
-  DEBUGF(LOG_CF(data, cf, "cf_send(len=%zu) -> %zd, %d ",
-         start_len, nwritten, *err));
+  DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) -> %zd, %d, "
+                "h2 windows %d-%d (stream-conn), "
+                "buffers %zu-%zu (stream-conn)",
+                ctx->tunnel.stream_id, len, nwritten, *err,
+                nghttp2_session_get_stream_remote_window_size(
+                  ctx->h2, ctx->tunnel.stream_id),
+                nghttp2_session_get_remote_window_size(ctx->h2),
+                Curl_bufq_len(&ctx->tunnel.sendbuf),
+                Curl_bufq_len(&ctx->outbufq)));
   CF_DATA_RESTORE(cf, save);
   return nwritten;
 }
 
+static bool proxy_h2_connisalive(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 bool *input_pending)
+{
+  struct cf_h2_proxy_ctx *ctx = cf->ctx;
+  bool alive = TRUE;
+
+  *input_pending = FALSE;
+  if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
+    return FALSE;
+
+  if(*input_pending) {
+    /* This happens before we've sent off a request and the connection is
+       not in use by any other transfer, there shouldn't be any data here,
+       only "protocol frames" */
+    CURLcode result;
+    ssize_t nread = -1;
+
+    *input_pending = FALSE;
+    nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result);
+    if(nread != -1) {
+      if(proxy_h2_process_pending_input(cf, data, &result) < 0)
+        /* immediate error, considered dead */
+        alive = FALSE;
+      else {
+        alive = !should_close_session(ctx);
+      }
+    }
+    else if(result != CURLE_AGAIN) {
+      /* the read failed so let's say this is dead anyway */
+      alive = FALSE;
+    }
+  }
+
+  return alive;
+}
+
+static bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf,
+                                 struct Curl_easy *data,
+                                 bool *input_pending)
+{
+  struct cf_h2_proxy_ctx *ctx = cf->ctx;
+  CURLcode result;
+  struct cf_call_data save;
+
+  CF_DATA_SAVE(save, cf, data);
+  result = (ctx && ctx->h2 && proxy_h2_connisalive(cf, data, input_pending));
+  DEBUGF(LOG_CF(data, cf, "conn alive -> %d, input_pending=%d",
+         result, *input_pending));
+  CF_DATA_RESTORE(cf, save);
+  return result;
+}
+
 struct Curl_cftype Curl_cft_h2_proxy = {
   "H2-PROXY",
   CF_TYPE_IP_CONNECT,
@@ -1323,7 +1450,7 @@ struct Curl_cftype Curl_cft_h2_proxy = {
   cf_h2_proxy_send,
   cf_h2_proxy_recv,
   Curl_cf_def_cntrl,
-  Curl_cf_def_conn_is_alive,
+  cf_h2_proxy_is_alive,
   Curl_cf_def_conn_keep_alive,
   Curl_cf_def_query,
 };

+ 8 - 3
Utilities/cmcurl/lib/cf-haproxy.c

@@ -71,6 +71,7 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
   struct cf_haproxy_ctx *ctx = cf->ctx;
   CURLcode result;
   const char *tcp_version;
+  const char *client_ip;
 
   DEBUGASSERT(ctx);
   DEBUGASSERT(ctx->state == HAPROXY_INIT);
@@ -82,11 +83,15 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
 #endif /* USE_UNIX_SOCKETS */
   /* Emit the correct prefix for IPv6 */
   tcp_version = cf->conn->bits.ipv6 ? "TCP6" : "TCP4";
+  if(data->set.str[STRING_HAPROXY_CLIENT_IP])
+    client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP];
+  else
+    client_ip = data->info.conn_primary_ip;
 
   result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
                          tcp_version,
                          data->info.conn_local_ip,
-                         data->info.conn_primary_ip,
+                         client_ip,
                          data->info.conn_local_port,
                          data->info.conn_primary_port);
 
@@ -110,7 +115,7 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
     return CURLE_OK;
   }
 
-  result = cf->next->cft->connect(cf->next, data, blocking, done);
+  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
   if(result || !*done)
     return result;
 
@@ -163,7 +168,7 @@ static void cf_haproxy_close(struct Curl_cfilter *cf,
   cf->connected = FALSE;
   cf_haproxy_ctx_reset(cf->ctx);
   if(cf->next)
-    cf->next->cft->close(cf->next, data);
+    cf->next->cft->do_close(cf->next, data);
 }
 
 static int cf_haproxy_get_select_socks(struct Curl_cfilter *cf,

+ 6 - 6
Utilities/cmcurl/lib/cf-https-connect.c

@@ -376,9 +376,9 @@ static bool cf_hc_data_pending(struct Curl_cfilter *cf,
          || cf_hc_baller_data_pending(&ctx->h21_baller, data);
 }
 
-static struct curltime get_max_baller_time(struct Curl_cfilter *cf,
-                                          struct Curl_easy *data,
-                                          int query)
+static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf,
+                                              struct Curl_easy *data,
+                                              int query)
 {
   struct cf_hc_ctx *ctx = cf->ctx;
   struct Curl_cfilter *cfb;
@@ -408,12 +408,12 @@ static CURLcode cf_hc_query(struct Curl_cfilter *cf,
     switch(query) {
     case CF_QUERY_TIMER_CONNECT: {
       struct curltime *when = pres2;
-      *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT);
+      *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT);
       return CURLE_OK;
     }
     case CF_QUERY_TIMER_APPCONNECT: {
       struct curltime *when = pres2;
-      *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT);
+      *when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT);
       return CURLE_OK;
     }
     default:
@@ -432,7 +432,7 @@ static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data)
   cf->connected = FALSE;
 
   if(cf->next) {
-    cf->next->cft->close(cf->next, data);
+    cf->next->cft->do_close(cf->next, data);
     Curl_conn_cf_discard_chain(&cf->next, data);
   }
 }

+ 43 - 34
Utilities/cmcurl/lib/cf-socket.c

@@ -871,7 +871,7 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
       /* this is our local socket, we did never publish it */
       DEBUGF(LOG_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
                     ", not active)", ctx->sock));
-      sclose(ctx->sock);
+      socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
       ctx->sock = CURL_SOCKET_BAD;
     }
     Curl_bufq_reset(&ctx->recvbuf);
@@ -901,22 +901,26 @@ static CURLcode set_local_ip(struct Curl_cfilter *cf,
   struct cf_socket_ctx *ctx = cf->ctx;
 
 #ifdef HAVE_GETSOCKNAME
-  char buffer[STRERROR_LEN];
-  struct Curl_sockaddr_storage ssloc;
-  curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage);
+  if(!(data->conn->handler->protocol & CURLPROTO_TFTP)) {
+    /* TFTP does not connect, so it cannot get the IP like this */
 
-  memset(&ssloc, 0, sizeof(ssloc));
-  if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) {
-    int error = SOCKERRNO;
-    failf(data, "getsockname() failed with errno %d: %s",
-          error, Curl_strerror(error, buffer, sizeof(buffer)));
-    return CURLE_FAILED_INIT;
-  }
-  if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
-                       ctx->l_ip, &ctx->l_port)) {
-    failf(data, "ssloc inet_ntop() failed with errno %d: %s",
-          errno, Curl_strerror(errno, buffer, sizeof(buffer)));
-    return CURLE_FAILED_INIT;
+    char buffer[STRERROR_LEN];
+    struct Curl_sockaddr_storage ssloc;
+    curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage);
+
+    memset(&ssloc, 0, sizeof(ssloc));
+    if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) {
+      int error = SOCKERRNO;
+      failf(data, "getsockname() failed with errno %d: %s",
+            error, Curl_strerror(error, buffer, sizeof(buffer)));
+      return CURLE_FAILED_INIT;
+    }
+    if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
+                         ctx->l_ip, &ctx->l_port)) {
+      failf(data, "ssloc inet_ntop() failed with errno %d: %s",
+            errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+      return CURLE_FAILED_INIT;
+    }
   }
 #else
   (void)data;
@@ -1356,26 +1360,31 @@ out:
 static void conn_set_primary_ip(struct Curl_cfilter *cf,
                                 struct Curl_easy *data)
 {
-  struct cf_socket_ctx *ctx = cf->ctx;
 #ifdef HAVE_GETPEERNAME
-  char buffer[STRERROR_LEN];
-  struct Curl_sockaddr_storage ssrem;
-  curl_socklen_t plen;
-  int port;
+  struct cf_socket_ctx *ctx = cf->ctx;
+  if(!(data->conn->handler->protocol & CURLPROTO_TFTP)) {
+    /* TFTP does not connect the endpoint: getpeername() failed with errno
+       107: Transport endpoint is not connected */
 
-  plen = sizeof(ssrem);
-  memset(&ssrem, 0, plen);
-  if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
-    int error = SOCKERRNO;
-    failf(data, "getpeername() failed with errno %d: %s",
-          error, Curl_strerror(error, buffer, sizeof(buffer)));
-    return;
-  }
-  if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
-                       cf->conn->primary_ip, &port)) {
-    failf(data, "ssrem inet_ntop() failed with errno %d: %s",
-          errno, Curl_strerror(errno, buffer, sizeof(buffer)));
-    return;
+    char buffer[STRERROR_LEN];
+    struct Curl_sockaddr_storage ssrem;
+    curl_socklen_t plen;
+    int port;
+
+    plen = sizeof(ssrem);
+    memset(&ssrem, 0, plen);
+    if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
+      int error = SOCKERRNO;
+      failf(data, "getpeername() failed with errno %d: %s",
+            error, Curl_strerror(error, buffer, sizeof(buffer)));
+      return;
+    }
+    if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
+                         cf->conn->primary_ip, &port)) {
+      failf(data, "ssrem inet_ntop() failed with errno %d: %s",
+            errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+      return;
+    }
   }
 #else
   cf->conn->primary_ip[0] = 0;

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

@@ -50,7 +50,7 @@ void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   cf->connected = FALSE;
   if(cf->next)
-    cf->next->cft->close(cf->next, data);
+    cf->next->cft->do_close(cf->next, data);
 }
 #endif
 
@@ -161,7 +161,7 @@ void Curl_conn_close(struct Curl_easy *data, int index)
   /* it is valid to call that without filters being present */
   cf = data->conn->cfilter[index];
   if(cf) {
-    cf->cft->close(cf, data);
+    cf->cft->do_close(cf, data);
   }
 }
 
@@ -179,7 +179,7 @@ ssize_t Curl_conn_recv(struct Curl_easy *data, int num, char *buf,
   if(cf) {
     return cf->cft->do_recv(cf, data, buf, len, code);
   }
-  failf(data, CMSGI(data->conn, num, "recv: no filter connected"));
+  failf(data, "recv: no filter connected");
   *code = CURLE_FAILED_INIT;
   return -1;
 }
@@ -198,7 +198,7 @@ ssize_t Curl_conn_send(struct Curl_easy *data, int num,
   if(cf) {
     return cf->cft->do_send(cf, data, mem, len, code);
   }
-  failf(data, CMSGI(data->conn, num, "send: no filter connected"));
+  failf(data, "send: no filter connected");
   DEBUGASSERT(0);
   *code = CURLE_FAILED_INIT;
   return -1;
@@ -293,14 +293,14 @@ CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
                               bool blocking, bool *done)
 {
   if(cf)
-    return cf->cft->connect(cf, data, blocking, done);
+    return cf->cft->do_connect(cf, data, blocking, done);
   return CURLE_FAILED_INIT;
 }
 
 void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   if(cf)
-    cf->cft->close(cf, data);
+    cf->cft->do_close(cf, data);
 }
 
 int Curl_conn_cf_get_select_socks(struct Curl_cfilter *cf,
@@ -348,7 +348,7 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
 
   *done = cf->connected;
   if(!*done) {
-    result = cf->cft->connect(cf, data, blocking, done);
+    result = cf->cft->do_connect(cf, data, blocking, done);
     if(!result && *done) {
       Curl_conn_ev_update_info(data, data->conn);
       conn_report_connect_stats(data, data->conn);

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

@@ -168,8 +168,8 @@ struct Curl_cftype {
   int flags;                              /* flags of filter type */
   int log_level;                          /* log level for such filters */
   Curl_cft_destroy_this *destroy;         /* destroy resources of this cf */
-  Curl_cft_connect *connect;              /* establish connection */
-  Curl_cft_close *close;                  /* close conn */
+  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_data_pending *has_data_pending;/* conn has data pending */

+ 2 - 1
Utilities/cmcurl/lib/conncache.h

@@ -39,7 +39,8 @@ struct connectdata;
 struct conncache {
   struct Curl_hash hash;
   size_t num_conn;
-  long next_connection_id;
+  curl_off_t next_connection_id;
+  curl_off_t next_easy_id;
   struct curltime last_cleanup;
   /* handle used for closing cached connections */
   struct Curl_easy *closure_handle;

+ 3 - 3
Utilities/cmcurl/lib/connect.c

@@ -253,7 +253,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
 }
 
 struct connfind {
-  long id_tofind;
+  curl_off_t id_tofind;
   struct connectdata *found;
 };
 
@@ -937,7 +937,7 @@ static void cf_he_close(struct Curl_cfilter *cf,
   ctx->state = SCFST_INIT;
 
   if(cf->next) {
-    cf->next->cft->close(cf->next, data);
+    cf->next->cft->do_close(cf->next, data);
     Curl_conn_cf_discard_chain(&cf->next, data);
   }
 }
@@ -1291,7 +1291,7 @@ static void cf_setup_close(struct Curl_cfilter *cf,
   ctx->state = CF_SETUP_INIT;
 
   if(cf->next) {
-    cf->next->cft->close(cf->next, data);
+    cf->next->cft->do_close(cf->next, data);
     Curl_conn_cf_discard_chain(&cf->next, data);
   }
 }

+ 5 - 4
Utilities/cmcurl/lib/cookie.c

@@ -123,8 +123,9 @@ static void freecookie(struct Cookie *co)
   free(co);
 }
 
-static bool tailmatch(const char *cookie_domain, size_t cookie_domain_len,
-                      const char *hostname)
+static bool cookie_tailmatch(const char *cookie_domain,
+                             size_t cookie_domain_len,
+                             const char *hostname)
 {
   size_t hostname_len = strlen(hostname);
 
@@ -696,7 +697,7 @@ Curl_cookie_add(struct Curl_easy *data,
           if(!domain
              || (is_ip && !strncmp(valuep, domain, vlen) &&
                  (vlen == strlen(domain)))
-             || (!is_ip && tailmatch(valuep, vlen, domain))) {
+             || (!is_ip && cookie_tailmatch(valuep, vlen, domain))) {
             strstore(&co->domain, valuep, vlen);
             if(!co->domain) {
               badcookie = TRUE;
@@ -1431,7 +1432,7 @@ struct Cookie *Curl_cookie_getlist(struct Curl_easy *data,
       /* now check if the domain is correct */
       if(!co->domain ||
          (co->tailmatch && !is_ip &&
-          tailmatch(co->domain, strlen(co->domain), host)) ||
+          cookie_tailmatch(co->domain, strlen(co->domain), host)) ||
          ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) {
         /*
          * the right part of the host matches the domain stuff in the

+ 3 - 3
Utilities/cmcurl/lib/curl_config.h.cmake

@@ -443,6 +443,9 @@
 /* Define to 1 if you have the sigsetjmp function or macro. */
 #cmakedefine HAVE_SIGSETJMP 1
 
+/* Define to 1 if you have the `snprintf' function. */
+#cmakedefine HAVE_SNPRINTF
+
 /* Define to 1 if struct sockaddr_in6 has the sin6_scope_id member */
 #cmakedefine HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1
 
@@ -572,9 +575,6 @@
 /* Define to 1 if you have the windows.h header file. */
 #cmakedefine HAVE_WINDOWS_H 1
 
-/* Define to 1 if you have the winldap.h header file. */
-#cmakedefine HAVE_WINLDAP_H 1
-
 /* Define to 1 if you have the winsock2.h header file. */
 #cmakedefine HAVE_WINSOCK2_H 1
 

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

@@ -130,13 +130,11 @@ void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf,
                        const char *fmt, ...)
 {
   DEBUGASSERT(cf);
-  if(data && Curl_log_cf_is_debug(cf)) {
+  if(data && Curl_log_cf_is_debug(cf, data)) {
     va_list ap;
     int len;
     char buffer[MAXINFO + 2];
-    len = msnprintf(buffer, MAXINFO, "[CONN-%ld%s-%s] ",
-                    cf->conn->connection_id, cf->sockindex? "/2" : "",
-                    cf->cft->name);
+    len = msnprintf(buffer, MAXINFO, "[%s] ", cf->cft->name);
     va_start(ap, fmt);
     len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
     va_end(ap);

+ 7 - 24
Utilities/cmcurl/lib/curl_log.h

@@ -74,7 +74,7 @@ void Curl_debug(struct Curl_easy *data, curl_infotype type,
     defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
 
 #define LOG_CF(data, cf, ...) \
-  do { if(Curl_log_cf_is_debug(cf)) \
+  do { if(Curl_log_cf_is_debug(cf, data)) \
          Curl_log_cf_debug(data, cf, __VA_ARGS__); } while(0)
 #else
 #define LOG_CF Curl_log_cf_debug
@@ -90,8 +90,10 @@ void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf,
                        const char *fmt, ...);
 #endif
 
-#define Curl_log_cf_is_debug(cf) \
-    ((cf) && (cf)->cft->log_level >= CURL_LOG_DEBUG)
+#define Curl_log_cf_is_debug(cf, data) \
+    ((data) && (data)->set.verbose && \
+     (cf) && (cf)->cft->log_level >= CURL_LOG_DEBUG)
+
 
 #else /* !DEBUGBUILD */
 
@@ -110,29 +112,10 @@ void Curl_log_cf_debug(struct Curl_easy *data, struct Curl_cfilter *cf,
                        const char *fmt, ...);
 #endif
 
-#define Curl_log_cf_is_debug(x)   ((void)(x), FALSE)
+#define Curl_log_cf_is_debug(x,y)   ((void)(x), (void)(y), FALSE)
 
 #endif  /* !DEBUGBUILD */
 
-#define LOG_CF_IS_DEBUG(x)        Curl_log_cf_is_debug(x)
-
-/* Macros intended for DEBUGF logging, use like:
- * DEBUGF(infof(data, CFMSG(cf, "this filter %s rocks"), "very much"));
- * and it will output:
- * [CONN-1-0][CF-SSL] this filter very much rocks
- * on connection #1 with sockindex 0 for filter of type "SSL". */
-#define DMSG(d,msg)  \
-  "[CONN-%ld] "msg, (d)->conn->connection_id
-#define DMSGI(d,i,msg)  \
-  "[CONN-%ld-%d] "msg, (d)->conn->connection_id, (i)
-#define CMSG(c,msg)  \
-  "[CONN-%ld] "msg, (c)->connection_id
-#define CMSGI(c,i,msg)  \
-  "[CONN-%ld-%d] "msg, (c)->connection_id, (i)
-#define CFMSG(cf,msg)  \
-  "[CONN-%ld-%d][CF-%s] "msg, (cf)->conn->connection_id, \
-  (cf)->sockindex, (cf)->cft->name
-
-
+#define LOG_CF_IS_DEBUG(cf, data)        Curl_log_cf_is_debug(cf, data)
 
 #endif /* HEADER_CURL_LOG_H */

+ 57 - 1
Utilities/cmcurl/lib/curl_memory.h

@@ -55,9 +55,65 @@
  */
 
 #ifdef HEADER_CURL_MEMDEBUG_H
-#error "Header memdebug.h shall not be included before curl_memory.h"
+/* cleanup after memdebug.h */
+
+#ifdef MEMDEBUG_NODEFINES
+#ifdef CURLDEBUG
+
+#undef strdup
+#undef malloc
+#undef calloc
+#undef realloc
+#undef free
+#undef send
+#undef recv
+
+#ifdef WIN32
+#  ifdef UNICODE
+#    undef wcsdup
+#    undef _wcsdup
+#    undef _tcsdup
+#  else
+#    undef _tcsdup
+#  endif
+#endif
+
+#undef socket
+#undef accept
+#ifdef HAVE_SOCKETPAIR
+#undef socketpair
 #endif
 
+#ifdef HAVE_GETADDRINFO
+#if defined(getaddrinfo) && defined(__osf__)
+#undef ogetaddrinfo
+#else
+#undef getaddrinfo
+#endif
+#endif /* HAVE_GETADDRINFO */
+
+#ifdef HAVE_FREEADDRINFO
+#undef freeaddrinfo
+#endif /* HAVE_FREEADDRINFO */
+
+/* sclose is probably already defined, redefine it! */
+#undef sclose
+#undef fopen
+#undef fdopen
+#undef fclose
+
+#endif /* MEMDEBUG_NODEFINES */
+#endif /* CURLDEBUG */
+
+#undef HEADER_CURL_MEMDEBUG_H
+#endif /* HEADER_CURL_MEMDEBUG_H */
+
+/*
+** Following section applies even when CURLDEBUG is not defined.
+*/
+
+#undef fake_sclose
+
 #ifndef CURL_DID_MEMORY_FUNC_TYPEDEFS /* only if not already done */
 /*
  * The following memory function replacement typedef's are COPIED from

+ 1 - 0
Utilities/cmcurl/lib/curl_printf.h

@@ -37,6 +37,7 @@
 # undef vprintf
 # undef vfprintf
 # undef vsnprintf
+# undef mvsnprintf
 # undef aprintf
 # undef vaprintf
 # define printf curl_mprintf

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

@@ -221,12 +221,12 @@ void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data,
 }
 
 /*
- * state()
+ * sasl_state()
  *
  * This is the ONLY way to change SASL state!
  */
-static void state(struct SASL *sasl, struct Curl_easy *data,
-                  saslstate newstate)
+static void sasl_state(struct SASL *sasl, struct Curl_easy *data,
+                       saslstate newstate)
 {
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
   /* for debug purposes */
@@ -508,7 +508,7 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
 
     if(!result) {
       *progress = SASL_INPROGRESS;
-      state(sasl, data, Curl_bufref_ptr(&resp) ? state2 : state1);
+      sasl_state(sasl, data, Curl_bufref_ptr(&resp) ? state2 : state1);
     }
   }
 
@@ -548,14 +548,14 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
     if(code != sasl->params->finalcode)
       result = CURLE_LOGIN_DENIED;
     *progress = SASL_DONE;
-    state(sasl, data, SASL_STOP);
+    sasl_state(sasl, data, SASL_STOP);
     return result;
   }
 
   if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP &&
      code != sasl->params->contcode) {
     *progress = SASL_DONE;
-    state(sasl, data, SASL_STOP);
+    sasl_state(sasl, data, SASL_STOP);
     return CURLE_LOGIN_DENIED;
   }
 
@@ -698,7 +698,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
     if(code == sasl->params->finalcode) {
       /* Final response was received so we are done */
       *progress = SASL_DONE;
-      state(sasl, data, SASL_STOP);
+      sasl_state(sasl, data, SASL_STOP);
       return result;
     }
     else if(code == sasl->params->contcode) {
@@ -708,7 +708,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
     }
     else {
       *progress = SASL_DONE;
-      state(sasl, data, SASL_STOP);
+      sasl_state(sasl, data, SASL_STOP);
       return CURLE_LOGIN_DENIED;
     }
 
@@ -745,7 +745,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
 
   Curl_bufref_free(&resp);
 
-  state(sasl, data, newstate);
+  sasl_state(sasl, data, newstate);
 
   return result;
 }

+ 2 - 1
Utilities/cmcurl/lib/curl_setup.h

@@ -262,7 +262,7 @@
 #if defined(__APPLE__) && !defined(USE_ARES)
 #include <TargetConditionals.h>
 #define USE_RESOLVE_ON_IPS 1
-#  if defined(TARGET_OS_OSX) && TARGET_OS_OSX
+#  if !defined(TARGET_OS_OSX) || TARGET_OS_OSX
 #    define CURL_OSX_CALL_COPYPROXIES 1
 #  endif
 #endif
@@ -302,6 +302,7 @@
 #  if defined(HAVE_PROTO_BSDSOCKET_H) && \
     (!defined(__amigaos4__) || defined(USE_AMISSL))
      /* use bsdsocket.library directly, instead of libc networking functions */
+#    define _SYS_MBUF_H /* m_len define clashes with curl */
 #    include <proto/bsdsocket.h>
 #    ifdef __amigaos4__
        int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds,

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

@@ -77,6 +77,12 @@
 #  endif
 #endif
 
+#ifdef USE_SCHANNEL
+/* Must set this before <schannel.h> is included directly or indirectly by
+   another Windows header. */
+#  define SCHANNEL_USE_BLACKLISTS 1
+#endif
+
 #ifdef __hpux
 #  if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL)
 #    ifdef _APP32_64BIT_OFF_T

+ 0 - 2
Utilities/cmcurl/lib/dynbuf.h

@@ -81,8 +81,6 @@ int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save);
 #define DYN_PAUSE_BUFFER    (64 * 1024 * 1024)
 #define DYN_HAXPROXY        2048
 #define DYN_HTTP_REQUEST    (1024*1024)
-#define DYN_H2_HEADERS      (128*1024)
-#define DYN_H2_TRAILERS     (128*1024)
 #define DYN_APRINTF         8000000
 #define DYN_RTSP_REQ_HEADER (64*1024)
 #define DYN_TRAILERS        (64*1024)

+ 13 - 5
Utilities/cmcurl/lib/easy.c

@@ -63,6 +63,7 @@
 #include "slist.h"
 #include "mime.h"
 #include "amigaos.h"
+#include "macos.h"
 #include "warnless.h"
 #include "sigpipe.h"
 #include "vssh/ssh.h"
@@ -83,7 +84,7 @@
 
 /* true globals -- for curl_global_init() and curl_global_cleanup() */
 static unsigned int  initialized;
-static long          init_flags;
+static long          easy_init_flags;
 
 #ifdef GLOBAL_INIT_IS_THREADSAFE
 
@@ -181,6 +182,11 @@ static CURLcode global_init(long flags, bool memoryfuncs)
   }
 #endif
 
+  if(Curl_macos_init()) {
+    DEBUGF(fprintf(stderr, "Error: Curl_macos_init failed\n"));
+    goto fail;
+  }
+
   if(Curl_resolver_global_init()) {
     DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n"));
     goto fail;
@@ -199,7 +205,7 @@ static CURLcode global_init(long flags, bool memoryfuncs)
   }
 #endif
 
-  init_flags = flags;
+  easy_init_flags = flags;
 
 #ifdef DEBUGBUILD
   if(getenv("CURL_GLOBAL_INIT"))
@@ -274,7 +280,7 @@ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m,
 
 /**
  * curl_global_cleanup() globally cleanups curl, uses the value of
- * "init_flags" to determine what needs to be cleaned up and what doesn't.
+ * "easy_init_flags" to determine what needs to be cleaned up and what doesn't.
  */
 void curl_global_cleanup(void)
 {
@@ -294,7 +300,7 @@ void curl_global_cleanup(void)
   Curl_resolver_global_cleanup();
 
 #ifdef WIN32
-  Curl_win32_cleanup(init_flags);
+  Curl_win32_cleanup(easy_init_flags);
 #endif
 
   Curl_amiga_cleanup();
@@ -308,7 +314,7 @@ void curl_global_cleanup(void)
   free(leakpointer);
 #endif
 
-  init_flags  = 0;
+  easy_init_flags = 0;
 
   global_init_unlock();
 }
@@ -893,6 +899,8 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
   /* the connection cache is setup on demand */
   outcurl->state.conn_cache = NULL;
   outcurl->state.lastconnect_id = -1;
+  outcurl->state.recent_conn_id = -1;
+  outcurl->id = -1;
 
   outcurl->progress.flags    = data->progress.flags;
   outcurl->progress.callback = data->progress.callback;

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

@@ -1,3 +1,5 @@
+#ifndef HEADER_CURL_EASY_LOCK_H
+#define HEADER_CURL_EASY_LOCK_H
 /***************************************************************************
  *                                  _   _ ____  _
  *  Project                     ___| | | |  _ \| |
@@ -103,3 +105,5 @@ static inline void curl_simple_lock_unlock(curl_simple_lock *lock)
 #undef  GLOBAL_INIT_IS_THREADSAFE
 
 #endif
+
+#endif /* HEADER_CURL_EASY_LOCK_H */

+ 5 - 2
Utilities/cmcurl/lib/easyoptions.c

@@ -120,6 +120,7 @@ struct curl_easyoption Curl_easyopts[] = {
   {"HAPPY_EYEBALLS_TIMEOUT_MS", CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS,
    CURLOT_LONG, 0},
   {"HAPROXYPROTOCOL", CURLOPT_HAPROXYPROTOCOL, CURLOT_LONG, 0},
+  {"HAPROXY_CLIENT_IP", CURLOPT_HAPROXY_CLIENT_IP, CURLOT_STRING, 0},
   {"HEADER", CURLOPT_HEADER, CURLOT_LONG, 0},
   {"HEADERDATA", CURLOPT_HEADERDATA, CURLOT_CBPTR, 0},
   {"HEADERFUNCTION", CURLOPT_HEADERFUNCTION, CURLOT_FUNCTION, 0},
@@ -164,7 +165,9 @@ struct curl_easyoption Curl_easyopts[] = {
   {"MAIL_AUTH", CURLOPT_MAIL_AUTH, CURLOT_STRING, 0},
   {"MAIL_FROM", CURLOPT_MAIL_FROM, CURLOT_STRING, 0},
   {"MAIL_RCPT", CURLOPT_MAIL_RCPT, CURLOT_SLIST, 0},
-  {"MAIL_RCPT_ALLLOWFAILS", CURLOPT_MAIL_RCPT_ALLLOWFAILS, CURLOT_LONG, 0},
+  {"MAIL_RCPT_ALLLOWFAILS", CURLOPT_MAIL_RCPT_ALLOWFAILS,
+   CURLOT_LONG, CURLOT_FLAG_ALIAS},
+  {"MAIL_RCPT_ALLOWFAILS", CURLOPT_MAIL_RCPT_ALLOWFAILS, CURLOT_LONG, 0},
   {"MAXAGE_CONN", CURLOPT_MAXAGE_CONN, CURLOT_LONG, 0},
   {"MAXCONNECTS", CURLOPT_MAXCONNECTS, CURLOT_LONG, 0},
   {"MAXFILESIZE", CURLOPT_MAXFILESIZE, CURLOT_LONG, 0},
@@ -370,6 +373,6 @@ struct curl_easyoption Curl_easyopts[] = {
  */
 int Curl_easyopts_check(void)
 {
-  return ((CURLOPT_LASTENTRY%10000) != (322 + 1));
+  return ((CURLOPT_LASTENTRY%10000) != (323 + 1));
 }
 #endif

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

@@ -56,13 +56,13 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
   int fd = -1;
   *tempname = NULL;
 
-  if(stat(filename, &sb) == -1 || !S_ISREG(sb.st_mode)) {
-    /* a non-regular file, fallback to direct fopen() */
-    *fh = fopen(filename, FOPEN_WRITETEXT);
-    if(*fh)
-      return CURLE_OK;
+  *fh = fopen(filename, FOPEN_WRITETEXT);
+  if(!*fh)
     goto fail;
-  }
+  if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode))
+    return CURLE_OK;
+  fclose(*fh);
+  *fh = NULL;
 
   result = Curl_rand_hex(data, randsuffix, sizeof(randsuffix));
   if(result)
@@ -85,7 +85,7 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
     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, sb.st_mode) == -1)
+      if(fchmod(fd, (mode_t)sb.st_mode) == -1)
         goto fail;
     }
   }

+ 65 - 65
Utilities/cmcurl/lib/ftp.c

@@ -93,14 +93,14 @@
 
 /* Local API functions */
 #ifndef DEBUGBUILD
-static void _state(struct Curl_easy *data,
-                   ftpstate newstate);
-#define state(x,y) _state(x,y)
+static void _ftp_state(struct Curl_easy *data,
+                       ftpstate newstate);
+#define ftp_state(x,y) _ftp_state(x,y)
 #else
-static void _state(struct Curl_easy *data,
-                   ftpstate newstate,
-                   int lineno);
-#define state(x,y) _state(x,y,__LINE__)
+static void _ftp_state(struct Curl_easy *data,
+                       ftpstate newstate,
+                       int lineno);
+#define ftp_state(x,y) _ftp_state(x,y,__LINE__)
 #endif
 
 static CURLcode ftp_sendquote(struct Curl_easy *data,
@@ -463,7 +463,7 @@ static CURLcode InitiateTransfer(struct Curl_easy *data)
   }
 
   conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
-  state(data, FTP_STOP);
+  ftp_state(data, FTP_STOP);
 
   return CURLE_OK;
 }
@@ -591,7 +591,7 @@ static CURLcode ftp_readresp(struct Curl_easy *data,
      * generically is a good idea.
      */
     infof(data, "We got a 421 - timeout");
-    state(data, FTP_STOP);
+    ftp_state(data, FTP_STOP);
     return CURLE_OPERATION_TIMEDOUT;
   }
 
@@ -750,10 +750,10 @@ static const char * const ftp_state_names[]={
 #endif
 
 /* This is the ONLY way to change FTP state! */
-static void _state(struct Curl_easy *data,
-                   ftpstate newstate
+static void _ftp_state(struct Curl_easy *data,
+                       ftpstate newstate
 #ifdef DEBUGBUILD
-                   , int lineno
+                       , int lineno
 #endif
   )
 {
@@ -784,7 +784,7 @@ static CURLcode ftp_state_user(struct Curl_easy *data,
   if(!result) {
     struct ftp_conn *ftpc = &conn->proto.ftpc;
     ftpc->ftp_trying_alternative = FALSE;
-    state(data, FTP_USER);
+    ftp_state(data, FTP_USER);
   }
   return result;
 }
@@ -794,7 +794,7 @@ static CURLcode ftp_state_pwd(struct Curl_easy *data,
 {
   CURLcode result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PWD");
   if(!result)
-    state(data, FTP_PWD);
+    ftp_state(data, FTP_PWD);
 
   return result;
 }
@@ -872,7 +872,7 @@ static CURLcode ftp_state_cwd(struct Curl_easy *data,
                              for all upcoming ones in the ftp->dirs[] array */
       result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->entrypath);
       if(!result)
-        state(data, FTP_CWD);
+        ftp_state(data, FTP_CWD);
     }
     else {
       if(ftpc->dirdepth) {
@@ -882,7 +882,7 @@ static CURLcode ftp_state_cwd(struct Curl_easy *data,
         result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
                                ftpc->dirs[ftpc->cwdcount -1]);
         if(!result)
-          state(data, FTP_CWD);
+          ftp_state(data, FTP_CWD);
       }
       else {
         /* No CWD necessary */
@@ -1261,11 +1261,11 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
   if(result)
     goto out;
   portsock = CURL_SOCKET_BAD; /* now held in filter */
-  state(data, FTP_PORT);
+  ftp_state(data, FTP_PORT);
 
 out:
   if(result) {
-    state(data, FTP_STOP);
+    ftp_state(data, FTP_STOP);
   }
   if(portsock != CURL_SOCKET_BAD)
     Curl_socket_close(data, conn, portsock);
@@ -1307,7 +1307,7 @@ static CURLcode ftp_state_use_pasv(struct Curl_easy *data,
   result = Curl_pp_sendf(data, &ftpc->pp, "%s", mode[modeoff]);
   if(!result) {
     ftpc->count1 = modeoff;
-    state(data, FTP_PASV);
+    ftp_state(data, FTP_PASV);
     infof(data, "Connect data stream passively");
   }
   return result;
@@ -1330,7 +1330,7 @@ static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data)
     /* doesn't transfer any data */
 
     /* still possibly do PRE QUOTE jobs */
-    state(data, FTP_RETR_PREQUOTE);
+    ftp_state(data, FTP_RETR_PREQUOTE);
     result = ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE);
   }
   else if(data->set.ftp_use_port) {
@@ -1355,7 +1355,7 @@ static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data)
         result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s",
                                conn->proto.ftpc.file);
       if(!result)
-        state(data, FTP_PRET);
+        ftp_state(data, FTP_PRET);
     }
     else
       result = ftp_state_use_pasv(data, conn);
@@ -1377,7 +1377,7 @@ static CURLcode ftp_state_rest(struct Curl_easy *data,
        whether it supports range */
     result = Curl_pp_sendf(data, &ftpc->pp, "REST %d", 0);
     if(!result)
-      state(data, FTP_REST);
+      ftp_state(data, FTP_REST);
   }
   else
     result = ftp_state_prepare_transfer(data);
@@ -1398,7 +1398,7 @@ static CURLcode ftp_state_size(struct Curl_easy *data,
     /* we know ftpc->file is a valid pointer to a file name */
     result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
     if(!result)
-      state(data, FTP_SIZE);
+      ftp_state(data, FTP_SIZE);
   }
   else
     result = ftp_state_rest(data, conn);
@@ -1466,7 +1466,7 @@ static CURLcode ftp_state_list(struct Curl_easy *data)
   free(cmd);
 
   if(!result)
-    state(data, FTP_LIST);
+    ftp_state(data, FTP_LIST);
 
   return result;
 }
@@ -1530,7 +1530,7 @@ static CURLcode ftp_state_mdtm(struct Curl_easy *data)
     result = Curl_pp_sendf(data, &ftpc->pp, "MDTM %s", ftpc->file);
 
     if(!result)
-      state(data, FTP_MDTM);
+      ftp_state(data, FTP_MDTM);
   }
   else
     result = ftp_state_type(data);
@@ -1569,7 +1569,7 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
       /* Got no given size to start from, figure it out */
       result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
       if(!result)
-        state(data, FTP_STOR_SIZE);
+        ftp_state(data, FTP_STOR_SIZE);
       return result;
     }
 
@@ -1624,7 +1624,7 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
          * ftp_done() because we didn't transfer anything! */
         ftp->transfer = PPTRANSFER_NONE;
 
-        state(data, FTP_STOP);
+        ftp_state(data, FTP_STOP);
         return CURLE_OK;
       }
     }
@@ -1634,7 +1634,7 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
   result = Curl_pp_sendf(data, &ftpc->pp, append?"APPE %s":"STOR %s",
                          ftpc->file);
   if(!result)
-    state(data, FTP_STOR);
+    ftp_state(data, FTP_STOR);
 
   return result;
 }
@@ -1695,7 +1695,7 @@ static CURLcode ftp_state_quote(struct Curl_easy *data,
       result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
       if(result)
         return result;
-      state(data, instate);
+      ftp_state(data, instate);
       quote = TRUE;
     }
   }
@@ -1709,7 +1709,7 @@ static CURLcode ftp_state_quote(struct Curl_easy *data,
       break;
     case FTP_RETR_PREQUOTE:
       if(ftp->transfer != PPTRANSFER_BODY)
-        state(data, FTP_STOP);
+        ftp_state(data, FTP_STOP);
       else {
         if(ftpc->known_filesize != -1) {
           Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
@@ -1731,12 +1731,12 @@ static CURLcode ftp_state_quote(struct Curl_easy *data,
             */
             result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
             if(!result)
-              state(data, FTP_RETR);
+              ftp_state(data, FTP_RETR);
           }
           else {
             result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
             if(!result)
-              state(data, FTP_RETR_SIZE);
+              ftp_state(data, FTP_RETR_SIZE);
           }
         }
       }
@@ -1780,7 +1780,7 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data,
   if(!result) {
     conn->proto.ftpc.count1++;
     /* remain in/go to the FTP_PASV state */
-    state(data, FTP_PASV);
+    ftp_state(data, FTP_PASV);
   }
   return result;
 }
@@ -2005,7 +2005,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
     return CURLE_OUT_OF_MEMORY;
 
   conn->bits.do_more = TRUE;
-  state(data, FTP_STOP); /* this phase is completed */
+  ftp_state(data, FTP_STOP); /* this phase is completed */
 
   return result;
 }
@@ -2039,7 +2039,7 @@ static CURLcode ftp_state_port_resp(struct Curl_easy *data,
   }
   else {
     infof(data, "Connect data stream actively");
-    state(data, FTP_STOP); /* end of DO phase */
+    ftp_state(data, FTP_STOP); /* end of DO phase */
     result = ftp_dophase_done(data, FALSE);
   }
 
@@ -2151,7 +2151,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
           infof(data, "The requested document is not new enough");
           ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
           data->info.timecond = TRUE;
-          state(data, FTP_STOP);
+          ftp_state(data, FTP_STOP);
           return CURLE_OK;
         }
         break;
@@ -2160,7 +2160,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
           infof(data, "The requested document is not old enough");
           ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
           data->info.timecond = TRUE;
-          state(data, FTP_STOP);
+          ftp_state(data, FTP_STOP);
           return CURLE_OK;
         }
         break;
@@ -2268,7 +2268,7 @@ static CURLcode ftp_state_retr(struct Curl_easy *data,
       /* Set ->transfer so that we won't get any error in ftp_done()
        * because we didn't transfer the any file */
       ftp->transfer = PPTRANSFER_NONE;
-      state(data, FTP_STOP);
+      ftp_state(data, FTP_STOP);
       return CURLE_OK;
     }
 
@@ -2279,13 +2279,13 @@ static CURLcode ftp_state_retr(struct Curl_easy *data,
     result = Curl_pp_sendf(data, &ftpc->pp, "REST %" CURL_FORMAT_CURL_OFF_T,
                            data->state.resume_from);
     if(!result)
-      state(data, FTP_RETR_REST);
+      ftp_state(data, FTP_RETR_REST);
   }
   else {
     /* no resume */
     result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
     if(!result)
-      state(data, FTP_RETR);
+      ftp_state(data, FTP_RETR);
   }
 
   return result;
@@ -2385,7 +2385,7 @@ static CURLcode ftp_state_rest_resp(struct Curl_easy *data,
     else {
       result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
       if(!result)
-        state(data, FTP_RETR);
+        ftp_state(data, FTP_RETR);
     }
     break;
   }
@@ -2401,7 +2401,7 @@ static CURLcode ftp_state_stor_resp(struct Curl_easy *data,
 
   if(ftpcode >= 400) {
     failf(data, "Failed FTP upload: %0d", ftpcode);
-    state(data, FTP_STOP);
+    ftp_state(data, FTP_STOP);
     /* oops, we never close the sockets! */
     return CURLE_UPLOAD_FAILED;
   }
@@ -2412,7 +2412,7 @@ static CURLcode ftp_state_stor_resp(struct Curl_easy *data,
   if(data->set.ftp_use_port) {
     bool connected;
 
-    state(data, FTP_STOP); /* no longer in STOR state */
+    ftp_state(data, FTP_STOP); /* no longer in STOR state */
 
     result = AllowServerConnect(data, &connected);
     if(result)
@@ -2535,7 +2535,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
       if(!connected) {
         struct ftp_conn *ftpc = &conn->proto.ftpc;
         infof(data, "Data conn was not available immediately");
-        state(data, FTP_STOP);
+        ftp_state(data, FTP_STOP);
         ftpc->wait_data_conn = TRUE;
       }
     }
@@ -2546,7 +2546,7 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
     if((instate == FTP_LIST) && (ftpcode == 450)) {
       /* simply no matching files in the dir listing */
       ftp->transfer = PPTRANSFER_NONE; /* don't download anything */
-      state(data, FTP_STOP); /* this phase is over */
+      ftp_state(data, FTP_STOP); /* this phase is over */
     }
     else {
       failf(data, "RETR response: %03d", ftpcode);
@@ -2582,7 +2582,7 @@ static CURLcode ftp_state_loggedin(struct Curl_easy *data)
     */
     result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "PBSZ %d", 0);
     if(!result)
-      state(data, FTP_PBSZ);
+      ftp_state(data, FTP_PBSZ);
   }
   else {
     result = ftp_state_pwd(data, conn);
@@ -2605,7 +2605,7 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data,
     result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s",
                            conn->passwd?conn->passwd:"");
     if(!result)
-      state(data, FTP_PASS);
+      ftp_state(data, FTP_PASS);
   }
   else if(ftpcode/100 == 2) {
     /* 230 User ... logged in.
@@ -2617,7 +2617,7 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data,
       result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s",
                              data->set.str[STRING_FTP_ACCOUNT]);
       if(!result)
-        state(data, FTP_ACCT);
+        ftp_state(data, FTP_ACCT);
     }
     else {
       failf(data, "ACCT requested but none available");
@@ -2638,7 +2638,7 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data,
                       data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
       if(!result) {
         ftpc->ftp_trying_alternative = TRUE;
-        state(data, FTP_USER);
+        ftp_state(data, FTP_USER);
       }
     }
     else {
@@ -2741,7 +2741,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
         result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
                                ftpauth[ftpc->count1]);
         if(!result)
-          state(data, FTP_AUTH);
+          ftp_state(data, FTP_AUTH);
       }
       else
         result = ftp_state_user(data, conn);
@@ -2808,7 +2808,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
         Curl_pp_sendf(data, &ftpc->pp, "PROT %c",
                       data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
       if(!result)
-        state(data, FTP_PROT);
+        ftp_state(data, FTP_PROT);
       break;
 
     case FTP_PROT:
@@ -2827,7 +2827,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
          */
         result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC");
         if(!result)
-          state(data, FTP_CCC);
+          ftp_state(data, FTP_CCC);
       }
       else
         result = ftp_state_pwd(data, conn);
@@ -2919,7 +2919,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
             infof(data, "Entry path is '%s'", ftpc->entrypath);
             /* also save it where getinfo can access it: */
             data->state.most_recent_ftp_entrypath = ftpc->entrypath;
-            state(data, FTP_SYST);
+            ftp_state(data, FTP_SYST);
             break;
           }
 
@@ -2935,7 +2935,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
           infof(data, "Failed to figure out path");
         }
       }
-      state(data, FTP_STOP); /* we are done with the CONNECT phase! */
+      ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
       DEBUGF(infof(data, "protocol connect phase DONE"));
       break;
 
@@ -2970,7 +2970,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
           /* remember target server OS */
           Curl_safefree(ftpc->server_os);
           ftpc->server_os = os;
-          state(data, FTP_NAMEFMT);
+          ftp_state(data, FTP_NAMEFMT);
           break;
         }
         /* Nothing special for the target server. */
@@ -2982,7 +2982,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
         /* Cannot identify server OS. Continue anyway and cross fingers. */
       }
 
-      state(data, FTP_STOP); /* we are done with the CONNECT phase! */
+      ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
       DEBUGF(infof(data, "protocol connect phase DONE"));
       break;
 
@@ -2993,7 +2993,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
         break;
       }
 
-      state(data, FTP_STOP); /* we are done with the CONNECT phase! */
+      ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
       DEBUGF(infof(data, "protocol connect phase DONE"));
       break;
 
@@ -3026,7 +3026,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
           result = Curl_pp_sendf(data, &ftpc->pp, "MKD %s",
                                  ftpc->dirs[ftpc->cwdcount - 1]);
           if(!result)
-            state(data, FTP_MKD);
+            ftp_state(data, FTP_MKD);
         }
         else {
           /* return failure */
@@ -3055,7 +3055,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
         result = CURLE_REMOTE_ACCESS_DENIED;
       }
       else {
-        state(data, FTP_CWD);
+        ftp_state(data, FTP_CWD);
         /* send CWD */
         result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
                                ftpc->dirs[ftpc->cwdcount - 1]);
@@ -3114,7 +3114,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
       /* fallthrough, just stop! */
     default:
       /* internal error */
-      state(data, FTP_STOP);
+      ftp_state(data, FTP_STOP);
       break;
     }
   } /* if(ftpcode) */
@@ -3191,7 +3191,7 @@ static CURLcode ftp_connect(struct Curl_easy *data,
 
   /* When we connect, we start in the state where we await the 220
      response */
-  state(data, FTP_WAIT220);
+  ftp_state(data, FTP_WAIT220);
 
   result = ftp_multi_statemach(data, done);
 
@@ -3516,13 +3516,13 @@ static CURLcode ftp_nb_type(struct Curl_easy *data,
   char want = (char)(ascii?'A':'I');
 
   if(ftpc->transfertype == want) {
-    state(data, newstate);
+    ftp_state(data, newstate);
     return ftp_state_type_resp(data, 200, newstate);
   }
 
   result = Curl_pp_sendf(data, &ftpc->pp, "TYPE %c", want);
   if(!result) {
-    state(data, newstate);
+    ftp_state(data, newstate);
 
     /* keep track of our current transfer type */
     ftpc->transfertype = want;
@@ -4040,11 +4040,11 @@ static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn)
             curl_easy_strerror(result));
       conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
       connclose(conn, "QUIT command failed"); /* mark for connection closure */
-      state(data, FTP_STOP);
+      ftp_state(data, FTP_STOP);
       return result;
     }
 
-    state(data, FTP_QUIT);
+    ftp_state(data, FTP_QUIT);
 
     result = ftp_block_statemach(data, conn);
   }

+ 7 - 0
Utilities/cmcurl/lib/getinfo.c

@@ -415,6 +415,13 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
   case CURLINFO_RETRY_AFTER:
     *param_offt = data->info.retry_after;
     break;
+  case CURLINFO_XFER_ID:
+    *param_offt = data->id;
+    break;
+  case CURLINFO_CONN_ID:
+    *param_offt = data->conn?
+                  data->conn->connection_id : data->state.recent_conn_id;
+    break;
   default:
     return CURLE_UNKNOWN_OPTION;
   }

+ 7 - 23
Utilities/cmcurl/lib/hostip.c

@@ -67,10 +67,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES)
-#include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
-#endif
-
 #if defined(CURLRES_SYNCH) &&                   \
   defined(HAVE_ALARM) &&                        \
   defined(SIGALRM) &&                           \
@@ -561,6 +557,7 @@ static struct Curl_addrinfo *get_localhost6(int port, const char *name)
 static struct Curl_addrinfo *get_localhost(int port, const char *name)
 {
   struct Curl_addrinfo *ca;
+  struct Curl_addrinfo *ca6;
   const size_t ss_size = sizeof(struct sockaddr_in);
   const size_t hostlen = strlen(name);
   struct sockaddr_in sa;
@@ -587,8 +584,12 @@ static struct Curl_addrinfo *get_localhost(int port, const char *name)
   memcpy(ca->ai_addr, &sa, ss_size);
   ca->ai_canonname = (char *)ca->ai_addr + ss_size;
   strcpy(ca->ai_canonname, name);
-  ca->ai_next = get_localhost6(port, name);
-  return ca;
+
+  ca6 = get_localhost6(port, name);
+  if(!ca6)
+    return ca;
+  ca6->ai_next = ca;
+  return ca6;
 }
 
 #ifdef ENABLE_IPV6
@@ -743,23 +744,6 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
         return CURLRESOLV_ERROR;
     }
 
-#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES)
-    {
-      /*
-       * The automagic conversion from IPv4 literals to IPv6 literals only
-       * works if the SCDynamicStoreCopyProxies system function gets called
-       * first. As Curl currently doesn't support system-wide HTTP proxies, we
-       * therefore don't use any value this function might return.
-       *
-       * This function is only available on a macOS and is not needed for
-       * IPv4-only builds, hence the conditions above.
-       */
-      CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL);
-      if(dict)
-        CFRelease(dict);
-    }
-#endif
-
 #ifndef USE_RESOLVE_ON_IPS
     /* First check if this is an IPv4 address string */
     if(Curl_inet_pton(AF_INET, hostname, &in) > 0)

+ 3 - 2
Utilities/cmcurl/lib/hsts.c

@@ -57,7 +57,7 @@
 /* to play well with debug builds, we can *set* a fixed time this will
    return */
 time_t deltatime; /* allow for "adjustments" for unit test purposes */
-static time_t debugtime(void *unused)
+static time_t hsts_debugtime(void *unused)
 {
   char *timestr = getenv("CURL_TIME");
   (void)unused;
@@ -70,7 +70,8 @@ static time_t debugtime(void *unused)
   }
   return time(NULL);
 }
-#define time(x) debugtime(x)
+#undef time
+#define time(x) hsts_debugtime(x)
 #endif
 
 struct hsts *Curl_hsts_init(void)

+ 17 - 15
Utilities/cmcurl/lib/http.c

@@ -1308,7 +1308,7 @@ CURLcode Curl_buffer_send(struct dynbuf *in,
       || IS_HTTPS_PROXY(conn->http_proxy.proxytype)
 #endif
        )
-     && conn->httpversion != 20) {
+     && conn->httpversion < 20) {
     /* Make sure this doesn't send more body bytes than what the max send
        speed says. The request bytes do not count to the max speed.
     */
@@ -2667,11 +2667,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
 #ifndef USE_HYPER
     /* With Hyper the body is always passed on separately */
     if(data->set.postfields) {
-
-      /* In HTTP2, we send request body in DATA frame regardless of
-         its size. */
-      if(conn->httpversion < 20 &&
-         !data->state.expect100header &&
+      if(!data->state.expect100header &&
          (http->postsize < MAX_INITIAL_POST_SIZE)) {
         /* if we don't use expect: 100  AND
            postsize is less than MAX_INITIAL_POST_SIZE
@@ -2832,16 +2828,18 @@ CURLcode Curl_http_cookies(struct Curl_easy *data,
     }
     if(co) {
       struct Cookie *store = co;
+      size_t clen = 8; /* hold the size of the generated Cookie: header */
       /* now loop through all cookies that matched */
       while(co) {
         if(co->value) {
-          if(0 == count) {
+          size_t add;
+          if(!count) {
             result = Curl_dyn_addn(r, STRCONST("Cookie: "));
             if(result)
               break;
           }
-          if((Curl_dyn_len(r) + strlen(co->name) + strlen(co->value) + 1) >=
-             MAX_COOKIE_HEADER_LEN) {
+          add = strlen(co->name) + strlen(co->value) + 1;
+          if(clen + add >= MAX_COOKIE_HEADER_LEN) {
             infof(data, "Restricted outgoing cookies due to header size, "
                   "'%s' not sent", co->name);
             linecap = TRUE;
@@ -2851,6 +2849,7 @@ CURLcode Curl_http_cookies(struct Curl_easy *data,
                                  co->name, co->value);
           if(result)
             break;
+          clen += add + (count ? 2 : 0);
           count++;
         }
         co = co->next; /* next cookie please */
@@ -3381,6 +3380,9 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     }
   }
 
+  if(data->req.upload_done)
+    Curl_conn_ev_data_done_send(data);
+
   if((conn->httpversion >= 20) && data->req.upload_chunky)
     /* upload_chunky was set above to set up the request in a chunky fashion,
        but is disabled here again to avoid that the chunked encoded version is
@@ -4569,8 +4571,8 @@ CURLcode Curl_http_req_make(struct httpreq **preq,
     if(!req->path)
       goto out;
   }
-  Curl_dynhds_init(&req->headers, 0, DYN_H2_HEADERS);
-  Curl_dynhds_init(&req->trailers, 0, DYN_H2_TRAILERS);
+  Curl_dynhds_init(&req->headers, 0, DYN_HTTP_REQUEST);
+  Curl_dynhds_init(&req->trailers, 0, DYN_HTTP_REQUEST);
   result = CURLE_OK;
 
 out:
@@ -4727,8 +4729,8 @@ CURLcode Curl_http_req_make2(struct httpreq **preq,
   if(result)
     goto out;
 
-  Curl_dynhds_init(&req->headers, 0, DYN_H2_HEADERS);
-  Curl_dynhds_init(&req->trailers, 0, DYN_H2_TRAILERS);
+  Curl_dynhds_init(&req->headers, 0, DYN_HTTP_REQUEST);
+  Curl_dynhds_init(&req->trailers, 0, DYN_HTTP_REQUEST);
   result = CURLE_OK;
 
 out:
@@ -4858,8 +4860,8 @@ CURLcode Curl_http_resp_make(struct http_resp **presp,
     if(!resp->description)
       goto out;
   }
-  Curl_dynhds_init(&resp->headers, 0, DYN_H2_HEADERS);
-  Curl_dynhds_init(&resp->trailers, 0, DYN_H2_TRAILERS);
+  Curl_dynhds_init(&resp->headers, 0, DYN_HTTP_REQUEST);
+  Curl_dynhds_init(&resp->trailers, 0, DYN_HTTP_REQUEST);
   result = CURLE_OK;
 
 out:

+ 51 - 78
Utilities/cmcurl/lib/http1.c

@@ -38,124 +38,97 @@
 #include "memdebug.h"
 
 
-#define MAX_URL_LEN   (4*1024)
+#define H1_MAX_URL_LEN   (8*1024)
 
 void Curl_h1_req_parse_init(struct h1_req_parser *parser, size_t max_line_len)
 {
   memset(parser, 0, sizeof(*parser));
   parser->max_line_len = max_line_len;
-  Curl_bufq_init(&parser->scratch, max_line_len, 1);
+  Curl_dyn_init(&parser->scratch, max_line_len);
 }
 
 void Curl_h1_req_parse_free(struct h1_req_parser *parser)
 {
   if(parser) {
     Curl_http_req_free(parser->req);
-    Curl_bufq_free(&parser->scratch);
+    Curl_dyn_free(&parser->scratch);
     parser->req = NULL;
     parser->done = FALSE;
   }
 }
 
+static CURLcode trim_line(struct h1_req_parser *parser, int options)
+{
+  DEBUGASSERT(parser->line);
+  if(parser->line_len) {
+    if(parser->line[parser->line_len - 1] == '\n')
+      --parser->line_len;
+    if(parser->line_len) {
+      if(parser->line[parser->line_len - 1] == '\r')
+        --parser->line_len;
+      else if(options & H1_PARSE_OPT_STRICT)
+        return CURLE_URL_MALFORMAT;
+    }
+    else if(options & H1_PARSE_OPT_STRICT)
+      return CURLE_URL_MALFORMAT;
+  }
+  else if(options & H1_PARSE_OPT_STRICT)
+    return CURLE_URL_MALFORMAT;
+
+  if(parser->line_len > parser->max_line_len) {
+    return CURLE_URL_MALFORMAT;
+  }
+  return CURLE_OK;
+}
+
 static ssize_t detect_line(struct h1_req_parser *parser,
-                           const char *buf, const size_t buflen, int options,
+                           const char *buf, const size_t buflen,
                            CURLcode *err)
 {
   const char  *line_end;
-  size_t len;
 
   DEBUGASSERT(!parser->line);
   line_end = memchr(buf, '\n', buflen);
   if(!line_end) {
-    *err = (buflen > parser->max_line_len)? CURLE_URL_MALFORMAT : CURLE_AGAIN;
+    *err = CURLE_AGAIN;
     return -1;
   }
-  len = line_end - buf + 1;
-  if(len > parser->max_line_len) {
-    *err = CURLE_URL_MALFORMAT;
-    return -1;
-  }
-
-  if(options & H1_PARSE_OPT_STRICT) {
-    if((len == 1) || (buf[len - 2] != '\r')) {
-      *err = CURLE_URL_MALFORMAT;
-      return -1;
-    }
-    parser->line = buf;
-    parser->line_len = len - 2;
-  }
-  else {
-    parser->line = buf;
-    parser->line_len = len - (((len == 1) || (buf[len - 2] != '\r'))? 1 : 2);
-  }
+  parser->line = buf;
+  parser->line_len = line_end - buf + 1;
   *err = CURLE_OK;
-  return (ssize_t)len;
+  return (ssize_t)parser->line_len;
 }
 
 static ssize_t next_line(struct h1_req_parser *parser,
                          const char *buf, const size_t buflen, int options,
                          CURLcode *err)
 {
-  ssize_t nread = 0, n;
+  ssize_t nread = 0;
 
   if(parser->line) {
-    if(parser->scratch_skip) {
-      /* last line was from scratch. Remove it now, since we are done
-       * with it and look for the next one. */
-      Curl_bufq_skip_and_shift(&parser->scratch, parser->scratch_skip);
-      parser->scratch_skip = 0;
-    }
     parser->line = NULL;
     parser->line_len = 0;
+    Curl_dyn_reset(&parser->scratch);
   }
 
-  if(Curl_bufq_is_empty(&parser->scratch)) {
-    nread = detect_line(parser, buf, buflen, options, err);
-    if(nread < 0) {
-      if(*err != CURLE_AGAIN)
+  nread = detect_line(parser, buf, buflen, err);
+  if(nread >= 0) {
+    if(Curl_dyn_len(&parser->scratch)) {
+      /* append detected line to scratch to have the complete line */
+      *err = Curl_dyn_addn(&parser->scratch, parser->line, parser->line_len);
+      if(*err)
         return -1;
-      /* not a complete line, add to scratch for later revisit */
-      nread = Curl_bufq_write(&parser->scratch,
-                              (const unsigned char *)buf, buflen, err);
-      return nread;
+      parser->line = Curl_dyn_ptr(&parser->scratch);
+      parser->line_len = Curl_dyn_len(&parser->scratch);
     }
-    /* found one */
+    *err = trim_line(parser, options);
+    if(*err)
+      return -1;
   }
-  else {
-    const char *sbuf;
-    size_t sbuflen;
-
-    /* scratch contains bytes from last attempt, add more to it */
-    if(buflen) {
-      const char *line_end;
-      size_t add_len;
-      ssize_t pos;
-
-      line_end = memchr(buf, '\n', buflen);
-      pos = line_end? (line_end - buf + 1) : -1;
-      add_len = (pos >= 0)? (size_t)pos : buflen;
-      nread = Curl_bufq_write(&parser->scratch,
-                              (const unsigned char *)buf, add_len, err);
-      if(nread < 0) {
-        /* Unable to add anything to scratch is an error, since we should
-         * have seen a line there then before. */
-        if(*err == CURLE_AGAIN)
-          *err = CURLE_URL_MALFORMAT;
-        return -1;
-      }
-    }
-
-    if(Curl_bufq_peek(&parser->scratch,
-                      (const unsigned char **)&sbuf, &sbuflen)) {
-      n = detect_line(parser, sbuf, sbuflen, options, err);
-      if(n < 0 && *err != CURLE_AGAIN)
-        return -1;  /* real error */
-      parser->scratch_skip = (size_t)n;
-    }
-    else {
-      /* we SHOULD be able to peek at scratch data */
-      DEBUGASSERT(0);
-    }
+  else if(*err == CURLE_AGAIN) {
+    /* no line end in `buf`, add it to our scratch */
+    *err = Curl_dyn_addn(&parser->scratch, (const unsigned char *)buf, buflen);
+    nread = (*err)? -1 : (ssize_t)buflen;
   }
   return nread;
 }
@@ -231,7 +204,7 @@ static CURLcode start_req(struct h1_req_parser *parser,
   else {
     /* origin-form OR absolute-form */
     CURLUcode uc;
-    char tmp[MAX_URL_LEN];
+    char tmp[H1_MAX_URL_LEN];
 
     /* default, unless we see an absolute URL */
     path = target;
@@ -328,7 +301,7 @@ ssize_t Curl_h1_req_parse_read(struct h1_req_parser *parser,
         goto out;
       }
       parser->done = TRUE;
-      Curl_bufq_free(&parser->scratch);
+      Curl_dyn_reset(&parser->scratch);
       /* last chance adjustments */
     }
     else {

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

@@ -33,11 +33,11 @@
 #define H1_PARSE_OPT_NONE       (0)
 #define H1_PARSE_OPT_STRICT     (1 << 0)
 
-#define H1_PARSE_DEFAULT_MAX_LINE_LEN (8 * 1024)
+#define H1_PARSE_DEFAULT_MAX_LINE_LEN   DYN_HTTP_REQUEST
 
 struct h1_req_parser {
   struct httpreq *req;
-  struct bufq scratch;
+  struct dynbuf scratch;
   size_t scratch_skip;
   const char *line;
   size_t max_line_len;

+ 204 - 128
Utilities/cmcurl/lib/http2.c

@@ -134,9 +134,11 @@ struct cf_h2_ctx {
   BIT(conn_closed);
   BIT(goaway);
   BIT(enable_push);
+  BIT(nw_out_blocked);
 };
 
 /* How to access `call_data` from a cf_h2 filter */
+#undef CF_CTX_CALL_DATA
 #define CF_CTX_CALL_DATA(cf)  \
   ((struct cf_h2_ctx *)(cf)->ctx)->call_data
 
@@ -175,6 +177,7 @@ struct stream_ctx {
   struct bufq sendbuf; /* request buffer */
   struct dynhds resp_trailers; /* response trailer fields */
   size_t resp_hds_len; /* amount of response header bytes in recvbuf */
+  size_t upload_blocked_len;
   curl_off_t upload_left; /* number of request bytes left to upload */
 
   char **push_headers;       /* allocated array */
@@ -183,6 +186,7 @@ struct stream_ctx {
 
   int status_code; /* HTTP response status code */
   uint32_t error; /* stream error code */
+  uint32_t local_window_size; /* the local recv window size */
   bool closed; /* TRUE on stream close */
   bool reset;  /* TRUE on stream reset */
   bool close_handled; /* TRUE if stream closure is handled by libcurl */
@@ -209,9 +213,12 @@ static void drain_stream(struct Curl_cfilter *cf,
 
   (void)cf;
   bits = CURL_CSELECT_IN;
-  if(!stream->send_closed && stream->upload_left)
+  if(!stream->send_closed &&
+     (stream->upload_left || stream->upload_blocked_len))
     bits |= CURL_CSELECT_OUT;
   if(data->state.dselect_bits != bits) {
+    DEBUGF(LOG_CF(data, cf, "[h2sid=%d] DRAIN dselect_bits=%x",
+                  stream->id, bits));
     data->state.dselect_bits = bits;
     Curl_expire(data, 0, EXPIRE_RUN_NOW);
   }
@@ -245,13 +252,14 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf,
                   H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
   Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
                   H2_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
-  Curl_dynhds_init(&stream->resp_trailers, 0, DYN_H2_TRAILERS);
+  Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST);
   stream->resp_hds_len = 0;
   stream->bodystarted = FALSE;
   stream->status_code = -1;
   stream->closed = FALSE;
   stream->close_handled = FALSE;
   stream->error = NGHTTP2_NO_ERROR;
+  stream->local_window_size = H2_STREAM_WINDOW_SIZE;
   stream->upload_left = 0;
 
   H2_STREAM_LCTX(data) = stream;
@@ -580,7 +588,6 @@ static bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data,
     ssize_t nread = -1;
 
     *input_pending = FALSE;
-    Curl_attach_connection(data, cf->conn);
     nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result);
     if(nread != -1) {
       DEBUGF(LOG_CF(data, cf, "%zd bytes stray data read before trying "
@@ -592,11 +599,10 @@ static bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data,
         alive = !should_close_session(ctx);
       }
     }
-    else {
+    else if(result != CURLE_AGAIN) {
       /* the read failed so let's say this is dead anyway */
       alive = FALSE;
     }
-    Curl_detach_connection(data);
   }
 
   return alive;
@@ -644,13 +650,17 @@ static CURLcode nw_out_flush(struct Curl_cfilter *cf,
   if(Curl_bufq_is_empty(&ctx->outbufq))
     return CURLE_OK;
 
-  DEBUGF(LOG_CF(data, cf, "h2 conn flush %zu bytes",
-                Curl_bufq_len(&ctx->outbufq)));
   nwritten = Curl_bufq_pass(&ctx->outbufq, nw_out_writer, cf, &result);
-  if(nwritten < 0 && result != CURLE_AGAIN) {
+  if(nwritten < 0) {
+    if(result == CURLE_AGAIN) {
+      DEBUGF(LOG_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN",
+                    Curl_bufq_len(&ctx->outbufq)));
+      ctx->nw_out_blocked = 1;
+    }
     return result;
   }
-  return CURLE_OK;
+  DEBUGF(LOG_CF(data, cf, "nw send buffer flushed"));
+  return Curl_bufq_is_empty(&ctx->outbufq)? CURLE_OK: CURLE_AGAIN;
 }
 
 /*
@@ -676,15 +686,17 @@ static ssize_t send_callback(nghttp2_session *h2,
                                   nw_out_writer, cf, &result);
   if(nwritten < 0) {
     if(result == CURLE_AGAIN) {
+      ctx->nw_out_blocked = 1;
       return NGHTTP2_ERR_WOULDBLOCK;
     }
     failf(data, "Failed sending HTTP2 data");
     return NGHTTP2_ERR_CALLBACK_FAILURE;
   }
 
-  if(!nwritten)
+  if(!nwritten) {
+    ctx->nw_out_blocked = 1;
     return NGHTTP2_ERR_WOULDBLOCK;
-
+  }
   return nwritten;
 }
 
@@ -964,6 +976,7 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
   struct stream_ctx *stream = H2_STREAM_CTX(data);
   int32_t stream_id = frame->hd.stream_id;
   CURLcode result;
+  size_t rbuflen;
   int rv;
 
   if(!stream) {
@@ -973,10 +986,10 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
 
   switch(frame->hd.type) {
   case NGHTTP2_DATA:
+    rbuflen = Curl_bufq_len(&stream->recvbuf);
     DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[DATA len=%zu pad=%zu], "
                   "buffered=%zu, window=%d/%d",
-                  stream_id, frame->hd.length, frame->data.padlen,
-                  Curl_bufq_len(&stream->recvbuf),
+                  stream_id, frame->hd.length, frame->data.padlen, rbuflen,
                   nghttp2_session_get_stream_effective_recv_data_length(
                     ctx->h2, stream->id),
                   nghttp2_session_get_stream_effective_local_window_size(
@@ -993,6 +1006,20 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf,
     if(frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
       drain_stream(cf, data, stream);
     }
+    else if(rbuflen > stream->local_window_size) {
+      int32_t wsize = nghttp2_session_get_stream_local_window_size(
+                        ctx->h2, stream->id);
+      if(wsize > 0 && (uint32_t)wsize != stream->local_window_size) {
+        /* H2 flow control is not absolute, as the server might not have the
+         * same view, yet. When we recieve more than we want, we enforce
+         * the local window size again to make nghttp2 send WINDOW_UPATEs
+         * accordingly. */
+        nghttp2_session_set_local_window_size(ctx->h2,
+                                              NGHTTP2_FLAG_NONE,
+                                              stream->id,
+                                              stream->local_window_size);
+      }
+    }
     break;
   case NGHTTP2_HEADERS:
     DEBUGF(LOG_CF(data, cf, "[h2sid=%d] FRAME[HEADERS]", stream_id));
@@ -1095,6 +1122,21 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
                       ctx->max_concurrent_streams));
         multi_connchanged(data->multi);
       }
+      /* Since the initial stream window is 64K, a request might be on HOLD,
+       * due to exhaustion. The (initial) SETTINGS may announce a much larger
+       * 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)) {
+        struct stream_ctx *stream = H2_STREAM_CTX(data);
+        data->req.keepon &= ~KEEP_SEND_HOLD;
+        if(stream) {
+          drain_stream(cf, data, stream);
+          DEBUGF(LOG_CF(data, cf, "[h2sid=%d] un-holding after SETTINGS",
+                        stream_id));
+        }
+      }
       break;
     }
     case NGHTTP2_GOAWAY:
@@ -1448,8 +1490,8 @@ static ssize_t req_body_read_callback(nghttp2_session *session,
   if(nread > 0 && stream->upload_left != -1)
     stream->upload_left -= nread;
 
-  DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] req_body_read(len=%zu) left=%zd"
-                " -> %zd, %d",
+  DEBUGF(LOG_CF(data_s, cf, "[h2sid=%d] req_body_read(len=%zu) left=%"
+                            CURL_FORMAT_CURL_OFF_T " -> %zd, %d",
                 stream_id, length, stream->upload_left, nread, result));
 
   if(stream->upload_left == 0)
@@ -1555,11 +1597,6 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
     *err = CURLE_SEND_ERROR; /* trigger Curl_retry_request() later */
     return -1;
   }
-  else if(stream->reset) {
-    failf(data, "HTTP/2 stream %u was reset", stream->id);
-    *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
-    return -1;
-  }
   else if(stream->error != NGHTTP2_NO_ERROR) {
     failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)",
           stream->id, nghttp2_http2_strerror(stream->error),
@@ -1567,6 +1604,11 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
     *err = CURLE_HTTP2_STREAM;
     return -1;
   }
+  else if(stream->reset) {
+    failf(data, "HTTP/2 stream %u was reset", stream->id);
+    *err = stream->bodystarted? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
+    return -1;
+  }
 
   if(!stream->bodystarted) {
     failf(data, "HTTP/2 stream %u was closed cleanly, but before getting "
@@ -1659,9 +1701,10 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
   struct stream_ctx *stream = H2_STREAM_CTX(data);
   int rv = 0;
 
-  if((sweight_wanted(data) != sweight_in_effect(data)) ||
-     (data->set.priority.exclusive != data->state.priority.exclusive) ||
-     (data->set.priority.parent != data->state.priority.parent) ) {
+  if(stream && stream->id > 0 &&
+     ((sweight_wanted(data) != sweight_in_effect(data)) ||
+      (data->set.priority.exclusive != data->state.priority.exclusive) ||
+      (data->set.priority.parent != data->state.priority.parent)) ) {
     /* send new weight and/or dependency */
     nghttp2_priority_spec pri_spec;
 
@@ -1675,7 +1718,8 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
       goto out;
   }
 
-  while(!rv && nghttp2_session_want_write(ctx->h2))
+  ctx->nw_out_blocked = 0;
+  while(!rv && !ctx->nw_out_blocked && nghttp2_session_want_write(ctx->h2))
     rv = nghttp2_session_send(ctx->h2);
 
 out:
@@ -1739,7 +1783,7 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
 
   /* Process network input buffer fist */
   if(!Curl_bufq_is_empty(&ctx->inbufq)) {
-    DEBUGF(LOG_CF(data, cf, "Process %zd bytes in connection buffer",
+    DEBUGF(LOG_CF(data, cf, "Process %zu bytes in connection buffer",
                   Curl_bufq_len(&ctx->inbufq)));
     if(h2_process_pending_input(cf, data, &result) < 0)
       return result;
@@ -1760,7 +1804,7 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
     }
 
     nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result);
-    /* DEBUGF(LOG_CF(data, cf, "read %zd bytes nw data -> %zd, %d",
+    /* DEBUGF(LOG_CF(data, cf, "read %zu bytes nw data -> %zd, %d",
                   Curl_bufq_len(&ctx->inbufq), nread, result)); */
     if(nread < 0) {
       if(result != CURLE_AGAIN) {
@@ -1836,7 +1880,7 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
 
 out:
   result = h2_progress_egress(cf, data);
-  if(result) {
+  if(result && result != CURLE_AGAIN) {
     *err = result;
     nread = -1;
   }
@@ -1864,7 +1908,8 @@ static ssize_t h2_submit(struct stream_ctx **pstream,
   struct h1_req_parser h1;
   struct dynhds h2_headers;
   nghttp2_nv *nva = NULL;
-  size_t nheader, i;
+  const void *body = NULL;
+  size_t nheader, bodylen, i;
   nghttp2_data_provider data_prd;
   int32_t stream_id;
   nghttp2_priority_spec pri_spec;
@@ -1929,8 +1974,8 @@ static ssize_t h2_submit(struct stream_ctx **pstream,
 
   h2_pri_spec(data, &pri_spec);
 
-  DEBUGF(LOG_CF(data, cf, "send request allowed %d (easy handle %p)",
-                nghttp2_session_check_request_allowed(ctx->h2), (void *)data));
+  DEBUGF(LOG_CF(data, cf, "send request allowed %d",
+                nghttp2_session_check_request_allowed(ctx->h2)));
 
   switch(data->state.httpreq) {
   case HTTPREQ_POST:
@@ -1966,9 +2011,35 @@ static ssize_t h2_submit(struct stream_ctx **pstream,
 
   DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) submit %s",
                 stream_id, len, data->state.url));
-  infof(data, "Using Stream ID: %u (easy handle %p)",
-        stream_id, (void *)data);
+  infof(data, "Using Stream ID: %u", stream_id);
   stream->id = stream_id;
+  stream->local_window_size = H2_STREAM_WINDOW_SIZE;
+  if(data->set.max_recv_speed) {
+    /* We are asked to only receive `max_recv_speed` bytes per second.
+     * Let's limit our stream window size around that, otherwise the server
+     * will send in large bursts only. We make the window 50% larger to
+     * allow for data in flight and avoid stalling. */
+    curl_off_t n = (((data->set.max_recv_speed - 1) / H2_CHUNK_SIZE) + 1);
+    n += CURLMAX((n/2), 1);
+    if(n < (H2_STREAM_WINDOW_SIZE / H2_CHUNK_SIZE) &&
+       n < (UINT_MAX / H2_CHUNK_SIZE)) {
+      stream->local_window_size = (uint32_t)n * H2_CHUNK_SIZE;
+    }
+  }
+
+  body = (const char *)buf + nwritten;
+  bodylen = len - nwritten;
+
+  if(bodylen) {
+    /* We have request body to send in DATA frame */
+    ssize_t n = Curl_bufq_write(&stream->sendbuf, body, bodylen, err);
+    if(n < 0) {
+      *err = CURLE_SEND_ERROR;
+      nwritten = -1;
+      goto out;
+    }
+    nwritten += n;
+  }
 
 out:
   DEBUGF(LOG_CF(data, cf, "[h2sid=%d] submit -> %zd, %d",
@@ -1982,17 +2053,13 @@ out:
 static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                           const void *buf, size_t len, CURLcode *err)
 {
-  /*
-   * Currently, we send request in this function, but this function is also
-   * used to send request body. It would be nice to add dedicated function for
-   * request.
-   */
   struct cf_h2_ctx *ctx = cf->ctx;
   struct stream_ctx *stream = H2_STREAM_CTX(data);
   struct cf_call_data save;
   int rv;
   ssize_t nwritten;
   CURLcode result;
+  int blocked = 0;
 
   CF_DATA_SAVE(save, cf, data);
 
@@ -2007,18 +2074,35 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
       nwritten = http2_handle_stream_close(cf, data, stream, err);
       goto out;
     }
-    /* If stream_id != -1, we have dispatched request HEADERS, and now
-       are going to send or sending request body in DATA frame */
-    nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err);
-    if(nwritten < 0) {
-      if(*err != CURLE_AGAIN)
+    else if(stream->upload_blocked_len) {
+      /* the data in `buf` has alread been submitted or added to the
+       * buffers, but have been EAGAINed on the last invocation. */
+      DEBUGASSERT(len >= stream->upload_blocked_len);
+      if(len < stream->upload_blocked_len) {
+        /* Did we get called again with a smaller `len`? This should not
+         * happend. We are not prepared to handle that. */
+        failf(data, "HTTP/2 send again with decreased length");
+        *err = CURLE_HTTP2;
+        nwritten = -1;
         goto out;
-      nwritten = 0;
+      }
+      nwritten = (ssize_t)stream->upload_blocked_len;
+      stream->upload_blocked_len = 0;
+    }
+    else {
+      /* If stream_id != -1, we have dispatched request HEADERS and
+       * optionally request body, and now are going to send or sending
+       * more request body in DATA frame */
+      nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err);
+      if(nwritten < 0) {
+        if(*err != CURLE_AGAIN)
+          goto out;
+        nwritten = 0;
+      }
     }
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] bufq_write(len=%zu) -> %zd, %d",
-                  stream->id, len, nwritten, *err));
 
     if(!Curl_bufq_is_empty(&stream->sendbuf)) {
+      /* req body data is buffered, resume the potentially suspended stream */
       rv = nghttp2_session_resume_data(ctx->h2, stream->id);
       if(nghttp2_is_fatal(rv)) {
         *err = CURLE_SEND_ERROR;
@@ -2026,104 +2110,99 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
         goto out;
       }
     }
-
-    result = h2_progress_ingress(cf, data);
-    if(result) {
-      *err = result;
-      nwritten = -1;
-      goto out;
-    }
-
-    result = h2_progress_egress(cf, data);
-    if(result) {
-      *err = result;
-      nwritten = -1;
-      goto out;
-    }
-
-    if(should_close_session(ctx)) {
-      if(stream->closed) {
-        nwritten = http2_handle_stream_close(cf, data, stream, err);
-      }
-      else {
-        DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session"));
-        *err = CURLE_HTTP2;
-        nwritten = -1;
-      }
-      goto out;
-    }
-
-    if(!nwritten) {
-      size_t rwin = nghttp2_session_get_stream_remote_window_size(ctx->h2,
-                                                          stream->id);
-      DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send: win %u/%zu",
-             stream->id,
-             nghttp2_session_get_remote_window_size(ctx->h2), rwin));
-      if(rwin == 0) {
-        /* We cannot upload more as the stream's remote window size
-         * is 0. We need to receive WIN_UPDATEs before we can continue.
-         */
-        data->req.keepon |= KEEP_SEND_HOLD;
-        DEBUGF(LOG_CF(data, cf, "[h2sid=%d] holding send as remote flow "
-               "window is exhausted", stream->id));
-      }
-      nwritten = -1;
-      *err = CURLE_AGAIN;
-    }
-    /* handled writing BODY for open stream. */
-    goto out;
   }
   else {
     nwritten = h2_submit(&stream, cf, data, buf, len, err);
     if(nwritten < 0) {
       goto out;
     }
+    DEBUGASSERT(stream);
+  }
 
-    result = h2_progress_ingress(cf, data);
-    if(result) {
-      *err = result;
-      nwritten = -1;
-      goto out;
+  /* Call the nghttp2 send loop and flush to write ALL buffered data,
+   * headers and/or request body completely out to the network */
+  result = h2_progress_egress(cf, data);
+  /* if the stream has been closed in egress handling (nghttp2 does that
+   * when it does not like the headers, for example */
+  if(stream && stream->closed) {
+    nwritten = http2_handle_stream_close(cf, data, stream, err);
+    goto out;
+  }
+  else if(result == CURLE_AGAIN) {
+    blocked = 1;
+  }
+  else if(result) {
+    *err = result;
+    nwritten = -1;
+    goto out;
+  }
+  else if(stream && !Curl_bufq_is_empty(&stream->sendbuf)) {
+    /* although we wrote everything that nghttp2 wants to send now,
+     * there is data left in our stream send buffer unwritten. This may
+     * be due to the stream's HTTP/2 flow window being exhausted. */
+    blocked = 1;
+  }
+
+  if(stream && blocked) {
+    /* Unable to send all data, due to connection blocked or H2 window
+     * exhaustion. Data is left in our stream buffer, or nghttp2's internal
+     * 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;
+      DEBUGF(LOG_CF(data, cf, "[h2sid=%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. */
+    stream->upload_blocked_len = nwritten;
+    DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) BLOCK: win %u/%zu "
+           "blocked_len=%zu",
+           stream->id, len,
+           nghttp2_session_get_remote_window_size(ctx->h2), rwin,
+           nwritten));
+    *err = CURLE_AGAIN;
+    nwritten = -1;
+    goto out;
+  }
+  else if(should_close_session(ctx)) {
+    /* nghttp2 thinks this session is done. If the stream has not been
+     * closed, this is an error state for out transfer */
+    if(stream->closed) {
+      nwritten = http2_handle_stream_close(cf, data, stream, err);
     }
-
-    result = h2_progress_egress(cf, data);
-    if(result) {
-      *err = result;
+    else {
+      DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session"));
+      *err = CURLE_HTTP2;
       nwritten = -1;
-      goto out;
-    }
-
-    if(should_close_session(ctx)) {
-      if(stream->closed) {
-        nwritten = http2_handle_stream_close(cf, data, stream, err);
-      }
-      else {
-        DEBUGF(LOG_CF(data, cf, "send: nothing to do in this session"));
-        *err = CURLE_HTTP2;
-        nwritten = -1;
-      }
-      goto out;
     }
   }
 
 out:
   if(stream) {
     DEBUGF(LOG_CF(data, cf, "[h2sid=%d] cf_send(len=%zu) -> %zd, %d, "
-                  "buffered=%zu, upload_left=%zu, stream-window=%d, "
-                  "connection-window=%d",
+                  "upload_left=%" CURL_FORMAT_CURL_OFF_T ", "
+                  "h2 windows %d-%d (stream-conn), "
+                  "buffers %zu-%zu (stream-conn)",
                   stream->id, len, nwritten, *err,
-                  Curl_bufq_len(&stream->sendbuf),
                   (ssize_t)stream->upload_left,
                   nghttp2_session_get_stream_remote_window_size(
                     ctx->h2, stream->id),
-                  nghttp2_session_get_remote_window_size(ctx->h2)));
-    drain_stream(cf, data, stream);
+                  nghttp2_session_get_remote_window_size(ctx->h2),
+                  Curl_bufq_len(&stream->sendbuf),
+                  Curl_bufq_len(&ctx->outbufq)));
   }
   else {
     DEBUGF(LOG_CF(data, cf, "cf_send(len=%zu) -> %zd, %d, "
-                  "connection-window=%d",
+                  "connection-window=%d, nw_send_buffer(%zu)",
                   len, nwritten, *err,
-                  nghttp2_session_get_remote_window_size(ctx->h2)));
+                  nghttp2_session_get_remote_window_size(ctx->h2),
+                  Curl_bufq_len(&ctx->outbufq)));
   }
   CF_DATA_RESTORE(cf, save);
   return nwritten;
@@ -2241,8 +2320,7 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf,
 
   DEBUGASSERT(data);
   if(ctx && ctx->h2 && stream) {
-    uint32_t window = !pause * H2_STREAM_WINDOW_SIZE;
-    CURLcode result;
+    uint32_t window = pause? 0 : stream->local_window_size;
 
     int rv = nghttp2_session_set_local_window_size(ctx->h2,
                                                    NGHTTP2_FLAG_NONE,
@@ -2257,10 +2335,8 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf,
     if(!pause)
       drain_stream(cf, data, stream);
 
-    /* make sure the window update gets sent */
-    result = h2_progress_egress(cf, data);
-    if(result)
-      return result;
+    /* attempt to send the window update */
+    (void)h2_progress_egress(cf, data);
 
     if(!pause) {
       /* Unpausing a h2 transfer, requires it to be run again. The server
@@ -2510,7 +2586,7 @@ CURLcode Curl_http2_switch(struct Curl_easy *data,
   CURLcode result;
 
   DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
-  DEBUGF(infof(data, DMSGI(data, sockindex, "switching to HTTP/2")));
+  DEBUGF(infof(data, "switching to HTTP/2"));
 
   result = http2_cfilter_add(&cf, data, conn, sockindex);
   if(result)
@@ -2569,7 +2645,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
   CURLcode result;
 
   DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
-  DEBUGF(infof(data, DMSGI(data, sockindex, "upgrading to HTTP/2")));
+  DEBUGF(infof(data, "upgrading to HTTP/2"));
   DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED);
 
   result = http2_cfilter_add(&cf, data, conn, sockindex);

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

@@ -71,7 +71,7 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
 
   DEBUGF(LOG_CF(data, cf, "connect"));
 connect_sub:
-  result = cf->next->cft->connect(cf->next, data, blocking, done);
+  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
   if(result || !*done)
     return result;
 
@@ -181,7 +181,7 @@ static void http_proxy_cf_close(struct Curl_cfilter *cf,
     ctx->cf_protocol = NULL;
   }
   if(cf->next)
-    cf->next->cft->close(cf->next, data);
+    cf->next->cft->do_close(cf->next, data);
 }
 
 

+ 55 - 41
Utilities/cmcurl/lib/imap.c

@@ -385,11 +385,11 @@ static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out)
 
 /***********************************************************************
  *
- * state()
+ * imap_state()
  *
  * This is the ONLY way to change IMAP state!
  */
-static void state(struct Curl_easy *data, imapstate newstate)
+static void imap_state(struct Curl_easy *data, imapstate newstate)
 {
   struct imap_conn *imapc = &data->conn->proto.imapc;
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
@@ -441,7 +441,7 @@ static CURLcode imap_perform_capability(struct Curl_easy *data,
   result = imap_sendf(data, "CAPABILITY");
 
   if(!result)
-    state(data, IMAP_CAPABILITY);
+    imap_state(data, IMAP_CAPABILITY);
 
   return result;
 }
@@ -458,7 +458,7 @@ static CURLcode imap_perform_starttls(struct Curl_easy *data)
   CURLcode result = imap_sendf(data, "STARTTLS");
 
   if(!result)
-    state(data, IMAP_STARTTLS);
+    imap_state(data, IMAP_STARTTLS);
 
   return result;
 }
@@ -487,7 +487,7 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
   if(!result) {
     imapc->ssldone = ssldone;
     if(imapc->state != IMAP_UPGRADETLS)
-      state(data, IMAP_UPGRADETLS);
+      imap_state(data, IMAP_UPGRADETLS);
 
     if(imapc->ssldone) {
       imap_to_imaps(conn);
@@ -514,7 +514,7 @@ static CURLcode imap_perform_login(struct Curl_easy *data,
   /* Check we have a username and password to authenticate with and end the
      connect phase if we don't */
   if(!data->state.aptr.user) {
-    state(data, IMAP_STOP);
+    imap_state(data, IMAP_STOP);
 
     return result;
   }
@@ -531,7 +531,7 @@ static CURLcode imap_perform_login(struct Curl_easy *data,
   free(passwd);
 
   if(!result)
-    state(data, IMAP_LOGIN);
+    imap_state(data, IMAP_LOGIN);
 
   return result;
 }
@@ -615,7 +615,7 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data,
      with and end the connect phase if we don't */
   if(imapc->preauth ||
      !Curl_sasl_can_authenticate(&imapc->sasl, data)) {
-    state(data, IMAP_STOP);
+    imap_state(data, IMAP_STOP);
     return result;
   }
 
@@ -624,7 +624,7 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data,
 
   if(!result) {
     if(progress == SASL_INPROGRESS)
-      state(data, IMAP_AUTHENTICATE);
+      imap_state(data, IMAP_AUTHENTICATE);
     else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
       /* Perform clear text authentication */
       result = imap_perform_login(data, conn);
@@ -667,7 +667,7 @@ static CURLcode imap_perform_list(struct Curl_easy *data)
   }
 
   if(!result)
-    state(data, IMAP_LIST);
+    imap_state(data, IMAP_LIST);
 
   return result;
 }
@@ -707,7 +707,7 @@ static CURLcode imap_perform_select(struct Curl_easy *data)
   free(mailbox);
 
   if(!result)
-    state(data, IMAP_SELECT);
+    imap_state(data, IMAP_SELECT);
 
   return result;
 }
@@ -749,7 +749,7 @@ static CURLcode imap_perform_fetch(struct Curl_easy *data)
     return CURLE_URL_MALFORMAT;
   }
   if(!result)
-    state(data, IMAP_FETCH);
+    imap_state(data, IMAP_FETCH);
 
   return result;
 }
@@ -820,7 +820,7 @@ static CURLcode imap_perform_append(struct Curl_easy *data)
   free(mailbox);
 
   if(!result)
-    state(data, IMAP_APPEND);
+    imap_state(data, IMAP_APPEND);
 
   return result;
 }
@@ -846,7 +846,7 @@ static CURLcode imap_perform_search(struct Curl_easy *data)
   result = imap_sendf(data, "SEARCH %s", imap->query);
 
   if(!result)
-    state(data, IMAP_SEARCH);
+    imap_state(data, IMAP_SEARCH);
 
   return result;
 }
@@ -863,7 +863,7 @@ static CURLcode imap_perform_logout(struct Curl_easy *data)
   CURLcode result = imap_sendf(data, "LOGOUT");
 
   if(!result)
-    state(data, IMAP_LOGOUT);
+    imap_state(data, IMAP_LOGOUT);
 
   return result;
 }
@@ -1017,7 +1017,7 @@ static CURLcode imap_state_auth_resp(struct Curl_easy *data,
   if(!result)
     switch(progress) {
     case SASL_DONE:
-      state(data, IMAP_STOP);  /* Authenticated */
+      imap_state(data, IMAP_STOP);  /* Authenticated */
       break;
     case SASL_IDLE:            /* No mechanism left after cancellation */
       if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
@@ -1049,7 +1049,7 @@ static CURLcode imap_state_login_resp(struct Curl_easy *data,
   }
   else
     /* End of connect phase */
-    state(data, IMAP_STOP);
+    imap_state(data, IMAP_STOP);
 
   return result;
 }
@@ -1075,7 +1075,7 @@ static CURLcode imap_state_listsearch_resp(struct Curl_easy *data,
     result = CURLE_QUOTE_ERROR;
   else
     /* End of DO phase */
-    state(data, IMAP_STOP);
+    imap_state(data, IMAP_STOP);
 
   return result;
 }
@@ -1143,7 +1143,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
 
   if(imapcode != '*') {
     Curl_pgrsSetDownloadSize(data, -1);
-    state(data, IMAP_STOP);
+    imap_state(data, IMAP_STOP);
     return CURLE_REMOTE_FILE_NOT_FOUND;
   }
 
@@ -1178,7 +1178,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
 
       if(!chunk) {
         /* no size, we're done with the data */
-        state(data, IMAP_STOP);
+        imap_state(data, IMAP_STOP);
         return CURLE_OK;
       }
       result = Curl_client_write(data, CLIENTWRITE_BODY, pp->cache, chunk);
@@ -1224,7 +1224,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
   }
 
   /* End of DO phase */
-  state(data, IMAP_STOP);
+  imap_state(data, IMAP_STOP);
 
   return result;
 }
@@ -1242,7 +1242,7 @@ static CURLcode imap_state_fetch_final_resp(struct Curl_easy *data,
     result = CURLE_WEIRD_SERVER_REPLY;
   else
     /* End of DONE phase */
-    state(data, IMAP_STOP);
+    imap_state(data, IMAP_STOP);
 
   return result;
 }
@@ -1265,7 +1265,7 @@ static CURLcode imap_state_append_resp(struct Curl_easy *data, int imapcode,
     Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
 
     /* End of DO phase */
-    state(data, IMAP_STOP);
+    imap_state(data, IMAP_STOP);
   }
 
   return result;
@@ -1284,7 +1284,7 @@ static CURLcode imap_state_append_final_resp(struct Curl_easy *data,
     result = CURLE_UPLOAD_FAILED;
   else
     /* End of DONE phase */
-    state(data, IMAP_STOP);
+    imap_state(data, IMAP_STOP);
 
   return result;
 }
@@ -1372,7 +1372,7 @@ static CURLcode imap_statemachine(struct Curl_easy *data,
       /* fallthrough, just stop! */
     default:
       /* internal error */
-      state(data, IMAP_STOP);
+      imap_state(data, IMAP_STOP);
       break;
     }
   } while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
@@ -1475,7 +1475,7 @@ static CURLcode imap_connect(struct Curl_easy *data, bool *done)
     return result;
 
   /* Start off waiting for the server greeting response */
-  state(data, IMAP_SERVERGREET);
+  imap_state(data, IMAP_SERVERGREET);
 
   /* Start off with an response id of '*' */
   strcpy(imapc->resptag, "*");
@@ -1516,12 +1516,12 @@ static CURLcode imap_done(struct Curl_easy *data, CURLcode status,
     /* Handle responses after FETCH or APPEND transfer has finished */
 
     if(!data->state.upload && data->set.mimepost.kind == MIMEKIND_NONE)
-      state(data, IMAP_FETCH_FINAL);
+      imap_state(data, IMAP_FETCH_FINAL);
     else {
       /* End the APPEND command first by sending an empty line */
       result = Curl_pp_sendf(data, &conn->proto.imapc.pp, "%s", "");
       if(!result)
-        state(data, IMAP_APPEND_FINAL);
+        imap_state(data, IMAP_APPEND_FINAL);
     }
 
     /* Run the state-machine */
@@ -1777,7 +1777,7 @@ static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...)
 
   /* Calculate the tag based on the connection ID and command ID */
   msnprintf(imapc->resptag, sizeof(imapc->resptag), "%c%03d",
-            'A' + curlx_sltosi(data->conn->connection_id % 26),
+            'A' + curlx_sltosi((long)(data->conn->connection_id % 26)),
             ++imapc->cmdid);
 
   /* start with a blank buffer */
@@ -1925,6 +1925,7 @@ static CURLcode imap_parse_url_options(struct connectdata *conn)
   CURLcode result = CURLE_OK;
   struct imap_conn *imapc = &conn->proto.imapc;
   const char *ptr = conn->options;
+  bool prefer_login = false;
 
   while(!result && ptr && *ptr) {
     const char *key = ptr;
@@ -1938,26 +1939,39 @@ static CURLcode imap_parse_url_options(struct connectdata *conn)
     while(*ptr && *ptr != ';')
       ptr++;
 
-    if(strncasecompare(key, "AUTH=", 5))
+    if(strncasecompare(key, "AUTH=+LOGIN", 11)) {
+      /* User prefers plaintext LOGIN over any SASL, including SASL LOGIN */
+      prefer_login = true;
+      imapc->sasl.prefmech = SASL_AUTH_NONE;
+    }
+    else if(strncasecompare(key, "AUTH=", 5)) {
+      prefer_login = false;
       result = Curl_sasl_parse_url_auth_option(&imapc->sasl,
                                                value, ptr - value);
-    else
+    }
+    else {
+      prefer_login = false;
       result = CURLE_URL_MALFORMAT;
+    }
 
     if(*ptr == ';')
       ptr++;
   }
 
-  switch(imapc->sasl.prefmech) {
-  case SASL_AUTH_NONE:
-    imapc->preftype = IMAP_TYPE_NONE;
-    break;
-  case SASL_AUTH_DEFAULT:
-    imapc->preftype = IMAP_TYPE_ANY;
-    break;
-  default:
-    imapc->preftype = IMAP_TYPE_SASL;
-    break;
+  if(prefer_login)
+    imapc->preftype = IMAP_TYPE_CLEARTEXT;
+  else {
+    switch(imapc->sasl.prefmech) {
+    case SASL_AUTH_NONE:
+      imapc->preftype = IMAP_TYPE_NONE;
+      break;
+    case SASL_AUTH_DEFAULT:
+      imapc->preftype = IMAP_TYPE_ANY;
+      break;
+    default:
+      imapc->preftype = IMAP_TYPE_SASL;
+      break;
+    }
   }
 
   return result;

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

@@ -261,7 +261,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
     }
     /* We pass NULL as |output_name_type| to avoid a leak. */
     gss_display_name(&min, gssname, &output_buffer, NULL);
-    infof(data, "Trying against %s", output_buffer.value);
+    infof(data, "Trying against %s", (char *)output_buffer.value);
     gssresp = GSS_C_NO_BUFFER;
     *context = GSS_C_NO_CONTEXT;
 

+ 8 - 0
Utilities/cmcurl/lib/ldap.c

@@ -50,6 +50,14 @@
 #endif
 
 #ifdef USE_WIN32_LDAP           /* Use Windows LDAP implementation. */
+# ifdef _MSC_VER
+#  pragma warning(push)
+#  pragma warning(disable: 4201)
+# endif
+# include <subauth.h>  /* for [P]UNICODE_STRING */
+# ifdef _MSC_VER
+#  pragma warning(pop)
+# endif
 # include <winldap.h>
 # ifndef LDAP_VENDOR_NAME
 #  error Your Platform SDK is NOT sufficient for LDAP support! \

+ 62 - 0
Utilities/cmcurl/lib/macos.c

@@ -0,0 +1,62 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if defined(__APPLE__)
+
+#if !defined(TARGET_OS_OSX) || TARGET_OS_OSX
+
+#include <curl/curl.h>
+
+#include "macos.h"
+
+#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES)
+#include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
+#endif
+
+CURLcode Curl_macos_init(void)
+{
+#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES)
+  {
+    /*
+     * The automagic conversion from IPv4 literals to IPv6 literals only
+     * works if the SCDynamicStoreCopyProxies system function gets called
+     * first. As Curl currently doesn't support system-wide HTTP proxies, we
+     * therefore don't use any value this function might return.
+     *
+     * This function is only available on a macOS and is not needed for
+     * IPv4-only builds, hence the conditions above.
+     */
+    CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL);
+    if(dict)
+      CFRelease(dict);
+  }
+#endif
+  return CURLE_OK;
+}
+
+#endif /* TARGET_OS_OSX */
+
+#endif /* __APPLE__ */

+ 38 - 0
Utilities/cmcurl/lib/macos.h

@@ -0,0 +1,38 @@
+#ifndef HEADER_CURL_MACOS_H
+#define HEADER_CURL_MACOS_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#if defined(__APPLE__) && (!defined(TARGET_OS_OSX) || TARGET_OS_OSX)
+
+CURLcode Curl_macos_init(void);
+
+#else
+
+#define Curl_macos_init() CURLE_OK
+
+#endif
+
+#endif /* HEADER_CURL_MACOS_H */

+ 8 - 8
Utilities/cmcurl/lib/mime.c

@@ -84,7 +84,7 @@ static const struct mime_encoder encoders[] = {
 };
 
 /* Base64 encoding table */
-static const char base64[] =
+static const char base64enc[] =
   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
 /* Quoted-printable character class table.
@@ -469,10 +469,10 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
     i = st->buf[st->bufbeg++] & 0xFF;
     i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF);
     i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF);
-    *ptr++ = base64[(i >> 18) & 0x3F];
-    *ptr++ = base64[(i >> 12) & 0x3F];
-    *ptr++ = base64[(i >> 6) & 0x3F];
-    *ptr++ = base64[i & 0x3F];
+    *ptr++ = base64enc[(i >> 18) & 0x3F];
+    *ptr++ = base64enc[(i >> 12) & 0x3F];
+    *ptr++ = base64enc[(i >> 6) & 0x3F];
+    *ptr++ = base64enc[i & 0x3F];
     cursize += 4;
     st->pos += 4;
     size -= 4;
@@ -496,10 +496,10 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
           i = (st->buf[st->bufbeg + 1] & 0xFF) << 8;
 
         i |= (st->buf[st->bufbeg] & 0xFF) << 16;
-        ptr[0] = base64[(i >> 18) & 0x3F];
-        ptr[1] = base64[(i >> 12) & 0x3F];
+        ptr[0] = base64enc[(i >> 18) & 0x3F];
+        ptr[1] = base64enc[(i >> 12) & 0x3F];
         if(++st->bufbeg != st->bufend) {
-          ptr[2] = base64[(i >> 6) & 0x3F];
+          ptr[2] = base64enc[(i >> 6) & 0x3F];
           st->bufbeg++;
         }
         cursize += 4;

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

@@ -636,7 +636,7 @@ MQTT_SUBACK_COMING:
 
     /* -- switched state -- */
     remlen = mq->remaining_length;
-    infof(data, "Remaining length: %zd bytes", remlen);
+    infof(data, "Remaining length: %zu bytes", remlen);
     if(data->set.max_filesize &&
        (curl_off_t)remlen > data->set.max_filesize) {
       failf(data, "Maximum file size exceeded");

+ 34 - 23
Utilities/cmcurl/lib/multi.c

@@ -112,7 +112,7 @@ static CURLMcode multi_timeout(struct Curl_multi *multi,
 static void process_pending_handles(struct Curl_multi *multi);
 
 #ifdef DEBUGBUILD
-static const char * const statename[]={
+static const char * const multi_statename[]={
   "INIT",
   "PENDING",
   "CONNECT",
@@ -194,15 +194,10 @@ static void mstate(struct Curl_easy *data, CURLMstate state
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
   if(data->mstate >= MSTATE_PENDING &&
      data->mstate < MSTATE_COMPLETED) {
-    long connection_id = -5000;
-
-    if(data->conn)
-      connection_id = data->conn->connection_id;
-
     infof(data,
-          "STATE: %s => %s handle %p; line %d (connection #%ld)",
-          statename[oldstate], statename[data->mstate],
-          (void *)data, lineno, connection_id);
+          "STATE: %s => %s handle %p; line %d",
+          multi_statename[oldstate], multi_statename[data->mstate],
+          (void *)data, lineno);
   }
 #endif
 
@@ -464,6 +459,20 @@ struct Curl_multi *curl_multi_init(void)
                            CURL_DNS_HASH_SIZE);
 }
 
+#ifdef DEBUGBUILD
+static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data)
+{
+  if(!multi->warned) {
+    infof(data, "!!! WARNING !!!");
+    infof(data, "This is a debug build of libcurl, "
+          "do not use in production.");
+    multi->warned = true;
+  }
+}
+#else
+#define multi_warn_debug(x,y) Curl_nop_stmt
+#endif
+
 /* returns TRUE if the easy handle is supposed to be present in the main link
    list */
 static bool in_main_list(struct Curl_easy *data)
@@ -623,8 +632,14 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
     data->set.server_response_timeout;
   data->state.conn_cache->closure_handle->set.no_signal =
     data->set.no_signal;
+  data->id = data->state.conn_cache->next_easy_id++;
+  if(data->state.conn_cache->next_easy_id <= 0)
+    data->state.conn_cache->next_easy_id = 0;
   CONNCACHE_UNLOCK(data);
 
+  multi_warn_debug(multi, data);
+  infof(data, "processing: %s", data->state.url);
+
   return CURLM_OK;
 }
 
@@ -742,6 +757,7 @@ static CURLcode multi_done(struct Curl_easy *data,
      but currently we have no such detail knowledge.
   */
 
+  data->state.recent_conn_id = conn->connection_id;
   if((data->set.reuse_forbid
 #if defined(USE_NTLM)
       && !(conn->http_ntlm_state == NTLMSTATE_TYPE2 ||
@@ -753,8 +769,9 @@ static CURLcode multi_done(struct Curl_easy *data,
 #endif
      ) || conn->bits.close
        || (premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) {
-    DEBUGF(infof(data, "multi_done, not re-using connection=%ld, forbid=%d"
-                 ", close=%d, premature=%d, conn_multiplex=%d",
+    DEBUGF(infof(data, "multi_done, not re-using connection=%"
+                       CURL_FORMAT_CURL_OFF_T ", forbid=%d"
+                       ", close=%d, premature=%d, conn_multiplex=%d",
                  conn->connection_id,
                  data->set.reuse_forbid, conn->bits.close, premature,
                  Curl_conn_is_multiplex(conn, FIRSTSOCKET)));
@@ -774,15 +791,16 @@ static CURLcode multi_done(struct Curl_easy *data,
       conn->bits.conn_to_host ? conn->conn_to_host.dispname :
       conn->host.dispname;
     /* create string before returning the connection */
-    long connection_id = conn->connection_id;
+    curl_off_t connection_id = conn->connection_id;
     msnprintf(buffer, sizeof(buffer),
-              "Connection #%ld to host %s left intact",
+              "Connection #%" CURL_FORMAT_CURL_OFF_T " to host %s left intact",
               connection_id, host);
     /* the connection is no longer in use by this transfer */
     CONNCACHE_UNLOCK(data);
     if(Curl_conncache_return_conn(data, conn)) {
       /* remember the most recently used connection */
       data->state.lastconnect_id = connection_id;
+      data->state.recent_conn_id = connection_id;
       infof(data, "%s", buffer);
     }
     else
@@ -1895,14 +1913,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
     multistate(data, MSTATE_COMPLETED);
   }
 
-#ifdef DEBUGBUILD
-  if(!multi->warned) {
-    infof(data, "!!! WARNING !!!");
-    infof(data, "This is a debug build of libcurl, "
-          "do not use in production.");
-    multi->warned = true;
-  }
-#endif
+  multi_warn_debug(multi, data);
 
   do {
     /* A "stream" here is a logical stream if the protocol can handle that
@@ -3690,7 +3701,7 @@ void Curl_expire_clear(struct Curl_easy *data)
     }
 
 #ifdef DEBUGBUILD
-    infof(data, "Expire cleared (transfer %p)", data);
+    infof(data, "Expire cleared");
 #endif
     nowp->tv_sec = 0;
     nowp->tv_usec = 0;
@@ -3798,7 +3809,7 @@ void Curl_multi_dump(struct Curl_multi *multi)
       /* only display handles that are not completed */
       fprintf(stderr, "handle %p, state %s, %d sockets\n",
               (void *)data,
-              statename[data->mstate], data->numsocks);
+              multi_statename[data->mstate], data->numsocks);
       for(i = 0; i < data->numsocks; i++) {
         curl_socket_t s = data->sockets[i];
         struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);

+ 22 - 22
Utilities/cmcurl/lib/pop3.c

@@ -282,11 +282,11 @@ static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out)
 
 /***********************************************************************
  *
- * state()
+ * pop3_state()
  *
  * This is the ONLY way to change POP3 state!
  */
-static void state(struct Curl_easy *data, pop3state newstate)
+static void pop3_state(struct Curl_easy *data, pop3state newstate)
 {
   struct pop3_conn *pop3c = &data->conn->proto.pop3c;
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
@@ -335,7 +335,7 @@ static CURLcode pop3_perform_capa(struct Curl_easy *data,
   result = Curl_pp_sendf(data, &pop3c->pp, "%s", "CAPA");
 
   if(!result)
-    state(data, POP3_CAPA);
+    pop3_state(data, POP3_CAPA);
 
   return result;
 }
@@ -353,7 +353,7 @@ static CURLcode pop3_perform_starttls(struct Curl_easy *data,
   CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "STLS");
 
   if(!result)
-    state(data, POP3_STARTTLS);
+    pop3_state(data, POP3_STARTTLS);
 
   return result;
 }
@@ -383,7 +383,7 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data,
   if(!result) {
     pop3c->ssldone = ssldone;
     if(pop3c->state != POP3_UPGRADETLS)
-      state(data, POP3_UPGRADETLS);
+      pop3_state(data, POP3_UPGRADETLS);
 
     if(pop3c->ssldone) {
       pop3_to_pop3s(conn);
@@ -408,7 +408,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data,
   /* Check we have a username and password to authenticate with and end the
      connect phase if we don't */
   if(!data->state.aptr.user) {
-    state(data, POP3_STOP);
+    pop3_state(data, POP3_STOP);
 
     return result;
   }
@@ -417,7 +417,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data,
   result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "USER %s",
                          conn->user ? conn->user : "");
   if(!result)
-    state(data, POP3_USER);
+    pop3_state(data, POP3_USER);
 
   return result;
 }
@@ -442,7 +442,7 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data,
   /* Check we have a username and password to authenticate with and end the
      connect phase if we don't */
   if(!data->state.aptr.user) {
-    state(data, POP3_STOP);
+    pop3_state(data, POP3_STOP);
 
     return result;
   }
@@ -468,7 +468,7 @@ static CURLcode pop3_perform_apop(struct Curl_easy *data,
   result = Curl_pp_sendf(data, &pop3c->pp, "APOP %s %s", conn->user, secret);
 
   if(!result)
-    state(data, POP3_APOP);
+    pop3_state(data, POP3_APOP);
 
   return result;
 }
@@ -552,7 +552,7 @@ static CURLcode pop3_perform_authentication(struct Curl_easy *data,
   /* Check we have enough data to authenticate with and end the
      connect phase if we don't */
   if(!Curl_sasl_can_authenticate(&pop3c->sasl, data)) {
-    state(data, POP3_STOP);
+    pop3_state(data, POP3_STOP);
     return result;
   }
 
@@ -562,7 +562,7 @@ static CURLcode pop3_perform_authentication(struct Curl_easy *data,
 
     if(!result)
       if(progress == SASL_INPROGRESS)
-        state(data, POP3_AUTH);
+        pop3_state(data, POP3_AUTH);
   }
 
   if(!result && progress == SASL_IDLE) {
@@ -620,7 +620,7 @@ static CURLcode pop3_perform_command(struct Curl_easy *data)
                             pop3->custom : command));
 
   if(!result)
-    state(data, POP3_COMMAND);
+    pop3_state(data, POP3_COMMAND);
 
   return result;
 }
@@ -638,7 +638,7 @@ static CURLcode pop3_perform_quit(struct Curl_easy *data,
   CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "QUIT");
 
   if(!result)
-    state(data, POP3_QUIT);
+    pop3_state(data, POP3_QUIT);
 
   return result;
 }
@@ -831,7 +831,7 @@ static CURLcode pop3_state_auth_resp(struct Curl_easy *data,
   if(!result)
     switch(progress) {
     case SASL_DONE:
-      state(data, POP3_STOP);  /* Authenticated */
+      pop3_state(data, POP3_STOP);  /* Authenticated */
       break;
     case SASL_IDLE:            /* No mechanism left after cancellation */
 #ifndef CURL_DISABLE_CRYPTO_AUTH
@@ -869,7 +869,7 @@ static CURLcode pop3_state_apop_resp(struct Curl_easy *data, int pop3code,
   }
   else
     /* End of connect phase */
-    state(data, POP3_STOP);
+    pop3_state(data, POP3_STOP);
 
   return result;
 }
@@ -892,7 +892,7 @@ static CURLcode pop3_state_user_resp(struct Curl_easy *data, int pop3code,
     result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "PASS %s",
                            conn->passwd ? conn->passwd : "");
   if(!result)
-    state(data, POP3_PASS);
+    pop3_state(data, POP3_PASS);
 
   return result;
 }
@@ -910,7 +910,7 @@ static CURLcode pop3_state_pass_resp(struct Curl_easy *data, int pop3code,
   }
   else
     /* End of connect phase */
-    state(data, POP3_STOP);
+    pop3_state(data, POP3_STOP);
 
   return result;
 }
@@ -929,7 +929,7 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data,
   (void)instate; /* no use for this yet */
 
   if(pop3code != '+') {
-    state(data, POP3_STOP);
+    pop3_state(data, POP3_STOP);
     return CURLE_WEIRD_SERVER_REPLY;
   }
 
@@ -967,7 +967,7 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data,
   }
 
   /* End of DO phase */
-  state(data, POP3_STOP);
+  pop3_state(data, POP3_STOP);
 
   return result;
 }
@@ -1037,12 +1037,12 @@ static CURLcode pop3_statemachine(struct Curl_easy *data,
       break;
 
     case POP3_QUIT:
-      state(data, POP3_STOP);
+      pop3_state(data, POP3_STOP);
       break;
 
     default:
       /* internal error */
-      state(data, POP3_STOP);
+      pop3_state(data, POP3_STOP);
       break;
     }
   } while(!result && pop3c->state != POP3_STOP && Curl_pp_moredata(pp));
@@ -1143,7 +1143,7 @@ static CURLcode pop3_connect(struct Curl_easy *data, bool *done)
     return result;
 
   /* Start off waiting for the server greeting response */
-  state(data, POP3_SERVERGREET);
+  pop3_state(data, POP3_SERVERGREET);
 
   result = pop3_multi_statemach(data, done);
 

+ 0 - 2
Utilities/cmcurl/lib/sendf.c

@@ -419,8 +419,6 @@ CURLcode Curl_read(struct Curl_easy *data,   /* transfer */
   *n += nread;
   result = CURLE_OK;
 out:
-  /* DEBUGF(infof(data, "Curl_read(handle=%p) -> %d, nread=%ld",
-        data, result, nread)); */
   return result;
 }
 

+ 10 - 1
Utilities/cmcurl/lib/setopt.c

@@ -1867,6 +1867,15 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
      */
     data->set.haproxyprotocol = (0 != va_arg(param, long)) ? TRUE : FALSE;
     break;
+  case CURLOPT_HAPROXY_CLIENT_IP:
+    /*
+     * Set the client IP to send through HAProxy PROXY protocol
+     */
+    result = Curl_setstropt(&data->set.str[STRING_HAPROXY_CLIENT_IP],
+                            va_arg(param, char *));
+    /* We enable implicitly the HAProxy protocol if we use this flag. */
+    data->set.haproxyprotocol = TRUE;
+    break;
 #endif
   case CURLOPT_INTERFACE:
     /*
@@ -2711,7 +2720,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     /* Set the list of mail recipients */
     data->set.mail_rcpt = va_arg(param, struct curl_slist *);
     break;
-  case CURLOPT_MAIL_RCPT_ALLLOWFAILS:
+  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;
     break;

+ 193 - 2
Utilities/cmcurl/lib/smb.c

@@ -27,8 +27,6 @@
 
 #if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE)
 
-#define BUILDING_CURL_SMB_C
-
 #ifdef WIN32
 #define getpid GetCurrentProcessId
 #endif
@@ -50,6 +48,199 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
+/*
+ * Definitions for SMB protocol data structures
+ */
+#if defined(_MSC_VER) || defined(__ILEC400__)
+#  define PACK
+#  pragma pack(push)
+#  pragma pack(1)
+#elif defined(__GNUC__)
+#  define PACK __attribute__((packed))
+#else
+#  define PACK
+#endif
+
+#define SMB_COM_CLOSE                 0x04
+#define SMB_COM_READ_ANDX             0x2e
+#define SMB_COM_WRITE_ANDX            0x2f
+#define SMB_COM_TREE_DISCONNECT       0x71
+#define SMB_COM_NEGOTIATE             0x72
+#define SMB_COM_SETUP_ANDX            0x73
+#define SMB_COM_TREE_CONNECT_ANDX     0x75
+#define SMB_COM_NT_CREATE_ANDX        0xa2
+#define SMB_COM_NO_ANDX_COMMAND       0xff
+
+#define SMB_WC_CLOSE                  0x03
+#define SMB_WC_READ_ANDX              0x0c
+#define SMB_WC_WRITE_ANDX             0x0e
+#define SMB_WC_SETUP_ANDX             0x0d
+#define SMB_WC_TREE_CONNECT_ANDX      0x04
+#define SMB_WC_NT_CREATE_ANDX         0x18
+
+#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10
+#define SMB_FLAGS_CASELESS_PATHNAMES  0x08
+#define SMB_FLAGS2_UNICODE_STRINGS    0x8000
+#define SMB_FLAGS2_IS_LONG_NAME       0x0040
+#define SMB_FLAGS2_KNOWS_LONG_NAME    0x0001
+
+#define SMB_CAP_LARGE_FILES           0x08
+#define SMB_GENERIC_WRITE             0x40000000
+#define SMB_GENERIC_READ              0x80000000
+#define SMB_FILE_SHARE_ALL            0x07
+#define SMB_FILE_OPEN                 0x01
+#define SMB_FILE_OVERWRITE_IF         0x05
+
+#define SMB_ERR_NOACCESS              0x00050001
+
+struct smb_header {
+  unsigned char nbt_type;
+  unsigned char nbt_flags;
+  unsigned short nbt_length;
+  unsigned char magic[4];
+  unsigned char command;
+  unsigned int status;
+  unsigned char flags;
+  unsigned short flags2;
+  unsigned short pid_high;
+  unsigned char signature[8];
+  unsigned short pad;
+  unsigned short tid;
+  unsigned short pid;
+  unsigned short uid;
+  unsigned short mid;
+} PACK;
+
+struct smb_negotiate_response {
+  struct smb_header h;
+  unsigned char word_count;
+  unsigned short dialect_index;
+  unsigned char security_mode;
+  unsigned short max_mpx_count;
+  unsigned short max_number_vcs;
+  unsigned int max_buffer_size;
+  unsigned int max_raw_size;
+  unsigned int session_key;
+  unsigned int capabilities;
+  unsigned int system_time_low;
+  unsigned int system_time_high;
+  unsigned short server_time_zone;
+  unsigned char encryption_key_length;
+  unsigned short byte_count;
+  char bytes[1];
+} PACK;
+
+struct andx {
+  unsigned char command;
+  unsigned char pad;
+  unsigned short offset;
+} PACK;
+
+struct smb_setup {
+  unsigned char word_count;
+  struct andx andx;
+  unsigned short max_buffer_size;
+  unsigned short max_mpx_count;
+  unsigned short vc_number;
+  unsigned int session_key;
+  unsigned short lengths[2];
+  unsigned int pad;
+  unsigned int capabilities;
+  unsigned short byte_count;
+  char bytes[1024];
+} PACK;
+
+struct smb_tree_connect {
+  unsigned char word_count;
+  struct andx andx;
+  unsigned short flags;
+  unsigned short pw_len;
+  unsigned short byte_count;
+  char bytes[1024];
+} PACK;
+
+struct smb_nt_create {
+  unsigned char word_count;
+  struct andx andx;
+  unsigned char pad;
+  unsigned short name_length;
+  unsigned int flags;
+  unsigned int root_fid;
+  unsigned int access;
+  curl_off_t allocation_size;
+  unsigned int ext_file_attributes;
+  unsigned int share_access;
+  unsigned int create_disposition;
+  unsigned int create_options;
+  unsigned int impersonation_level;
+  unsigned char security_flags;
+  unsigned short byte_count;
+  char bytes[1024];
+} PACK;
+
+struct smb_nt_create_response {
+  struct smb_header h;
+  unsigned char word_count;
+  struct andx andx;
+  unsigned char op_lock_level;
+  unsigned short fid;
+  unsigned int create_disposition;
+
+  curl_off_t create_time;
+  curl_off_t last_access_time;
+  curl_off_t last_write_time;
+  curl_off_t last_change_time;
+  unsigned int ext_file_attributes;
+  curl_off_t allocation_size;
+  curl_off_t end_of_file;
+} PACK;
+
+struct smb_read {
+  unsigned char word_count;
+  struct andx andx;
+  unsigned short fid;
+  unsigned int offset;
+  unsigned short max_bytes;
+  unsigned short min_bytes;
+  unsigned int timeout;
+  unsigned short remaining;
+  unsigned int offset_high;
+  unsigned short byte_count;
+} PACK;
+
+struct smb_write {
+  struct smb_header h;
+  unsigned char word_count;
+  struct andx andx;
+  unsigned short fid;
+  unsigned int offset;
+  unsigned int timeout;
+  unsigned short write_mode;
+  unsigned short remaining;
+  unsigned short pad;
+  unsigned short data_length;
+  unsigned short data_offset;
+  unsigned int offset_high;
+  unsigned short byte_count;
+  unsigned char pad2;
+} PACK;
+
+struct smb_close {
+  unsigned char word_count;
+  unsigned short fid;
+  unsigned int last_mtime;
+  unsigned short byte_count;
+} PACK;
+
+struct smb_tree_disconnect {
+  unsigned char word_count;
+  unsigned short byte_count;
+} PACK;
+
+#if defined(_MSC_VER) || defined(__ILEC400__)
+#  pragma pack(pop)
+#endif
+
 /* Local API functions */
 static CURLcode smb_setup_connection(struct Curl_easy *data,
                                      struct connectdata *conn);

+ 0 - 197
Utilities/cmcurl/lib/smb.h

@@ -48,203 +48,6 @@ struct smb_conn {
   size_t got;
 };
 
-/*
- * Definitions for SMB protocol data structures
- */
-#ifdef BUILDING_CURL_SMB_C
-
-#if defined(_MSC_VER) || defined(__ILEC400__)
-#  define PACK
-#  pragma pack(push)
-#  pragma pack(1)
-#elif defined(__GNUC__)
-#  define PACK __attribute__((packed))
-#else
-#  define PACK
-#endif
-
-#define SMB_COM_CLOSE                 0x04
-#define SMB_COM_READ_ANDX             0x2e
-#define SMB_COM_WRITE_ANDX            0x2f
-#define SMB_COM_TREE_DISCONNECT       0x71
-#define SMB_COM_NEGOTIATE             0x72
-#define SMB_COM_SETUP_ANDX            0x73
-#define SMB_COM_TREE_CONNECT_ANDX     0x75
-#define SMB_COM_NT_CREATE_ANDX        0xa2
-#define SMB_COM_NO_ANDX_COMMAND       0xff
-
-#define SMB_WC_CLOSE                  0x03
-#define SMB_WC_READ_ANDX              0x0c
-#define SMB_WC_WRITE_ANDX             0x0e
-#define SMB_WC_SETUP_ANDX             0x0d
-#define SMB_WC_TREE_CONNECT_ANDX      0x04
-#define SMB_WC_NT_CREATE_ANDX         0x18
-
-#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10
-#define SMB_FLAGS_CASELESS_PATHNAMES  0x08
-#define SMB_FLAGS2_UNICODE_STRINGS    0x8000
-#define SMB_FLAGS2_IS_LONG_NAME       0x0040
-#define SMB_FLAGS2_KNOWS_LONG_NAME    0x0001
-
-#define SMB_CAP_LARGE_FILES           0x08
-#define SMB_GENERIC_WRITE             0x40000000
-#define SMB_GENERIC_READ              0x80000000
-#define SMB_FILE_SHARE_ALL            0x07
-#define SMB_FILE_OPEN                 0x01
-#define SMB_FILE_OVERWRITE_IF         0x05
-
-#define SMB_ERR_NOACCESS              0x00050001
-
-struct smb_header {
-  unsigned char nbt_type;
-  unsigned char nbt_flags;
-  unsigned short nbt_length;
-  unsigned char magic[4];
-  unsigned char command;
-  unsigned int status;
-  unsigned char flags;
-  unsigned short flags2;
-  unsigned short pid_high;
-  unsigned char signature[8];
-  unsigned short pad;
-  unsigned short tid;
-  unsigned short pid;
-  unsigned short uid;
-  unsigned short mid;
-} PACK;
-
-struct smb_negotiate_response {
-  struct smb_header h;
-  unsigned char word_count;
-  unsigned short dialect_index;
-  unsigned char security_mode;
-  unsigned short max_mpx_count;
-  unsigned short max_number_vcs;
-  unsigned int max_buffer_size;
-  unsigned int max_raw_size;
-  unsigned int session_key;
-  unsigned int capabilities;
-  unsigned int system_time_low;
-  unsigned int system_time_high;
-  unsigned short server_time_zone;
-  unsigned char encryption_key_length;
-  unsigned short byte_count;
-  char bytes[1];
-} PACK;
-
-struct andx {
-  unsigned char command;
-  unsigned char pad;
-  unsigned short offset;
-} PACK;
-
-struct smb_setup {
-  unsigned char word_count;
-  struct andx andx;
-  unsigned short max_buffer_size;
-  unsigned short max_mpx_count;
-  unsigned short vc_number;
-  unsigned int session_key;
-  unsigned short lengths[2];
-  unsigned int pad;
-  unsigned int capabilities;
-  unsigned short byte_count;
-  char bytes[1024];
-} PACK;
-
-struct smb_tree_connect {
-  unsigned char word_count;
-  struct andx andx;
-  unsigned short flags;
-  unsigned short pw_len;
-  unsigned short byte_count;
-  char bytes[1024];
-} PACK;
-
-struct smb_nt_create {
-  unsigned char word_count;
-  struct andx andx;
-  unsigned char pad;
-  unsigned short name_length;
-  unsigned int flags;
-  unsigned int root_fid;
-  unsigned int access;
-  curl_off_t allocation_size;
-  unsigned int ext_file_attributes;
-  unsigned int share_access;
-  unsigned int create_disposition;
-  unsigned int create_options;
-  unsigned int impersonation_level;
-  unsigned char security_flags;
-  unsigned short byte_count;
-  char bytes[1024];
-} PACK;
-
-struct smb_nt_create_response {
-  struct smb_header h;
-  unsigned char word_count;
-  struct andx andx;
-  unsigned char op_lock_level;
-  unsigned short fid;
-  unsigned int create_disposition;
-
-  curl_off_t create_time;
-  curl_off_t last_access_time;
-  curl_off_t last_write_time;
-  curl_off_t last_change_time;
-  unsigned int ext_file_attributes;
-  curl_off_t allocation_size;
-  curl_off_t end_of_file;
-} PACK;
-
-struct smb_read {
-  unsigned char word_count;
-  struct andx andx;
-  unsigned short fid;
-  unsigned int offset;
-  unsigned short max_bytes;
-  unsigned short min_bytes;
-  unsigned int timeout;
-  unsigned short remaining;
-  unsigned int offset_high;
-  unsigned short byte_count;
-} PACK;
-
-struct smb_write {
-  struct smb_header h;
-  unsigned char word_count;
-  struct andx andx;
-  unsigned short fid;
-  unsigned int offset;
-  unsigned int timeout;
-  unsigned short write_mode;
-  unsigned short remaining;
-  unsigned short pad;
-  unsigned short data_length;
-  unsigned short data_offset;
-  unsigned int offset_high;
-  unsigned short byte_count;
-  unsigned char pad2;
-} PACK;
-
-struct smb_close {
-  unsigned char word_count;
-  unsigned short fid;
-  unsigned int last_mtime;
-  unsigned short byte_count;
-} PACK;
-
-struct smb_tree_disconnect {
-  unsigned char word_count;
-  unsigned short byte_count;
-} PACK;
-
-#if defined(_MSC_VER) || defined(__ILEC400__)
-#  pragma pack(pop)
-#endif
-
-#endif /* BUILDING_CURL_SMB_C */
-
 #if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \
     (SIZEOF_CURL_OFF_T > 4)
 

+ 22 - 22
Utilities/cmcurl/lib/smtp.c

@@ -281,11 +281,11 @@ static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
 
 /***********************************************************************
  *
- * state()
+ * smtp_state()
  *
  * This is the ONLY way to change SMTP state!
  */
-static void state(struct Curl_easy *data, smtpstate newstate)
+static void smtp_state(struct Curl_easy *data, smtpstate newstate)
 {
   struct smtp_conn *smtpc = &data->conn->proto.smtpc;
 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
@@ -338,7 +338,7 @@ static CURLcode smtp_perform_ehlo(struct Curl_easy *data)
   result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain);
 
   if(!result)
-    state(data, SMTP_EHLO);
+    smtp_state(data, SMTP_EHLO);
 
   return result;
 }
@@ -362,7 +362,7 @@ static CURLcode smtp_perform_helo(struct Curl_easy *data,
   result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain);
 
   if(!result)
-    state(data, SMTP_HELO);
+    smtp_state(data, SMTP_HELO);
 
   return result;
 }
@@ -381,7 +381,7 @@ static CURLcode smtp_perform_starttls(struct Curl_easy *data,
                                   "%s", "STARTTLS");
 
   if(!result)
-    state(data, SMTP_STARTTLS);
+    smtp_state(data, SMTP_STARTTLS);
 
   return result;
 }
@@ -410,7 +410,7 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
   if(!result) {
     smtpc->ssldone = ssldone;
     if(smtpc->state != SMTP_UPGRADETLS)
-      state(data, SMTP_UPGRADETLS);
+      smtp_state(data, SMTP_UPGRADETLS);
 
     if(smtpc->ssldone) {
       smtp_to_smtps(conn);
@@ -499,7 +499,7 @@ static CURLcode smtp_perform_authentication(struct Curl_easy *data)
      server supports authentication, and end the connect phase if not */
   if(!smtpc->auth_supported ||
      !Curl_sasl_can_authenticate(&smtpc->sasl, data)) {
-    state(data, SMTP_STOP);
+    smtp_state(data, SMTP_STOP);
     return result;
   }
 
@@ -508,7 +508,7 @@ static CURLcode smtp_perform_authentication(struct Curl_easy *data)
 
   if(!result) {
     if(progress == SASL_INPROGRESS)
-      state(data, SMTP_AUTH);
+      smtp_state(data, SMTP_AUTH);
     else {
       /* Other mechanisms not supported */
       infof(data, "No known authentication mechanisms supported");
@@ -586,7 +586,7 @@ static CURLcode smtp_perform_command(struct Curl_easy *data)
                            smtp->custom : "HELP");
 
   if(!result)
-    state(data, SMTP_COMMAND);
+    smtp_state(data, SMTP_COMMAND);
 
   return result;
 }
@@ -771,7 +771,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
   free(size);
 
   if(!result)
-    state(data, SMTP_MAIL);
+    smtp_state(data, SMTP_MAIL);
 
   return result;
 }
@@ -812,7 +812,7 @@ static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data)
   free(address);
 
   if(!result)
-    state(data, SMTP_RCPT);
+    smtp_state(data, SMTP_RCPT);
 
   return result;
 }
@@ -830,7 +830,7 @@ static CURLcode smtp_perform_quit(struct Curl_easy *data,
   CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "QUIT");
 
   if(!result)
-    state(data, SMTP_QUIT);
+    smtp_state(data, SMTP_QUIT);
 
   return result;
 }
@@ -996,7 +996,7 @@ static CURLcode smtp_state_helo_resp(struct Curl_easy *data, int smtpcode,
   }
   else
     /* End of connect phase */
-    state(data, SMTP_STOP);
+    smtp_state(data, SMTP_STOP);
 
   return result;
 }
@@ -1017,7 +1017,7 @@ static CURLcode smtp_state_auth_resp(struct Curl_easy *data,
   if(!result)
     switch(progress) {
     case SASL_DONE:
-      state(data, SMTP_STOP);  /* Authenticated */
+      smtp_state(data, SMTP_STOP);  /* Authenticated */
       break;
     case SASL_IDLE:            /* No mechanism left after cancellation */
       failf(data, "Authentication cancelled");
@@ -1064,11 +1064,11 @@ static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode,
         }
         else
           /* End of DO phase */
-          state(data, SMTP_STOP);
+          smtp_state(data, SMTP_STOP);
       }
       else
         /* End of DO phase */
-        state(data, SMTP_STOP);
+        smtp_state(data, SMTP_STOP);
     }
   }
 
@@ -1145,7 +1145,7 @@ static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data,
         result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "DATA");
 
         if(!result)
-          state(data, SMTP_DATA);
+          smtp_state(data, SMTP_DATA);
       }
     }
   }
@@ -1172,7 +1172,7 @@ static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode,
     Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
 
     /* End of DO phase */
-    state(data, SMTP_STOP);
+    smtp_state(data, SMTP_STOP);
   }
 
   return result;
@@ -1192,7 +1192,7 @@ static CURLcode smtp_state_postdata_resp(struct Curl_easy *data,
     result = CURLE_WEIRD_SERVER_REPLY;
 
   /* End of DONE phase */
-  state(data, SMTP_STOP);
+  smtp_state(data, SMTP_STOP);
 
   return result;
 }
@@ -1274,7 +1274,7 @@ static CURLcode smtp_statemachine(struct Curl_easy *data,
       /* fallthrough, just stop! */
     default:
       /* internal error */
-      state(data, SMTP_STOP);
+      smtp_state(data, SMTP_STOP);
       break;
     }
   } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
@@ -1379,7 +1379,7 @@ static CURLcode smtp_connect(struct Curl_easy *data, bool *done)
     return result;
 
   /* Start off waiting for the server greeting response */
-  state(data, SMTP_SERVERGREET);
+  smtp_state(data, SMTP_SERVERGREET);
 
   result = smtp_multi_statemach(data, done);
 
@@ -1461,7 +1461,7 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
       free(eob);
     }
 
-    state(data, SMTP_POSTDATA);
+    smtp_state(data, SMTP_POSTDATA);
 
     /* Run the state-machine */
     result = smtp_block_statemach(data, conn, FALSE);

+ 9 - 10
Utilities/cmcurl/lib/socks.c

@@ -161,7 +161,7 @@ static void socksstate(struct socks_state *sx, struct Curl_easy *data,
   enum connect_t oldstate = sx->state;
 #ifdef DEBUG_AND_VERBOSE
   /* synced with the state list in urldata.h */
-  static const char * const statename[] = {
+  static const char * const socks_statename[] = {
     "INIT",
     "SOCKS_INIT",
     "SOCKS_SEND",
@@ -193,7 +193,7 @@ static void socksstate(struct socks_state *sx, struct Curl_easy *data,
 #ifdef DEBUG_AND_VERBOSE
   infof(data,
         "SXSTATE: %s => %s; line %d",
-        statename[oldstate], statename[sx->state],
+        socks_statename[oldstate], socks_statename[sx->state],
         lineno);
 #endif
 }
@@ -567,7 +567,6 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
   */
   struct connectdata *conn = cf->conn;
   unsigned char *socksreq = (unsigned char *)data->state.buffer;
-  char dest[256] = "unknown";  /* printable hostname:port */
   int idx;
   CURLcode result;
   CURLproxycode presult;
@@ -820,8 +819,8 @@ CONNECT_REQ_INIT:
     /* FALLTHROUGH */
 CONNECT_RESOLVED:
   case CONNECT_RESOLVED: {
+    char dest[MAX_IPADR_LEN] = "unknown";  /* printable address */
     struct Curl_addrinfo *hp = NULL;
-    size_t destlen;
     if(dns)
       hp = dns->addr;
     if(!hp) {
@@ -831,8 +830,6 @@ CONNECT_RESOLVED:
     }
 
     Curl_printable_address(hp, dest, sizeof(dest));
-    destlen = strlen(dest);
-    msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", sx->remote_port);
 
     len = 0;
     socksreq[len++] = 5; /* version (SOCKS5) */
@@ -848,7 +845,8 @@ CONNECT_RESOLVED:
         socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
       }
 
-      infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)", dest);
+      infof(data, "SOCKS5 connect to %s:%d (locally resolved)", dest,
+            sx->remote_port);
     }
 #ifdef ENABLE_IPV6
     else if(hp->ai_family == AF_INET6) {
@@ -862,7 +860,8 @@ CONNECT_RESOLVED:
           ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
       }
 
-      infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)", dest);
+      infof(data, "SOCKS5 connect to [%s]:%d (locally resolved)", dest,
+            sx->remote_port);
     }
 #endif
     else {
@@ -1115,7 +1114,7 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
     return CURLE_OK;
   }
 
-  result = cf->next->cft->connect(cf->next, data, blocking, done);
+  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
   if(result || !*done)
     return result;
 
@@ -1193,7 +1192,7 @@ static void socks_proxy_cf_close(struct Curl_cfilter *cf,
   DEBUGASSERT(cf->next);
   cf->connected = FALSE;
   socks_proxy_cf_free(cf);
-  cf->next->cft->close(cf->next, data);
+  cf->next->cft->do_close(cf->next, data);
 }
 
 static void socks_proxy_cf_destroy(struct Curl_cfilter *cf,

+ 2 - 3
Utilities/cmcurl/lib/telnet.c

@@ -1534,7 +1534,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
   }
 
   while(keepon) {
-    DEBUGF(infof(data, "telnet_do(handle=%p), poll %d fds", data, poll_cnt));
+    DEBUGF(infof(data, "telnet_do, poll %d fds", poll_cnt));
     switch(Curl_poll(pfd, poll_cnt, interval_ms)) {
     case -1:                    /* error, stop reading */
       keepon = FALSE;
@@ -1558,8 +1558,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
            * in a clean way? Seems to be timing related, happens more
            * on slow debug build */
           if(data->state.os_errno == ECONNRESET) {
-            DEBUGF(infof(data, "telnet_do(handle=%p), unexpected ECONNRESET"
-                         " on recv", data));
+            DEBUGF(infof(data, "telnet_do, unexpected ECONNRESET on recv"));
           }
           break;
         }

+ 15 - 1
Utilities/cmcurl/lib/timeval.c

@@ -58,7 +58,8 @@ struct curltime Curl_now(void)
   return now;
 }
 
-#elif defined(HAVE_CLOCK_GETTIME_MONOTONIC)
+#elif defined(HAVE_CLOCK_GETTIME_MONOTONIC) ||  \
+  defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW)
 
 struct curltime Curl_now(void)
 {
@@ -87,6 +88,19 @@ struct curltime Curl_now(void)
     have_clock_gettime = TRUE;
 #endif
 
+#ifdef HAVE_CLOCK_GETTIME_MONOTONIC_RAW
+  if(
+#if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) &&    \
+        (HAVE_BUILTIN_AVAILABLE == 1)
+    have_clock_gettime &&
+#endif
+    (0 == clock_gettime(CLOCK_MONOTONIC_RAW, &tsnow))) {
+    cnow.tv_sec = tsnow.tv_sec;
+    cnow.tv_usec = (unsigned int)(tsnow.tv_nsec / 1000);
+  }
+  else
+#endif
+
   if(
 #if defined(__APPLE__) && defined(HAVE_BUILTIN_AVAILABLE) && \
         (HAVE_BUILTIN_AVAILABLE == 1)

+ 10 - 6
Utilities/cmcurl/lib/transfer.c

@@ -428,6 +428,8 @@ static CURLcode readwrite_data(struct Curl_easy *data,
   size_t excess = 0; /* excess bytes read */
   bool readmore = FALSE; /* used by RTP to signal for more data */
   int maxloops = 100;
+  curl_off_t max_recv = data->set.max_recv_speed?
+                        data->set.max_recv_speed : CURL_OFF_T_MAX;
   char *buf = data->state.buffer;
   DEBUGASSERT(buf);
 
@@ -472,7 +474,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
     else {
       /* read nothing but since we wanted nothing we consider this an OK
          situation to proceed from */
-      DEBUGF(infof(data, DMSG(data, "readwrite_data: we're done")));
+      DEBUGF(infof(data, "readwrite_data: we're done"));
       nread = 0;
     }
 
@@ -666,6 +668,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
       }
 
       k->bytecount += nread;
+      max_recv -= nread;
 
       Curl_pgrsSetDownloadCounter(data, k->bytecount);
 
@@ -749,9 +752,9 @@ static CURLcode readwrite_data(struct Curl_easy *data,
       break;
     }
 
-  } while(data_pending(data) && maxloops--);
+  } while((max_recv > 0) && data_pending(data) && maxloops--);
 
-  if(maxloops <= 0) {
+  if(maxloops <= 0 || max_recv <= 0) {
     /* we mark it as read-again-please */
     data->state.dselect_bits = CURL_CSELECT_IN;
     *comeback = TRUE;
@@ -768,7 +771,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
 
 out:
   if(result)
-    DEBUGF(infof(data, DMSG(data, "readwrite_data() -> %d"), result));
+    DEBUGF(infof(data, "readwrite_data() -> %d", result));
   return result;
 }
 
@@ -1233,7 +1236,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
   *done = (0 == (k->keepon&(KEEP_RECVBITS|KEEP_SENDBITS))) ? TRUE : FALSE;
 out:
   if(result)
-    DEBUGF(infof(data, DMSG(data, "Curl_readwrite() -> %d"), result));
+    DEBUGF(infof(data, "Curl_readwrite() -> %d", result));
   return result;
 }
 
@@ -1551,10 +1554,11 @@ CURLcode Curl_follow(struct Curl_easy *data,
 
   if((type != FOLLOW_RETRY) &&
      (data->req.httpcode != 401) && (data->req.httpcode != 407) &&
-     Curl_is_absolute_url(newurl, NULL, 0, FALSE))
+     Curl_is_absolute_url(newurl, NULL, 0, FALSE)) {
     /* If this is not redirect due to a 401 or 407 response and an absolute
        URL: don't allow a custom port number */
     disallowport = TRUE;
+  }
 
   DEBUGASSERT(data->state.uh);
   uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl,

+ 22 - 16
Utilities/cmcurl/lib/url.c

@@ -659,6 +659,9 @@ CURLcode Curl_open(struct Curl_easy **curl)
 
     /* most recent connection is not yet defined */
     data->state.lastconnect_id = -1;
+    data->state.recent_conn_id = -1;
+    /* and not assigned an id yet */
+    data->id = -1;
 
     data->progress.flags |= PGRS_HIDE;
     data->state.current_speed = -1; /* init to negative == impossible */
@@ -680,7 +683,7 @@ CURLcode Curl_open(struct Curl_easy **curl)
 static void conn_shutdown(struct Curl_easy *data)
 {
   DEBUGASSERT(data);
-  infof(data, "Closing connection %ld", data->conn->connection_id);
+  infof(data, "Closing connection");
 
   /* possible left-overs from the async name resolvers */
   Curl_resolver_cancel(data);
@@ -763,7 +766,8 @@ void Curl_disconnect(struct Curl_easy *data,
   /* the transfer must be detached from the connection */
   DEBUGASSERT(!data->conn);
 
-  DEBUGF(infof(data, "Curl_disconnect(conn #%ld, dead=%d)",
+  DEBUGF(infof(data, "Curl_disconnect(conn #%"
+         CURL_FORMAT_CURL_OFF_T ", dead=%d)",
          conn->connection_id, dead_connection));
   /*
    * If this connection isn't marked to force-close, leave it open if there
@@ -937,6 +941,7 @@ static bool extract_if_dead(struct connectdata *conn,
     else {
       bool input_pending;
 
+      Curl_attach_connection(data, conn);
       dead = !Curl_conn_is_alive(data, conn, &input_pending);
       if(input_pending) {
         /* For reuse, we want a "clean" connection state. The includes
@@ -949,10 +954,12 @@ static bool extract_if_dead(struct connectdata *conn,
          */
         dead = TRUE;
       }
+      Curl_detach_connection(data);
     }
 
     if(dead) {
-      infof(data, "Connection %ld seems to be dead", conn->connection_id);
+      infof(data, "Connection %" CURL_FORMAT_CURL_OFF_T " seems to be dead",
+            conn->connection_id);
       Curl_conncache_remove_conn(data, conn, FALSE);
       return TRUE;
     }
@@ -1147,8 +1154,8 @@ ConnectionExists(struct Curl_easy *data,
           /* primary_ip[0] is NUL only if the resolving of the name hasn't
              completed yet and until then we don't re-use this connection */
           if(!check->primary_ip[0]) {
-            infof(data,
-                  "Connection #%ld is still name resolving, can't reuse",
+            infof(data, "Connection #%" CURL_FORMAT_CURL_OFF_T " is still "
+                        "name resolving, can't reuse",
                   check->connection_id);
             continue;
           }
@@ -1158,8 +1165,8 @@ ConnectionExists(struct Curl_easy *data,
       if(!Curl_conn_is_connected(check, FIRSTSOCKET)) {
         foundPendingCandidate = TRUE;
         /* Don't pick a connection that hasn't connected yet */
-        infof(data, "Connection #%ld isn't open enough, can't reuse",
-              check->connection_id);
+        infof(data, "Connection #%" CURL_FORMAT_CURL_OFF_T
+              "isn't open enough, can't reuse", check->connection_id);
         continue;
       }
 
@@ -1335,8 +1342,8 @@ ConnectionExists(struct Curl_easy *data,
             if(!Curl_ssl_config_matches(&needle->ssl_config,
                                         &check->ssl_config)) {
               DEBUGF(infof(data,
-                           "Connection #%ld has different SSL parameters, "
-                           "can't reuse",
+                           "Connection #%" CURL_FORMAT_CURL_OFF_T
+                           " has different SSL parameters, can't reuse",
                            check->connection_id));
               continue;
             }
@@ -1477,14 +1484,14 @@ void Curl_verboseconnect(struct Curl_easy *data,
                          struct connectdata *conn)
 {
   if(data->set.verbose)
-    infof(data, "Connected to %s (%s) port %u (#%ld)",
+    infof(data, "Connected to %s (%s) port %u",
 #ifndef CURL_DISABLE_PROXY
           conn->bits.socksproxy ? conn->socks_proxy.host.dispname :
           conn->bits.httpproxy ? conn->http_proxy.host.dispname :
 #endif
           conn->bits.conn_to_host ? conn->conn_to_host.dispname :
           conn->host.dispname,
-          conn->primary_ip, conn->port, conn->connection_id);
+          conn->primary_ip, conn->port);
 }
 #endif
 
@@ -1857,7 +1864,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
    * User name and password set with their own options override the
    * credentials possibly set in the URL.
    */
-  if(!data->state.aptr.passwd) {
+  if(!data->set.str[STRING_PASSWORD]) {
     uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0);
     if(!uc) {
       char *decoded;
@@ -3678,15 +3685,14 @@ static CURLcode create_conn(struct Curl_easy *data,
     *in_connect = conn;
 
 #ifndef CURL_DISABLE_PROXY
-    infof(data, "Re-using existing connection #%ld with %s %s",
-          conn->connection_id,
+    infof(data, "Re-using existing connection with %s %s",
           conn->bits.proxy?"proxy":"host",
           conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname :
           conn->http_proxy.host.name ? conn->http_proxy.host.dispname :
           conn->host.dispname);
 #else
-    infof(data, "Re-using existing connection #%ld with host %s",
-          conn->connection_id, conn->host.dispname);
+    infof(data, "Re-using existing connection with host %s",
+          conn->host.dispname);
 #endif
   }
   else {

+ 52 - 41
Utilities/cmcurl/lib/urlapi.c

@@ -201,7 +201,7 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url,
 size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen,
                             bool guess_scheme)
 {
-  int i;
+  int i = 0;
   DEBUGASSERT(!buf || (buflen > MAX_SCHEME_LEN));
   (void)buflen; /* only used in debug-builds */
   if(buf)
@@ -210,17 +210,18 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen,
   if(guess_scheme && STARTS_WITH_DRIVE_PREFIX(url))
     return 0;
 #endif
-  for(i = 0; i < MAX_SCHEME_LEN; ++i) {
-    char s = url[i];
-    if(s && (ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') )) {
-      /* RFC 3986 3.1 explains:
-        scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
-      */
-    }
-    else {
-      break;
+  if(ISALPHA(url[0]))
+    for(i = 1; i < MAX_SCHEME_LEN; ++i) {
+      char s = url[i];
+      if(s && (ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') )) {
+        /* RFC 3986 3.1 explains:
+           scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+        */
+      }
+      else {
+        break;
+      }
     }
-  }
   if(i && (url[i] == ':') && ((url[i + 1] == '/') || !guess_scheme)) {
     /* If this does not guess scheme, the scheme always ends with the colon so
        that this also detects data: URLs etc. In guessing mode, data: could
@@ -1546,7 +1547,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
         }
       }
 
-      url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+      url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
                     scheme,
                     u->user ? u->user : "",
                     u->password ? ":": "",
@@ -1557,7 +1558,6 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
                     allochost ? allochost : u->host,
                     port ? ":": "",
                     port ? port : "",
-                    (u->path && (u->path[0] != '/')) ? "/": "",
                     u->path ? u->path : "/",
                     (u->query && u->query[0]) ? "?": "",
                     (u->query && u->query[0]) ? u->query : "",
@@ -1639,8 +1639,10 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
   bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0;
   bool plusencode = FALSE;
   bool urlskipslash = FALSE;
+  bool leadingslash = FALSE;
   bool appendquery = FALSE;
   bool equalsencode = FALSE;
+  size_t nalloc;
 
   if(!u)
     return CURLUE_BAD_HANDLE;
@@ -1693,6 +1695,11 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
     return CURLUE_OK;
   }
 
+  nalloc = strlen(part);
+  if(nalloc > CURL_MAX_INPUT_LENGTH)
+    /* excessive input length */
+    return CURLUE_MALFORMED_INPUT;
+
   switch(what) {
   case CURLUPART_SCHEME: {
     size_t plen = strlen(part);
@@ -1706,13 +1713,17 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
       return CURLUE_UNSUPPORTED_SCHEME;
     storep = &u->scheme;
     urlencode = FALSE; /* never */
-    /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */
-    while(plen--) {
-      if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.'))
-        s++; /* fine */
-      else
-        return CURLUE_BAD_SCHEME;
+    if(ISALPHA(*s)) {
+      /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */
+      while(--plen) {
+        if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.'))
+          s++; /* fine */
+        else
+          return CURLUE_BAD_SCHEME;
+      }
     }
+    else
+      return CURLUE_BAD_SCHEME;
     break;
   }
   case CURLUPART_USER:
@@ -1746,6 +1757,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
   break;
   case CURLUPART_PATH:
     urlskipslash = TRUE;
+    leadingslash = TRUE; /* enforce */
     storep = &u->path;
     break;
   case CURLUPART_QUERY:
@@ -1794,18 +1806,17 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
   }
   DEBUGASSERT(storep);
   {
-    const char *newp = part;
-    size_t nalloc = strlen(part);
-
-    if(nalloc > CURL_MAX_INPUT_LENGTH)
-      /* excessive input length */
-      return CURLUE_MALFORMED_INPUT;
+    const char *newp;
+    struct dynbuf enc;
+    Curl_dyn_init(&enc, nalloc * 3 + 1 + leadingslash);
 
+    if(leadingslash && (part[0] != '/')) {
+      CURLcode result = Curl_dyn_addn(&enc, "/", 1);
+      if(result)
+        return CURLUE_OUT_OF_MEMORY;
+    }
     if(urlencode) {
       const unsigned char *i;
-      struct dynbuf enc;
-
-      Curl_dyn_init(&enc, nalloc * 3 + 1);
 
       for(i = (const unsigned char *)part; *i; i++) {
         CURLcode result;
@@ -1833,14 +1844,13 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
             return CURLUE_OUT_OF_MEMORY;
         }
       }
-      newp = Curl_dyn_ptr(&enc);
     }
     else {
       char *p;
-      newp = strdup(part);
-      if(!newp)
+      CURLcode result = Curl_dyn_add(&enc, part);
+      if(result)
         return CURLUE_OUT_OF_MEMORY;
-      p = (char *)newp;
+      p = Curl_dyn_ptr(&enc);
       while(*p) {
         /* make sure percent encoded are lower case */
         if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) &&
@@ -1853,6 +1863,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
           p++;
       }
     }
+    newp = Curl_dyn_ptr(&enc);
 
     if(appendquery) {
       /* Append the 'newp' string onto the old query. Add a '&' separator if
@@ -1861,24 +1872,24 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
       size_t querylen = u->query ? strlen(u->query) : 0;
       bool addamperand = querylen && (u->query[querylen -1] != '&');
       if(querylen) {
-        struct dynbuf enc;
-        Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+        struct dynbuf qbuf;
+        Curl_dyn_init(&qbuf, CURL_MAX_INPUT_LENGTH);
 
-        if(Curl_dyn_addn(&enc, u->query, querylen)) /* add original query */
+        if(Curl_dyn_addn(&qbuf, u->query, querylen)) /* add original query */
           goto nomem;
 
         if(addamperand) {
-          if(Curl_dyn_addn(&enc, "&", 1))
+          if(Curl_dyn_addn(&qbuf, "&", 1))
             goto nomem;
         }
-        if(Curl_dyn_add(&enc, newp))
+        if(Curl_dyn_add(&qbuf, newp))
           goto nomem;
-        free((char *)newp);
+        Curl_dyn_free(&enc);
         free(*storep);
-        *storep = Curl_dyn_ptr(&enc);
+        *storep = Curl_dyn_ptr(&qbuf);
         return CURLUE_OK;
 nomem:
-        free((char *)newp);
+        Curl_dyn_free(&enc);
         return CURLUE_OUT_OF_MEMORY;
       }
     }
@@ -1890,7 +1901,7 @@ nomem:
       }
       else {
         if(!n || hostname_check(u, (char *)newp, n)) {
-          free((char *)newp);
+          Curl_dyn_free(&enc);
           return CURLUE_BAD_HOSTNAME;
         }
       }

+ 13 - 3
Utilities/cmcurl/lib/urldata.h

@@ -882,8 +882,8 @@ struct connectdata {
 #define CONN_INUSE(c) ((c)->easyq.size)
 
   /**** Fields set when inited and not modified again */
-  long connection_id; /* Contains a unique number to make it easier to
-                         track the connections in the log output */
+  curl_off_t connection_id; /* Contains a unique number to make it easier to
+                               track the connections in the log output */
 
   /* 'dns_entry' is the particular host we use. This points to an entry in the
      DNS cache and it will not get pruned while locked. It gets unlocked in
@@ -1294,7 +1294,9 @@ struct UrlState {
   /* buffers to store authentication data in, as parsed from input options */
   struct curltime keeps_speed; /* for the progress meter really */
 
-  long lastconnect_id; /* The last connection, -1 if undefined */
+  curl_off_t lastconnect_id; /* The last connection, -1 if undefined */
+  curl_off_t recent_conn_id; /* The most recent connection used, might no
+                              * longer exist */
   struct dynbuf headerb; /* buffer to store headers in */
 
   char *buffer; /* download buffer */
@@ -1563,6 +1565,7 @@ enum dupstring {
   STRING_DNS_LOCAL_IP6,
   STRING_SSL_EC_CURVES,
   STRING_AWS_SIGV4, /* Parameters for V4 signature */
+  STRING_HAPROXY_CLIENT_IP,     /* CURLOPT_HAPROXY_CLIENT_IP */
 
   /* -- end of null-terminated strings -- */
 
@@ -1902,6 +1905,13 @@ struct Curl_easy {
   /* First a simple identifier to easier detect if a user mix up this easy
      handle with a multi handle. Set this to CURLEASY_MAGIC_NUMBER */
   unsigned int magic;
+  /* once an easy handle is tied to a connection cache
+     a non-negative number to distinguish this transfer from
+     other using the same cache. For easier tracking
+     in log output.
+     This may wrap around after LONG_MAX to 0 again, so it
+     has no uniqueness guarantuee for very large processings. */
+  curl_off_t id;
 
   /* first, two fields for the linked list of these */
   struct Curl_easy *next;

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

@@ -300,7 +300,7 @@ char *curl_version(void)
    protocol line has its own #if line to make things easier on the eye.
  */
 
-static const char * const protocols[] = {
+static const char * const supported_protocols[] = {
 #ifndef CURL_DISABLE_DICT
   "dict",
 #endif
@@ -535,7 +535,7 @@ static curl_version_info_data version_info = {
   NULL, /* ssl_version */
   0,    /* ssl_version_num, this is kept at zero */
   NULL, /* zlib_version */
-  protocols,
+  supported_protocols,
   NULL, /* c-ares version */
   0,    /* c-ares version numerical */
   NULL, /* libidn version */

+ 3 - 2
Utilities/cmcurl/lib/vquic/curl_msh3.c

@@ -123,6 +123,7 @@ struct cf_msh3_ctx {
 };
 
 /* How to access `call_data` from a cf_msh3 filter */
+#undef CF_CTX_CALL_DATA
 #define CF_CTX_CALL_DATA(cf)  \
   ((struct cf_msh3_ctx *)(cf)->ctx)->call_data
 
@@ -172,7 +173,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
   msh3_lock_initialize(&stream->recv_lock);
   Curl_bufq_init2(&stream->recvbuf, H3_STREAM_CHUNK_SIZE,
                   H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
-  DEBUGF(LOG_CF(data, cf, "data setup (easy %p)", (void *)data));
+  DEBUGF(LOG_CF(data, cf, "data setup"));
   return CURLE_OK;
 }
 
@@ -645,7 +646,7 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
   }
   else {
     /* request is open */
-    DEBUGF(LOG_CF(data, cf, "req: send %zd body bytes", len));
+    DEBUGF(LOG_CF(data, cf, "req: send %zu body bytes", len));
     if(len > 0xFFFFFFFF) {
       len = 0xFFFFFFFF;
     }

+ 263 - 166
Utilities/cmcurl/lib/vquic/curl_ngtcp2.c

@@ -33,7 +33,7 @@
 #ifdef OPENSSL_IS_BORINGSSL
 #include <ngtcp2/ngtcp2_crypto_boringssl.h>
 #else
-#include <ngtcp2/ngtcp2_crypto_openssl.h>
+#include <ngtcp2/ngtcp2_crypto_quictls.h>
 #endif
 #include "vtls/openssl.h"
 #elif defined(USE_GNUTLS)
@@ -165,17 +165,19 @@ struct cf_ngtcp2_ctx {
 };
 
 /* How to access `call_data` from a cf_ngtcp2 filter */
+#undef CF_CTX_CALL_DATA
 #define CF_CTX_CALL_DATA(cf)  \
   ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data
 
 /**
  * All about the H3 internals of a stream
  */
-struct stream_ctx {
+struct h3_stream_ctx {
   int64_t id; /* HTTP/3 protocol identifier */
   struct bufq sendbuf;   /* h3 request body */
   struct bufq recvbuf;   /* h3 response body */
   size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */
+  size_t upload_blocked_len; /* the amount written last and EGAINed */
   size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */
   uint64_t error3; /* HTTP/3 stream error code */
   curl_off_t upload_left; /* number of request bytes left to upload */
@@ -186,18 +188,18 @@ struct stream_ctx {
   bool send_closed; /* stream is local closed */
 };
 
-#define H3_STREAM_CTX(d)    ((struct stream_ctx *)(((d) && (d)->req.p.http)? \
-                             ((struct HTTP *)(d)->req.p.http)->h3_ctx \
-                               : NULL))
-#define H3_STREAM_LCTX(d)   ((struct HTTP *)(d)->req.p.http)->h3_ctx
-#define H3_STREAM_ID(d)     (H3_STREAM_CTX(d)? \
-                             H3_STREAM_CTX(d)->id : -2)
+#define H3_STREAM_CTX(d)  ((struct h3_stream_ctx *)(((d) && (d)->req.p.http)? \
+                           ((struct HTTP *)(d)->req.p.http)->h3_ctx \
+                             : NULL))
+#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx
+#define H3_STREAM_ID(d)   (H3_STREAM_CTX(d)? \
+                           H3_STREAM_CTX(d)->id : -2)
 
 static CURLcode h3_data_setup(struct Curl_cfilter *cf,
                               struct Curl_easy *data)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
 
   if(!data || !data->req.p.http) {
     failf(data, "initialization failure, transfer not http initialized");
@@ -223,13 +225,13 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
   stream->recv_buf_nonflow = 0;
 
   H3_STREAM_LCTX(data) = stream;
-  DEBUGF(LOG_CF(data, cf, "data setup (easy %p)", (void *)data));
+  DEBUGF(LOG_CF(data, cf, "data setup"));
   return CURLE_OK;
 }
 
 static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
 
   (void)cf;
   if(stream) {
@@ -246,10 +248,37 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
    the maximum packet burst to MAX_PKT_BURST packets. */
 #define MAX_PKT_BURST 10
 
-static CURLcode cf_process_ingress(struct Curl_cfilter *cf,
-                                   struct Curl_easy *data);
-static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
-                                struct Curl_easy *data);
+struct pkt_io_ctx {
+  struct Curl_cfilter *cf;
+  struct Curl_easy *data;
+  ngtcp2_tstamp ts;
+  size_t pkt_count;
+  ngtcp2_path_storage ps;
+};
+
+static ngtcp2_tstamp timestamp(void)
+{
+  struct curltime ct = Curl_now();
+  return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS;
+}
+
+static void pktx_init(struct pkt_io_ctx *pktx,
+                      struct Curl_cfilter *cf,
+                      struct Curl_easy *data)
+{
+  pktx->cf = cf;
+  pktx->data = data;
+  pktx->ts = timestamp();
+  pktx->pkt_count = 0;
+  ngtcp2_path_storage_zero(&pktx->ps);
+}
+
+static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
+                                    struct Curl_easy *data,
+                                    struct pkt_io_ctx *pktx);
+static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
+                                   struct Curl_easy *data,
+                                   struct pkt_io_ctx *pktx);
 static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id,
                                    uint64_t datalen, void *user_data,
                                    void *stream_user_data);
@@ -261,12 +290,6 @@ static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref)
   return ctx->qconn;
 }
 
-static ngtcp2_tstamp timestamp(void)
-{
-  struct curltime ct = Curl_now();
-  return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS;
-}
-
 #ifdef DEBUG_NGTCP2
 static void quic_printf(void *user_data, const char *fmt, ...)
 {
@@ -300,7 +323,8 @@ static void qlog_callback(void *user_data, uint32_t flags,
 }
 
 static void quic_settings(struct cf_ngtcp2_ctx *ctx,
-                          struct Curl_easy *data)
+                          struct Curl_easy *data,
+                          struct pkt_io_ctx *pktx)
 {
   ngtcp2_settings *s = &ctx->settings;
   ngtcp2_transport_params *t = &ctx->transport_params;
@@ -314,7 +338,7 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx,
 #endif
 
   (void)data;
-  s->initial_ts = timestamp();
+  s->initial_ts = pktx->ts;
   s->handshake_timeout = QUIC_HANDSHAKE_TIMEOUT;
   s->max_window = 100 * ctx->max_stream_window;
   s->max_stream_window = ctx->max_stream_window;
@@ -327,7 +351,7 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx,
   t->initial_max_streams_uni = QUIC_MAX_STREAMS;
   t->max_idle_timeout = QUIC_IDLE_TIMEOUT;
   if(ctx->qlogfd != -1) {
-    s->qlog.write = qlog_callback;
+    s->qlog_write = qlog_callback;
   }
 }
 
@@ -383,8 +407,8 @@ static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx,
     goto out;
   }
 #else
-  if(ngtcp2_crypto_openssl_configure_client_context(ssl_ctx) != 0) {
-    failf(data, "ngtcp2_crypto_openssl_configure_client_context failed");
+  if(ngtcp2_crypto_quictls_configure_client_context(ssl_ctx) != 0) {
+    failf(data, "ngtcp2_crypto_quictls_configure_client_context failed");
     goto out;
   }
 #endif
@@ -686,7 +710,7 @@ static void report_consumed_data(struct Curl_cfilter *cf,
                                  struct Curl_easy *data,
                                  size_t consumed)
 {
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
 
   if(!stream)
@@ -902,13 +926,13 @@ static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid,
   return 0;
 }
 
-static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_crypto_level level,
+static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level,
                           void *user_data)
 {
   struct Curl_cfilter *cf = user_data;
   (void)tconn;
 
-  if(level != NGTCP2_CRYPTO_LEVEL_APPLICATION) {
+  if(level != NGTCP2_ENCRYPTION_LEVEL_1RTT) {
     return 0;
   }
 
@@ -962,6 +986,61 @@ static ngtcp2_callbacks ng_callbacks = {
   NULL, /* early_data_rejected */
 };
 
+/**
+ * Connection maintenance like timeouts on packet ACKs etc. are done by us, not
+ * the OS like for TCP. POLL events on the socket therefore are not
+ * sufficient.
+ * ngtcp2 tells us when it wants to be invoked again. We handle that via
+ * the `Curl_expire()` mechanisms.
+ */
+static CURLcode check_and_set_expiry(struct Curl_cfilter *cf,
+                                     struct Curl_easy *data,
+                                     struct pkt_io_ctx *pktx)
+{
+  struct cf_ngtcp2_ctx *ctx = cf->ctx;
+  struct pkt_io_ctx local_pktx;
+  ngtcp2_tstamp expiry;
+
+  if(!pktx) {
+    pktx_init(&local_pktx, cf, data);
+    pktx = &local_pktx;
+  }
+  else {
+    pktx->ts = timestamp();
+  }
+
+  expiry = ngtcp2_conn_get_expiry(ctx->qconn);
+  if(expiry != UINT64_MAX) {
+    if(expiry <= pktx->ts) {
+      CURLcode result;
+      int rv = ngtcp2_conn_handle_expiry(ctx->qconn, pktx->ts);
+      if(rv) {
+        failf(data, "ngtcp2_conn_handle_expiry returned error: %s",
+              ngtcp2_strerror(rv));
+        ngtcp2_ccerr_set_liberr(&ctx->last_error, rv, NULL, 0);
+        return CURLE_SEND_ERROR;
+      }
+      result = cf_progress_ingress(cf, data, pktx);
+      if(result)
+        return result;
+      result = cf_progress_egress(cf, data, pktx);
+      if(result)
+        return result;
+      /* ask again, things might have changed */
+      expiry = ngtcp2_conn_get_expiry(ctx->qconn);
+    }
+
+    if(expiry > pktx->ts) {
+      ngtcp2_duration timeout = expiry - pktx->ts;
+      if(timeout % NGTCP2_MILLISECONDS) {
+        timeout += NGTCP2_MILLISECONDS;
+      }
+      Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC);
+    }
+  }
+  return CURLE_OK;
+}
+
 static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf,
                                       struct Curl_easy *data,
                                       curl_socket_t *socks)
@@ -969,7 +1048,7 @@ static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf,
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
   struct SingleRequest *k = &data->req;
   int rv = GETSOCK_BLANK;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   struct cf_call_data save;
 
   CF_DATA_SAVE(save, cf, data);
@@ -991,15 +1070,15 @@ static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf,
   return rv;
 }
 
-static void drain_stream(struct Curl_cfilter *cf,
-                         struct Curl_easy *data)
+static void h3_drain_stream(struct Curl_cfilter *cf,
+                            struct Curl_easy *data)
 {
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   unsigned char bits;
 
   (void)cf;
   bits = CURL_CSELECT_IN;
-  if(stream && !stream->send_closed && stream->upload_left)
+  if(stream && stream->upload_left && !stream->send_closed)
     bits |= CURL_CSELECT_OUT;
   if(data->state.dselect_bits != bits) {
     data->state.dselect_bits = bits;
@@ -1013,7 +1092,7 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
 {
   struct Curl_cfilter *cf = user_data;
   struct Curl_easy *data = stream_user_data;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   (void)conn;
   (void)stream_id;
   (void)app_error_code;
@@ -1031,7 +1110,7 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
     stream->reset = TRUE;
     stream->send_closed = TRUE;
   }
-  drain_stream(cf, data);
+  h3_drain_stream(cf, data);
   return 0;
 }
 
@@ -1045,7 +1124,7 @@ static CURLcode write_resp_raw(struct Curl_cfilter *cf,
                                const void *mem, size_t memlen,
                                bool flow)
 {
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   CURLcode result = CURLE_OK;
   ssize_t nwritten;
 
@@ -1085,7 +1164,7 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
   (void)stream3_id;
 
   result = write_resp_raw(cf, data, buf, buflen, TRUE);
-  drain_stream(cf, data);
+  h3_drain_stream(cf, data);
   return result? -1 : 0;
 }
 
@@ -1110,7 +1189,7 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
 {
   struct Curl_cfilter *cf = user_data;
   struct Curl_easy *data = stream_user_data;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   CURLcode result = CURLE_OK;
   (void)conn;
   (void)stream_id;
@@ -1130,7 +1209,7 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
   if(stream->status_code / 100 != 1) {
     stream->resp_hds_complete = TRUE;
   }
-  drain_stream(cf, data);
+  h3_drain_stream(cf, data);
   return 0;
 }
 
@@ -1143,7 +1222,7 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
   nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
   nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
   struct Curl_easy *data = stream_user_data;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   CURLcode result = CURLE_OK;
   (void)conn;
   (void)stream_id;
@@ -1207,7 +1286,8 @@ static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id,
   (void)conn;
   (void)stream_user_data;
 
-  rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, stream_id, app_error_code);
+  rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id,
+                                        app_error_code);
   if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
   }
@@ -1225,7 +1305,7 @@ static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id,
   (void)conn;
   (void)data;
 
-  rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, stream_id,
+  rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id,
                                          app_error_code);
   DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] reset -> %d", stream_id, rv));
   if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) {
@@ -1249,7 +1329,8 @@ static nghttp3_callbacks ngh3_callbacks = {
   cb_h3_stop_sending,
   NULL, /* end_stream */
   cb_h3_reset_stream,
-  NULL /* shutdown */
+  NULL, /* shutdown */
+  NULL /* recv_settings */
 };
 
 static int init_ngh3_conn(struct Curl_cfilter *cf)
@@ -1314,7 +1395,7 @@ fail:
 
 static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
                                   struct Curl_easy *data,
-                                  struct stream_ctx *stream,
+                                  struct h3_stream_ctx *stream,
                                   CURLcode *err)
 {
   ssize_t nread = -1;
@@ -1364,9 +1445,10 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
                               char *buf, size_t len, CURLcode *err)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   ssize_t nread = -1;
   struct cf_call_data save;
+  struct pkt_io_ctx pktx;
 
   (void)ctx;
 
@@ -1377,6 +1459,8 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
   DEBUGASSERT(ctx->h3conn);
   *err = CURLE_OK;
 
+  pktx_init(&pktx, cf, data);
+
   if(!stream) {
     *err = CURLE_RECV_ERROR;
     goto out;
@@ -1392,7 +1476,7 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
     report_consumed_data(cf, data, nread);
   }
 
-  if(cf_process_ingress(cf, data)) {
+  if(cf_progress_ingress(cf, data, &pktx)) {
     *err = CURLE_RECV_ERROR;
     nread = -1;
     goto out;
@@ -1410,7 +1494,7 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
   }
 
   if(nread > 0) {
-    drain_stream(cf, data);
+    h3_drain_stream(cf, data);
   }
   else {
     if(stream->closed) {
@@ -1422,10 +1506,17 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
   }
 
 out:
-  if(cf_flush_egress(cf, data)) {
+  if(cf_progress_egress(cf, data, &pktx)) {
     *err = CURLE_SEND_ERROR;
     nread = -1;
   }
+  else {
+    CURLcode result2 = check_and_set_expiry(cf, data, &pktx);
+    if(result2) {
+      *err = result2;
+      nread = -1;
+    }
+  }
   DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_recv(len=%zu) -> %zd, %d",
                 stream? stream->id : -1, len, nread, *err));
   CF_DATA_RESTORE(cf, save);
@@ -1438,7 +1529,7 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id,
 {
   struct Curl_cfilter *cf = user_data;
   struct Curl_easy *data = stream_user_data;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   size_t skiplen;
 
   (void)cf;
@@ -1454,10 +1545,8 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id,
   Curl_bufq_skip(&stream->sendbuf, skiplen);
   stream->sendbuf_len_in_flight -= skiplen;
 
-  /* `sendbuf` *might* now have more room. If so, resume this
-   * possibly paused stream. And also tell our transfer engine that
-   * it may continue KEEP_SEND if told to PAUSE. */
-  if(!Curl_bufq_is_full(&stream->sendbuf)) {
+  /* Everything ACKed, we resume upload processing */
+  if(!stream->sendbuf_len_in_flight) {
     int rv = nghttp3_conn_resume_stream(conn, stream_id);
     if(rv) {
       return NGTCP2_ERR_CALLBACK_FAILURE;
@@ -1465,7 +1554,7 @@ static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id,
     if((data->req.keepon & KEEP_SEND_HOLD) &&
        (data->req.keepon & KEEP_SEND)) {
       data->req.keepon &= ~KEEP_SEND_HOLD;
-      drain_stream(cf, data);
+      h3_drain_stream(cf, data);
       DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] unpausing acks",
                     stream_id));
     }
@@ -1481,7 +1570,7 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
 {
   struct Curl_cfilter *cf = user_data;
   struct Curl_easy *data = stream_user_data;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   ssize_t nwritten = 0;
   size_t nvecs = 0;
   (void)cf;
@@ -1530,8 +1619,10 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
   }
 
   DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] read req body -> "
-                "%d vecs%s with %zu (buffered=%zu, left=%zd)", stream->id,
-                (int)nvecs, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
+                          "%d vecs%s with %zu (buffered=%zu, left=%"
+                          CURL_FORMAT_CURL_OFF_T ")",
+                stream->id, (int)nvecs,
+                *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
                 nwritten, Curl_bufq_len(&stream->sendbuf),
                 stream->upload_left));
   return (nghttp3_ssize)nvecs;
@@ -1547,7 +1638,7 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf,
                               CURLcode *err)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = NULL;
+  struct h3_stream_ctx *stream = NULL;
   struct h1_req_parser h1;
   struct dynhds h2_headers;
   size_t nheader;
@@ -1614,16 +1705,19 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf,
     else
       /* data sending without specifying the data amount up front */
       stream->upload_left = -1; /* unknown */
-    reader.read_data = cb_h3_read_req_body;
-    preader = &reader;
     break;
   default:
     /* there is not request body */
     stream->upload_left = 0; /* no request body */
-    preader = NULL;
     break;
   }
 
+  stream->send_closed = (stream->upload_left == 0);
+  if(!stream->send_closed) {
+    reader.read_data = cb_h3_read_req_body;
+    preader = &reader;
+  }
+
   rc = nghttp3_conn_submit_request(ctx->h3conn, stream->id,
                                    nva, nheader, preader, data);
   if(rc) {
@@ -1642,8 +1736,7 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf,
     goto out;
   }
 
-  infof(data, "Using HTTP/3 Stream ID: %" PRId64 " (easy handle %p)",
-        stream->id, (void *)data);
+  infof(data, "Using HTTP/3 Stream ID: %" PRId64, stream->id);
   DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s",
                 stream->id, data->state.url));
 
@@ -1658,20 +1751,23 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                               const void *buf, size_t len, CURLcode *err)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  struct stream_ctx *stream = H3_STREAM_CTX(data);
+  struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   ssize_t sent = 0;
   struct cf_call_data save;
+  struct pkt_io_ctx pktx;
+  CURLcode result;
 
   CF_DATA_SAVE(save, cf, data);
   DEBUGASSERT(cf->connected);
   DEBUGASSERT(ctx->qconn);
   DEBUGASSERT(ctx->h3conn);
+  pktx_init(&pktx, cf, data);
   *err = CURLE_OK;
 
-  if(stream && stream->closed) {
-    *err = CURLE_HTTP3;
+  result = cf_progress_ingress(cf, data, &pktx);
+  if(result) {
+    *err = result;
     sent = -1;
-    goto out;
   }
 
   if(!stream || stream->id < 0) {
@@ -1681,32 +1777,66 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
       goto out;
     }
   }
+  else if(stream->upload_blocked_len) {
+    /* the data in `buf` has alread been submitted or added to the
+     * buffers, but have been EAGAINed on the last invocation. */
+    DEBUGASSERT(len >= stream->upload_blocked_len);
+    if(len < stream->upload_blocked_len) {
+      /* Did we get called again with a smaller `len`? This should not
+       * happen. We are not prepared to handle that. */
+      failf(data, "HTTP/3 send again with decreased length");
+      *err = CURLE_HTTP3;
+      sent = -1;
+      goto out;
+    }
+    sent = (ssize_t)stream->upload_blocked_len;
+    stream->upload_blocked_len = 0;
+  }
+  else if(stream->closed) {
+    *err = CURLE_HTTP3;
+    sent = -1;
+    goto out;
+  }
   else {
     sent = Curl_bufq_write(&stream->sendbuf, buf, len, err);
     DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send, add to "
                   "sendbuf(len=%zu) -> %zd, %d",
                   stream->id, len, sent, *err));
     if(sent < 0) {
-      if(*err == CURLE_AGAIN) {
-        /* Can't add more to the send buf, needs to drain first.
-         * Pause the sending to avoid a busy loop. */
-        data->req.keepon |= KEEP_SEND_HOLD;
-        DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] pause send",
-                      stream->id));
-      }
       goto out;
     }
 
     (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id);
   }
 
-  if(cf_flush_egress(cf, data)) {
-    *err = CURLE_SEND_ERROR;
+  result = cf_progress_egress(cf, data, &pktx);
+  if(result) {
+    *err = result;
     sent = -1;
-    goto out;
+  }
+
+  if(stream && sent > 0 && stream->sendbuf_len_in_flight) {
+    /* We have unacknowledged DATA and cannot report success to our
+     * caller. Instead we EAGAIN and remember how much we have already
+     * "written" into our various internal connection buffers.
+     * We put the stream upload on HOLD, until this gets ACKed. */
+    stream->upload_blocked_len = sent;
+    DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu), "
+                  "%zu bytes in flight -> EGAIN", stream->id, len,
+                  stream->sendbuf_len_in_flight));
+    *err = CURLE_AGAIN;
+    sent = -1;
+    data->req.keepon |= KEEP_SEND_HOLD;
   }
 
 out:
+  result = check_and_set_expiry(cf, data, &pktx);
+  if(result) {
+    *err = result;
+    sent = -1;
+  }
+  DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu) -> %zd, %d",
+                stream? stream->id : -1, len, sent, *err));
   CF_DATA_RESTORE(cf, save);
   return sent;
 }
@@ -1763,34 +1893,27 @@ static CURLcode qng_verify_peer(struct Curl_cfilter *cf,
   return result;
 }
 
-struct recv_ctx {
-  struct Curl_cfilter *cf;
-  struct Curl_easy *data;
-  ngtcp2_tstamp ts;
-  size_t pkt_count;
-};
-
 static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen,
                          struct sockaddr_storage *remote_addr,
                          socklen_t remote_addrlen, int ecn,
                          void *userp)
 {
-  struct recv_ctx *r = userp;
-  struct cf_ngtcp2_ctx *ctx = r->cf->ctx;
+  struct pkt_io_ctx *pktx = userp;
+  struct cf_ngtcp2_ctx *ctx = pktx->cf->ctx;
   ngtcp2_pkt_info pi;
   ngtcp2_path path;
   int rv;
 
-  ++r->pkt_count;
+  ++pktx->pkt_count;
   ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr,
                    ctx->q.local_addrlen);
   ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr,
                    remote_addrlen);
   pi.ecn = (uint32_t)ecn;
 
-  rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, r->ts);
+  rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, pktx->ts);
   if(rv) {
-    DEBUGF(LOG_CF(r->data, r->cf, "ingress, read_pkt -> %s",
+    DEBUGF(LOG_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s",
                   ngtcp2_strerror(rv)));
     if(!ctx->last_error.error_code) {
       if(rv == NGTCP2_ERR_CRYPTO) {
@@ -1813,41 +1936,40 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen,
   return CURLE_OK;
 }
 
-static CURLcode cf_process_ingress(struct Curl_cfilter *cf,
-                                   struct Curl_easy *data)
+static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
+                                    struct Curl_easy *data,
+                                    struct pkt_io_ctx *pktx)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  struct recv_ctx rctx;
+  struct pkt_io_ctx local_pktx;
   size_t pkts_chunk = 128, i;
   size_t pkts_max = 10 * pkts_chunk;
-  CURLcode result;
+  CURLcode result = CURLE_OK;
 
-  rctx.cf = cf;
-  rctx.data = data;
-  rctx.ts = timestamp();
-  rctx.pkt_count = 0;
+  if(!pktx) {
+    pktx_init(&local_pktx, cf, data);
+    pktx = &local_pktx;
+  }
+  else {
+    pktx->ts = timestamp();
+  }
 
   for(i = 0; i < pkts_max; i += pkts_chunk) {
-    rctx.pkt_count = 0;
+    pktx->pkt_count = 0;
     result = vquic_recv_packets(cf, data, &ctx->q, pkts_chunk,
-                                recv_pkt, &rctx);
+                                recv_pkt, pktx);
     if(result) /* error */
       break;
-    if(rctx.pkt_count < pkts_chunk) /* got less than we could */
+    if(pktx->pkt_count < pkts_chunk) /* got less than we could */
       break;
     /* give egress a chance before we receive more */
-    result = cf_flush_egress(cf, data);
+    result = cf_progress_egress(cf, data, pktx);
+    if(result) /* error */
+      break;
   }
   return result;
 }
 
-struct read_ctx {
-  struct Curl_cfilter *cf;
-  struct Curl_easy *data;
-  ngtcp2_tstamp ts;
-  ngtcp2_path_storage *ps;
-};
-
 /**
  * Read a network packet to send from ngtcp2 into `buf`.
  * Return number of bytes written or -1 with *err set.
@@ -1856,7 +1978,7 @@ static ssize_t read_pkt_to_send(void *userp,
                                 unsigned char *buf, size_t buflen,
                                 CURLcode *err)
 {
-  struct read_ctx *x = userp;
+  struct pkt_io_ctx *x = userp;
   struct cf_ngtcp2_ctx *ctx = x->cf->ctx;
   nghttp3_vec vec[16];
   nghttp3_ssize veccnt;
@@ -1896,7 +2018,7 @@ static ssize_t read_pkt_to_send(void *userp,
 
     flags = NGTCP2_WRITE_STREAM_FLAG_MORE |
             (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0);
-    n = ngtcp2_conn_writev_stream(ctx->qconn, x->ps? &x->ps->path : NULL,
+    n = ngtcp2_conn_writev_stream(ctx->qconn, &x->ps.path,
                                   NULL, buf, buflen,
                                   &ndatalen, flags, stream_id,
                                   (const ngtcp2_vec *)vec, veccnt, x->ts);
@@ -1955,28 +2077,25 @@ out:
   return nwritten;
 }
 
-static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
-                                struct Curl_easy *data)
+static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
+                                   struct Curl_easy *data,
+                                   struct pkt_io_ctx *pktx)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
-  int rv;
   ssize_t nread;
   size_t max_payload_size, path_max_payload_size, max_pktcnt;
   size_t pktcnt = 0;
   size_t gsolen = 0;  /* this disables gso until we have a clue */
-  ngtcp2_path_storage ps;
-  ngtcp2_tstamp ts = timestamp();
-  ngtcp2_tstamp expiry;
-  ngtcp2_duration timeout;
   CURLcode curlcode;
-  struct read_ctx readx;
+  struct pkt_io_ctx local_pktx;
 
-  rv = ngtcp2_conn_handle_expiry(ctx->qconn, ts);
-  if(rv) {
-    failf(data, "ngtcp2_conn_handle_expiry returned error: %s",
-          ngtcp2_strerror(rv));
-    ngtcp2_ccerr_set_liberr(&ctx->last_error, rv, NULL, 0);
-    return CURLE_SEND_ERROR;
+  if(!pktx) {
+    pktx_init(&local_pktx, cf, data);
+    pktx = &local_pktx;
+  }
+  else {
+    pktx->ts = timestamp();
+    ngtcp2_path_storage_zero(&pktx->ps);
   }
 
   curlcode = vquic_flush(cf, data, &ctx->q);
@@ -1988,8 +2107,6 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
     return curlcode;
   }
 
-  ngtcp2_path_storage_zero(&ps);
-
   /* In UDP, there is a maximum theoretical packet paload length and
    * a minimum payload length that is "guarantueed" to work.
    * To detect if this minimum payload can be increased, ngtcp2 sends
@@ -2008,15 +2125,10 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
   max_pktcnt = CURLMIN(MAX_PKT_BURST,
                        ctx->q.sendbuf.chunk_size / max_payload_size);
 
-  readx.cf = cf;
-  readx.data = data;
-  readx.ts = ts;
-  readx.ps = &ps;
-
   for(;;) {
     /* add the next packet to send, if any, to our buffer */
     nread = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size,
-                           read_pkt_to_send, &readx, &curlcode);
+                           read_pkt_to_send, pktx, &curlcode);
     /* DEBUGF(LOG_CF(data, cf, "sip packet(maxlen=%zu) -> %zd, %d",
                   max_payload_size, nread, curlcode)); */
     if(nread < 0) {
@@ -2076,21 +2188,6 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
   }
 
 out:
-  /* non-errored exit. check when we should run again. */
-  expiry = ngtcp2_conn_get_expiry(ctx->qconn);
-  if(expiry != UINT64_MAX) {
-    if(expiry <= ts) {
-      timeout = 0;
-    }
-    else {
-      timeout = expiry - ts;
-      if(timeout % NGTCP2_MILLISECONDS) {
-        timeout += NGTCP2_MILLISECONDS;
-      }
-    }
-    Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC);
-  }
-
   return CURLE_OK;
 }
 
@@ -2101,7 +2198,7 @@ out:
 static bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf,
                                    const struct Curl_easy *data)
 {
-  const struct stream_ctx *stream = H3_STREAM_CTX(data);
+  const struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
   (void)cf;
   return stream && !Curl_bufq_is_empty(&stream->recvbuf);
 }
@@ -2113,7 +2210,7 @@ static CURLcode h3_data_pause(struct Curl_cfilter *cf,
   /* TODO: there seems right now no API in ngtcp2 to shrink/enlarge
    * the streams windows. As we do in HTTP/2. */
   if(!pause) {
-    drain_stream(cf, data);
+    h3_drain_stream(cf, data);
     Curl_expire(data, 0, EXPIRE_RUN_NOW);
   }
   return CURLE_OK;
@@ -2141,7 +2238,7 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf,
     break;
   }
   case CF_CTRL_DATA_DONE_SEND: {
-    struct stream_ctx *stream = H3_STREAM_CTX(data);
+    struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
     if(stream && !stream->send_closed) {
       stream->send_closed = TRUE;
       stream->upload_left = Curl_bufq_len(&stream->sendbuf);
@@ -2150,11 +2247,7 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf,
     break;
   }
   case CF_CTRL_DATA_IDLE:
-    if(timestamp() >= ngtcp2_conn_get_expiry(ctx->qconn)) {
-      if(cf_flush_egress(cf, data)) {
-        result = CURLE_SEND_ERROR;
-      }
-    }
+    result = check_and_set_expiry(cf, data, NULL);
     break;
   default:
     break;
@@ -2250,7 +2343,8 @@ static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
  * Might be called twice for happy eyeballs.
  */
 static CURLcode cf_connect_start(struct Curl_cfilter *cf,
-                                 struct Curl_easy *data)
+                                 struct Curl_easy *data,
+                                 struct pkt_io_ctx *pktx)
 {
   struct cf_ngtcp2_ctx *ctx = cf->ctx;
   int rc;
@@ -2294,7 +2388,7 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
 
   (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd);
   ctx->qlogfd = qfd; /* -1 if failure above */
-  quic_settings(ctx, data);
+  quic_settings(ctx, data, pktx);
 
   result = vquic_ctx_init(&ctx->q);
   if(result)
@@ -2344,6 +2438,7 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
   CURLcode result = CURLE_OK;
   struct cf_call_data save;
   struct curltime now;
+  struct pkt_io_ctx pktx;
 
   if(cf->connected) {
     *done = TRUE;
@@ -2359,6 +2454,7 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
 
   *done = FALSE;
   now = Curl_now();
+  pktx_init(&pktx, cf, data);
 
   CF_DATA_SAVE(save, cf, data);
 
@@ -2370,19 +2466,19 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
 
   if(!ctx->qconn) {
     ctx->started_at = now;
-    result = cf_connect_start(cf, data);
+    result = cf_connect_start(cf, data, &pktx);
     if(result)
       goto out;
-    result = cf_flush_egress(cf, data);
+    result = cf_progress_egress(cf, data, &pktx);
     /* we do not expect to be able to recv anything yet */
     goto out;
   }
 
-  result = cf_process_ingress(cf, data);
+  result = cf_progress_ingress(cf, data, &pktx);
   if(result)
     goto out;
 
-  result = cf_flush_egress(cf, data);
+  result = cf_progress_egress(cf, data, &pktx);
   if(result)
     goto out;
 
@@ -2402,7 +2498,7 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
 
 out:
   if(result == CURLE_RECV_ERROR && ctx->qconn &&
-     ngtcp2_conn_is_in_draining_period(ctx->qconn)) {
+     ngtcp2_conn_in_draining_period(ctx->qconn)) {
     /* When a QUIC server instance is shutting down, it may send us a
      * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
      * state.
@@ -2439,6 +2535,9 @@ out:
           r_ip, r_port, curl_easy_strerror(result));
   }
 #endif
+  if(!result && ctx->qconn) {
+    result = check_and_set_expiry(cf, data, &pktx);
+  }
   DEBUGF(LOG_CF(data, cf, "connect -> %d, done=%d", result, *done));
   CF_DATA_RESTORE(cf, save);
   return result;
@@ -2510,13 +2609,11 @@ static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf,
        not in use by any other transfer, there shouldn't be any data here,
        only "protocol frames" */
     *input_pending = FALSE;
-    Curl_attach_connection(data, cf->conn);
-    if(cf_process_ingress(cf, data))
+    if(cf_progress_ingress(cf, data, NULL))
       alive = FALSE;
     else {
       alive = TRUE;
     }
-    Curl_detach_connection(data);
   }
 
   return alive;

+ 22 - 30
Utilities/cmcurl/lib/vquic/curl_quiche.c

@@ -277,7 +277,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
   stream->id = -1;
   Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
                   H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
-  DEBUGF(LOG_CF(data, cf, "data setup (easy %p)", (void *)data));
+  DEBUGF(LOG_CF(data, cf, "data setup"));
   return CURLE_OK;
 }
 
@@ -329,7 +329,7 @@ static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf,
   else {
     DEBUGASSERT(data->multi);
     for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
-      if(H3_STREAM_ID(sdata) == stream3_id) {
+      if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream3_id) {
         return sdata;
       }
     }
@@ -425,12 +425,8 @@ static ssize_t stream_resp_read(void *reader_ctx,
     *err = CURLE_OK;
     return nread;
   }
-  else if(nread < 0) {
-    *err = CURLE_AGAIN;
-    return -1;
-  }
   else {
-    *err = stream->resp_got_header? CURLE_PARTIAL_FILE : CURLE_RECV_ERROR;
+    *err = CURLE_AGAIN;
     return -1;
   }
 }
@@ -461,8 +457,8 @@ static CURLcode cf_recv_body(struct Curl_cfilter *cf,
   if(nwritten < 0 && result != CURLE_AGAIN) {
     DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] recv_body error %zd",
                   stream->id, nwritten));
-    failf(data, "Error %zd in HTTP/3 response body for stream[%"PRId64"]",
-          nwritten, stream->id);
+    failf(data, "Error %d in HTTP/3 response body for stream[%"PRId64"]",
+          result, stream->id);
     stream->closed = TRUE;
     stream->reset = TRUE;
     stream->send_closed = TRUE;
@@ -595,8 +591,13 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf,
                       "for [h3sid=%"PRId64"] -> %d",
                       stream? stream->id : -1, cf_ev_name(ev),
                       stream3_id, result));
-        quiche_h3_event_free(ev);
-        return result;
+        if(data == sdata) {
+          /* Only report this error to the caller if it is about the
+           * transfer we were called with. Otherwise we fail a transfer
+           * due to a problem in another one. */
+          quiche_h3_event_free(ev);
+          return result;
+        }
       }
       quiche_h3_event_free(ev);
     }
@@ -649,7 +650,7 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen,
     }
   }
   else if((size_t)nread < pktlen) {
-    DEBUGF(LOG_CF(r->data, r->cf, "ingress, quiche only read %zd/%zd bytes",
+    DEBUGF(LOG_CF(r->data, r->cf, "ingress, quiche only read %zd/%zu bytes",
                   nread, pktlen));
   }
 
@@ -826,7 +827,7 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
 
   if(!stream) {
     *err = CURLE_RECV_ERROR;
-    goto out;
+    return -1;
   }
 
   if(!Curl_bufq_is_empty(&stream->recvbuf)) {
@@ -883,8 +884,10 @@ out:
   }
   if(nread > 0)
     ctx->data_recvd += nread;
-  DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] cf_recv(total=%zd) -> %zd, %d",
-                stream->id, ctx->data_recvd, nread, *err));
+  DEBUGF(LOG_CF(data, cf, "[h3sid=%"PRId64"] cf_recv(total=%"
+                CURL_FORMAT_CURL_OFF_T ") -> %zd, %d",
+                stream ? stream->id : (int64_t)0,
+                ctx->data_recvd, nread, *err));
   return nread;
 }
 
@@ -909,8 +912,7 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf,
   if(!stream) {
     *err = h3_data_setup(cf, data);
     if(*err) {
-      nwritten = -1;
-      goto out;
+      return -1;
     }
     stream = H3_STREAM_CTX(data);
     DEBUGASSERT(stream);
@@ -995,8 +997,7 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf,
   stream->closed = FALSE;
   stream->reset = FALSE;
 
-  infof(data, "Using HTTP/3 Stream ID: %" PRId64 " (easy handle %p)",
-        stream3_id, (void *)data);
+  infof(data, "Using HTTP/3 Stream ID: %" PRId64, stream3_id);
   DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] opened for %s",
                 stream3_id, data->state.url));
 
@@ -1068,7 +1069,7 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data,
         stream->send_closed = TRUE;
 
       DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] send body(len=%zu, "
-                    "left=%zd) -> %zd",
+                              "left=%" CURL_FORMAT_CURL_OFF_T ") -> %zd",
                     stream->id, len, stream->upload_left, nwritten));
       *err = CURLE_OK;
     }
@@ -1151,10 +1152,8 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
   (void)arg1;
   (void)arg2;
   switch(event) {
-  case CF_CTRL_DATA_SETUP: {
-    result = h3_data_setup(cf, data);
+  case CF_CTRL_DATA_SETUP:
     break;
-  }
   case CF_CTRL_DATA_PAUSE:
     result = h3_data_pause(cf, data, (arg1 != 0));
     break;
@@ -1343,11 +1342,6 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
   }
 #endif
 
-  /* we do not get a setup event for the initial transfer */
-  result = h3_data_setup(cf, data);
-  if(result)
-    return result;
-
   result = cf_flush_egress(cf, data);
   if(result)
     return result;
@@ -1555,13 +1549,11 @@ static bool cf_quiche_conn_is_alive(struct Curl_cfilter *cf,
        not in use by any other transfer, there shouldn't be any data here,
        only "protocol frames" */
     *input_pending = FALSE;
-    Curl_attach_connection(data, cf->conn);
     if(cf_process_ingress(cf, data))
       alive = FALSE;
     else {
       alive = TRUE;
     }
-    Curl_detach_connection(data);
   }
 
   return alive;

+ 3 - 3
Utilities/cmcurl/lib/vquic/vquic.c

@@ -362,7 +362,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
   }
 
 out:
-  DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zd bytes -> %d",
+  DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
                 pkts, total_nread, result));
   return result;
 }
@@ -425,7 +425,7 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf,
   }
 
 out:
-  DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zd bytes -> %d",
+  DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
                 pkts, total_nread, result));
   return result;
 }
@@ -482,7 +482,7 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf,
   }
 
 out:
-  DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zd bytes -> %d",
+  DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
                 pkts, total_nread, result));
   return result;
 }

+ 13 - 13
Utilities/cmcurl/lib/vssh/libssh2.c

@@ -100,11 +100,9 @@
 
 /* Local functions: */
 static const char *sftp_libssh2_strerror(unsigned long err);
-#ifdef CURL_LIBSSH2_DEBUG
 static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc);
 static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc);
 static LIBSSH2_FREE_FUNC(my_libssh2_free);
-#endif
 static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data);
 static CURLcode ssh_connect(struct Curl_easy *data, bool *done);
 static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done);
@@ -284,8 +282,6 @@ static CURLcode libssh2_session_error_to_CURLE(int err)
   return CURLE_SSH;
 }
 
-#ifdef CURL_LIBSSH2_DEBUG
-
 static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc)
 {
   (void)abstract; /* arg not used */
@@ -305,8 +301,6 @@ static LIBSSH2_FREE_FUNC(my_libssh2_free)
     free(ptr);
 }
 
-#endif
-
 /*
  * SSH State machine related code
  */
@@ -895,6 +889,7 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data)
     }
 
     if(found) {
+      int rc;
       infof(data, "Found host %s in %s",
             conn->host.name, data->set.str[STRING_SSH_KNOWNHOSTS]);
 
@@ -944,9 +939,15 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data)
       }
 
       infof(data, "Set \"%s\" as SSH hostkey type", hostkey_method);
-      result = libssh2_session_error_to_CURLE(
-        libssh2_session_method_pref(
-          sshc->ssh_session, LIBSSH2_METHOD_HOSTKEY, hostkey_method));
+      rc = libssh2_session_method_pref(sshc->ssh_session,
+                                       LIBSSH2_METHOD_HOSTKEY, hostkey_method);
+      if(rc) {
+        char *errmsg = NULL;
+        int errlen;
+        libssh2_session_last_error(sshc->ssh_session, &errmsg, &errlen, 0);
+        failf(data, "libssh2: %s", errmsg);
+        result = libssh2_session_error_to_CURLE(rc);
+      }
     }
     else {
       infof(data, "Did not find host %s in %s",
@@ -3268,13 +3269,12 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done)
   sock = conn->sock[FIRSTSOCKET];
 #endif /* CURL_LIBSSH2_DEBUG */
 
-#ifdef CURL_LIBSSH2_DEBUG
+  /* libcurl MUST to set custom memory functions so that the kbd_callback
+     funciton's memory allocations can be properled freed */
   sshc->ssh_session = libssh2_session_init_ex(my_libssh2_malloc,
                                               my_libssh2_free,
                                               my_libssh2_realloc, data);
-#else
-  sshc->ssh_session = libssh2_session_init_ex(NULL, NULL, NULL, data);
-#endif
+
   if(!sshc->ssh_session) {
     failf(data, "Failure initialising ssh session");
     return CURLE_FAILED_INIT;

+ 1 - 1
Utilities/cmcurl/lib/vssh/wolfssh.c

@@ -277,7 +277,7 @@ static ssize_t wsftp_send(struct Curl_easy *data, int sockindex,
     return -1;
   }
   DEBUGASSERT(rc == (int)len);
-  infof(data, "sent %zd bytes SFTP from offset %zd",
+  infof(data, "sent %zu bytes SFTP from offset %" CURL_FORMAT_CURL_OFF_T,
         len, sshc->offset);
   sshc->offset += len;
   return (ssize_t)rc;

+ 21 - 11
Utilities/cmcurl/lib/vtls/bearssl.c

@@ -52,7 +52,7 @@ struct x509_context {
   int cert_num;
 };
 
-struct ssl_backend_data {
+struct bearssl_ssl_backend_data {
   br_ssl_client_context ctx;
   struct x509_context x509;
   unsigned char buf[BR_SSL_BUFSIZE_BIDI];
@@ -574,7 +574,8 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
                                       struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
@@ -751,7 +752,8 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
                                   unsigned target)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
   unsigned state;
   unsigned char *buf;
   size_t len;
@@ -820,7 +822,8 @@ static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf,
                                       struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
   CURLcode ret;
 
   DEBUGASSERT(backend);
@@ -842,7 +845,8 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
                                       struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   CURLcode ret;
 
@@ -889,7 +893,8 @@ static ssize_t bearssl_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                             const void *buf, size_t len, CURLcode *err)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
   unsigned char *app;
   size_t applen;
 
@@ -923,7 +928,8 @@ static ssize_t bearssl_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
                             char *buf, size_t len, CURLcode *err)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
   unsigned char *app;
   size_t applen;
 
@@ -1050,10 +1056,12 @@ static bool bearssl_data_pending(struct Curl_cfilter *cf,
                                  const struct Curl_easy *data)
 {
   struct ssl_connect_data *ctx = cf->ctx;
+  struct bearssl_ssl_backend_data *backend;
 
   (void)data;
   DEBUGASSERT(ctx && ctx->backend);
-  return br_ssl_engine_current_state(&ctx->backend->ctx.eng) & BR_SSL_RECVAPP;
+  backend = (struct bearssl_ssl_backend_data *)ctx->backend;
+  return br_ssl_engine_current_state(&backend->ctx.eng) & BR_SSL_RECVAPP;
 }
 
 static CURLcode bearssl_random(struct Curl_easy *data UNUSED_PARAM,
@@ -1101,7 +1109,8 @@ static CURLcode bearssl_connect_nonblocking(struct Curl_cfilter *cf,
 static void *bearssl_get_internals(struct ssl_connect_data *connssl,
                                    CURLINFO info UNUSED_PARAM)
 {
-  struct ssl_backend_data *backend = connssl->backend;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
   DEBUGASSERT(backend);
   return &backend->ctx;
 }
@@ -1109,7 +1118,8 @@ static void *bearssl_get_internals(struct ssl_connect_data *connssl,
 static void bearssl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct bearssl_ssl_backend_data *backend =
+    (struct bearssl_ssl_backend_data *)connssl->backend;
   size_t i;
 
   DEBUGASSERT(backend);
@@ -1147,7 +1157,7 @@ static CURLcode bearssl_sha256sum(const unsigned char *input,
 const struct Curl_ssl Curl_ssl_bearssl = {
   { CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */
   SSLSUPP_CAINFO_BLOB | SSLSUPP_SSL_CTX | SSLSUPP_HTTPS_PROXY,
-  sizeof(struct ssl_backend_data),
+  sizeof(struct bearssl_ssl_backend_data),
 
   Curl_none_init,                  /* init */
   Curl_none_cleanup,               /* cleanup */

+ 8 - 6
Utilities/cmcurl/lib/vtls/gskit.c

@@ -103,14 +103,14 @@
 #define CURL_GSKPROTO_TLSV12_MASK        (1 << CURL_GSKPROTO_TLSV12)
 #define CURL_GSKPROTO_LAST      5
 
-struct ssl_backend_data {
+struct gskit_ssl_backend_data {
   gsk_handle handle;
   int iocport;
   int localfd;
   int remotefd;
 };
 
-#define BACKEND connssl->backend
+#define BACKEND ((struct gskit_ssl_backend_data *)connssl->backend)
 
 /* Supported ciphers. */
 struct gskit_cipher {
@@ -518,6 +518,7 @@ static int pipe_ssloverssl(struct Curl_cfilter *cf, struct Curl_easy *data,
   struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next);
   struct ssl_connect_data *connssl_next = cf_ssl_next?
                                             cf_ssl_next->ctx : NULL;
+  struct gskit_ssl_backend_data *backend_next;
   struct pollfd fds[2];
   int n;
   int m;
@@ -531,6 +532,8 @@ static int pipe_ssloverssl(struct Curl_cfilter *cf, struct Curl_easy *data,
     return 0;   /* No SSL over SSL: OK. */
 
   DEBUGASSERT(connssl_next->backend);
+  backend_next = (struct gskit_ssl_backend_data *)connssl_next->backend;
+
   n = 1;
   fds[0].fd = BACKEND->remotefd;
   fds[1].fd = Curl_conn_cf_get_socket(cf, data);
@@ -550,8 +553,7 @@ static int pipe_ssloverssl(struct Curl_cfilter *cf, struct Curl_easy *data,
   if(fds[0].revents & POLLOUT) {
     /* Try getting data from HTTPS proxy and pipe it upstream. */
     n = 0;
-    i = gsk_secure_soc_read(connssl_next->backend->handle,
-                            buf, sizeof(buf), &n);
+    i = gsk_secure_soc_read(backend_next->handle, buf, sizeof(buf), &n);
     switch(i) {
     case GSK_OK:
       if(n) {
@@ -575,7 +577,7 @@ static int pipe_ssloverssl(struct Curl_cfilter *cf, struct Curl_easy *data,
     if(n < 0)
       return -1;
     if(n) {
-      i = gsk_secure_soc_write(connssl_next->backend->handle, buf, n, &m);
+      i = gsk_secure_soc_write(backend_next->handle, buf, n, &m);
       if(i != GSK_OK || n != m)
         return -1;
       ret = 1;
@@ -1294,7 +1296,7 @@ const struct Curl_ssl Curl_ssl_gskit = {
   SSLSUPP_CERTINFO |
   SSLSUPP_PINNEDPUBKEY,
 
-  sizeof(struct ssl_backend_data),
+  sizeof(struct gskit_ssl_backend_data),
 
   gskit_init,                     /* init */
   gskit_cleanup,                  /* cleanup */

+ 28 - 14
Utilities/cmcurl/lib/vtls/gtls.c

@@ -76,7 +76,7 @@ static bool gtls_inited = FALSE;
 
 # include <gnutls/ocsp.h>
 
-struct ssl_backend_data {
+struct gtls_ssl_backend_data {
   struct gtls_instance gtls;
 };
 
@@ -91,7 +91,9 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen)
   DEBUGASSERT(data);
   nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
   if(nwritten < 0) {
-    gnutls_transport_set_errno(connssl->backend->gtls.session,
+    struct gtls_ssl_backend_data *backend =
+      (struct gtls_ssl_backend_data *)connssl->backend;
+    gnutls_transport_set_errno(backend->gtls.session,
                                (CURLE_AGAIN == result)? EAGAIN : EINVAL);
     nwritten = -1;
   }
@@ -109,7 +111,9 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen)
   DEBUGASSERT(data);
   nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
   if(nread < 0) {
-    gnutls_transport_set_errno(connssl->backend->gtls.session,
+    struct gtls_ssl_backend_data *backend =
+      (struct gtls_ssl_backend_data *)connssl->backend;
+    gnutls_transport_set_errno(backend->gtls.session,
                                (CURLE_AGAIN == result)? EAGAIN : EINVAL);
     nread = -1;
   }
@@ -212,7 +216,8 @@ static CURLcode handshake(struct Curl_cfilter *cf,
                           bool nonblocking)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct gtls_ssl_backend_data *backend =
+    (struct gtls_ssl_backend_data *)connssl->backend;
   gnutls_session_t session;
   curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
 
@@ -679,7 +684,8 @@ static CURLcode
 gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct gtls_ssl_backend_data *backend =
+    (struct gtls_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   long * const pverifyresult = &ssl_config->certverifyresult;
@@ -1346,7 +1352,8 @@ gtls_connect_common(struct Curl_cfilter *cf,
 
   /* Finish connecting once the handshake is done */
   if(ssl_connect_1 == connssl->connecting_state) {
-    struct ssl_backend_data *backend = connssl->backend;
+    struct gtls_ssl_backend_data *backend =
+      (struct gtls_ssl_backend_data *)connssl->backend;
     gnutls_session_t session;
     DEBUGASSERT(backend);
     session = backend->gtls.session;
@@ -1390,11 +1397,13 @@ static bool gtls_data_pending(struct Curl_cfilter *cf,
                               const struct Curl_easy *data)
 {
   struct ssl_connect_data *ctx = cf->ctx;
+  struct gtls_ssl_backend_data *backend;
 
   (void)data;
   DEBUGASSERT(ctx && ctx->backend);
-  if(ctx->backend->gtls.session &&
-     0 != gnutls_record_check_pending(ctx->backend->gtls.session))
+  backend = (struct gtls_ssl_backend_data *)ctx->backend;
+  if(backend->gtls.session &&
+     0 != gnutls_record_check_pending(backend->gtls.session))
     return TRUE;
   return FALSE;
 }
@@ -1406,7 +1415,8 @@ static ssize_t gtls_send(struct Curl_cfilter *cf,
                          CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct gtls_ssl_backend_data *backend =
+    (struct gtls_ssl_backend_data *)connssl->backend;
   ssize_t rc;
 
   (void)data;
@@ -1428,7 +1438,8 @@ static void gtls_close(struct Curl_cfilter *cf,
                        struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct gtls_ssl_backend_data *backend =
+    (struct gtls_ssl_backend_data *)connssl->backend;
 
   (void) data;
   DEBUGASSERT(backend);
@@ -1463,7 +1474,8 @@ static int gtls_shutdown(struct Curl_cfilter *cf,
 {
   struct ssl_connect_data *connssl = cf->ctx;
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-  struct ssl_backend_data *backend = connssl->backend;
+  struct gtls_ssl_backend_data *backend =
+    (struct gtls_ssl_backend_data *)connssl->backend;
   int retval = 0;
 
   DEBUGASSERT(backend);
@@ -1541,7 +1553,8 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf,
                          CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct gtls_ssl_backend_data *backend =
+    (struct gtls_ssl_backend_data *)connssl->backend;
   ssize_t ret;
 
   (void)data;
@@ -1620,7 +1633,8 @@ static bool gtls_cert_status_request(void)
 static void *gtls_get_internals(struct ssl_connect_data *connssl,
                                 CURLINFO info UNUSED_PARAM)
 {
-  struct ssl_backend_data *backend = connssl->backend;
+  struct gtls_ssl_backend_data *backend =
+    (struct gtls_ssl_backend_data *)connssl->backend;
   (void)info;
   DEBUGASSERT(backend);
   return backend->gtls.session;
@@ -1634,7 +1648,7 @@ const struct Curl_ssl Curl_ssl_gnutls = {
   SSLSUPP_PINNEDPUBKEY |
   SSLSUPP_HTTPS_PROXY,
 
-  sizeof(struct ssl_backend_data),
+  sizeof(struct gtls_ssl_backend_data),
 
   gtls_init,                     /* init */
   gtls_cleanup,                  /* cleanup */

+ 21 - 11
Utilities/cmcurl/lib/vtls/mbedtls.c

@@ -81,7 +81,7 @@
 #  endif
 #endif
 
-struct ssl_backend_data {
+struct mbed_ssl_backend_data {
   mbedtls_ctr_drbg_context ctr_drbg;
   mbedtls_entropy_context entropy;
   mbedtls_ssl_context ssl;
@@ -255,7 +255,8 @@ static CURLcode
 set_ssl_version_min_max(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct mbed_ssl_backend_data *backend =
+    (struct mbed_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
 #if MBEDTLS_VERSION_NUMBER >= 0x03000000
   int mbedtls_ver_min = MBEDTLS_SSL_MINOR_VERSION_3;
@@ -307,7 +308,8 @@ static CURLcode
 mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct mbed_ssl_backend_data *backend =
+    (struct mbed_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
@@ -697,7 +699,8 @@ mbed_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   int ret;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct mbed_ssl_backend_data *backend =
+    (struct mbed_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   const mbedtls_x509_crt *peercert;
   const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)?
@@ -860,7 +863,8 @@ mbed_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   CURLcode retcode = CURLE_OK;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct mbed_ssl_backend_data *backend =
+    (struct mbed_ssl_backend_data *)connssl->backend;
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
 
   DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
@@ -915,7 +919,8 @@ static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data,
                          CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct mbed_ssl_backend_data *backend =
+    (struct mbed_ssl_backend_data *)connssl->backend;
   int ret = -1;
 
   (void)data;
@@ -939,7 +944,8 @@ static void mbedtls_close_all(struct Curl_easy *data)
 static void mbedtls_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct mbed_ssl_backend_data *backend =
+    (struct mbed_ssl_backend_data *)connssl->backend;
   char buf[32];
 
   (void)data;
@@ -968,7 +974,8 @@ static ssize_t mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
                          CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct mbed_ssl_backend_data *backend =
+    (struct mbed_ssl_backend_data *)connssl->backend;
   int ret = -1;
   ssize_t len = -1;
 
@@ -1204,10 +1211,12 @@ static bool mbedtls_data_pending(struct Curl_cfilter *cf,
                                  const struct Curl_easy *data)
 {
   struct ssl_connect_data *ctx = cf->ctx;
+  struct mbed_ssl_backend_data *backend;
 
   (void)data;
   DEBUGASSERT(ctx && ctx->backend);
-  return mbedtls_ssl_get_bytes_avail(&ctx->backend->ssl) != 0;
+  backend = (struct mbed_ssl_backend_data *)ctx->backend;
+  return mbedtls_ssl_get_bytes_avail(&backend->ssl) != 0;
 }
 
 static CURLcode mbedtls_sha256sum(const unsigned char *input,
@@ -1234,7 +1243,8 @@ static CURLcode mbedtls_sha256sum(const unsigned char *input,
 static void *mbedtls_get_internals(struct ssl_connect_data *connssl,
                                    CURLINFO info UNUSED_PARAM)
 {
-  struct ssl_backend_data *backend = connssl->backend;
+  struct mbed_ssl_backend_data *backend =
+    (struct mbed_ssl_backend_data *)connssl->backend;
   (void)info;
   DEBUGASSERT(backend);
   return &backend->ssl;
@@ -1249,7 +1259,7 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
   SSLSUPP_SSL_CTX |
   SSLSUPP_HTTPS_PROXY,
 
-  sizeof(struct ssl_backend_data),
+  sizeof(struct mbed_ssl_backend_data),
 
   mbedtls_init,                     /* init */
   mbedtls_cleanup,                  /* cleanup */

+ 55 - 26
Utilities/cmcurl/lib/vtls/nss.c

@@ -81,7 +81,7 @@
 /* enough to fit the string "PEM Token #[0|1]" */
 #define SLOTSIZE 13
 
-struct ssl_backend_data {
+struct nss_ssl_backend_data {
   PRFileDesc *handle;
   char *client_nickname;
   struct Curl_easy *data;
@@ -489,7 +489,8 @@ static CURLcode nss_create_object(struct ssl_connect_data *connssl,
 
   const int slot_id = (cacert) ? 0 : 1;
   char *slot_name = aprintf("PEM Token #%d", slot_id);
-  struct ssl_backend_data *backend = connssl->backend;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
 
   DEBUGASSERT(backend);
 
@@ -806,7 +807,9 @@ static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig,
   struct Curl_cfilter *cf = (struct Curl_cfilter *)arg;
   struct ssl_connect_data *connssl = cf->ctx;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
-  struct Curl_easy *data = connssl->backend->data;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
+  struct Curl_easy *data = backend->data;
 
   DEBUGASSERT(data);
 #ifdef SSL_ENABLE_OCSP_STAPLING
@@ -851,7 +854,9 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg)
 {
   struct Curl_cfilter *cf = (struct Curl_cfilter *)arg;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct Curl_easy *data = connssl->backend->data;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
+  struct Curl_easy *data = backend->data;
   unsigned int buflenmax = 50;
   unsigned char buf[50];
   unsigned int buflen;
@@ -1055,7 +1060,9 @@ static SECStatus BadCertHandler(void *arg, PRFileDesc *sock)
 {
   struct Curl_cfilter *cf = (struct Curl_cfilter *)arg;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct Curl_easy *data = connssl->backend->data;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
+  struct Curl_easy *data = backend->data;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config;
   PRErrorCode err = PR_GetError();
@@ -1117,7 +1124,8 @@ static CURLcode cmp_peer_pubkey(struct ssl_connect_data *connssl,
                                 const char *pinnedpubkey)
 {
   CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
   struct Curl_easy *data = NULL;
   CERTCertificate *cert;
 
@@ -1173,7 +1181,8 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock,
                                   struct SECKEYPrivateKeyStr **pRetKey)
 {
   struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
   struct Curl_easy *data = NULL;
   const char *nickname = NULL;
   static const char pem_slotname[] = "PEM Token #1";
@@ -1538,7 +1547,8 @@ static void nss_cleanup(void)
 static void close_one(struct ssl_connect_data *connssl)
 {
   /* before the cleanup, check whether we are using a client certificate */
-  struct ssl_backend_data *backend = connssl->backend;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
   bool client_cert = true;
 
   DEBUGASSERT(backend);
@@ -1580,7 +1590,8 @@ static void close_one(struct ssl_connect_data *connssl)
 static void nss_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
   (void)data;
   DEBUGASSERT(backend);
 
@@ -1796,7 +1807,8 @@ static CURLcode nss_fail_connect(struct Curl_cfilter *cf,
                                  CURLcode curlerr)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
 
   DEBUGASSERT(backend);
 
@@ -1826,7 +1838,8 @@ static CURLcode nss_set_blocking(struct Curl_cfilter *cf,
 {
   struct ssl_connect_data *connssl = cf->ctx;
   PRSocketOptionData sock_opt;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
 
   DEBUGASSERT(backend);
 
@@ -1849,7 +1862,8 @@ static CURLcode nss_setup_connect(struct Curl_cfilter *cf,
   PRBool ssl_cbc_random_iv;
   curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   struct Curl_cfilter *cf_ssl_next = Curl_ssl_cf_get_ssl(cf->next);
@@ -2031,14 +2045,16 @@ static CURLcode nss_setup_connect(struct Curl_cfilter *cf,
   /* Is there an SSL filter "in front" of us or are we writing directly
    * to the socket? */
   if(connssl_next) {
+    struct nss_ssl_backend_data *backend_next =
+      (struct nss_ssl_backend_data *)connssl_next->backend;
     /* The filter should be connected by now, with full handshake */
-    DEBUGASSERT(connssl_next->backend->handle);
+    DEBUGASSERT(backend_next->handle);
     DEBUGASSERT(ssl_connection_complete == connssl_next->state);
     /* We tell our NSS instance to use do IO with the 'next' NSS
     * instance. This NSS instance will take ownership of the next
     * one, including its destruction. We therefore need to `disown`
     * the next filter's handle, once import succeeds. */
-    nspr_io = connssl_next->backend->handle;
+    nspr_io = backend->handle;
     second_layer = TRUE;
   }
   else {
@@ -2077,8 +2093,11 @@ static CURLcode nss_setup_connect(struct Curl_cfilter *cf,
 
   PR_Close(model); /* We don't need this any more */
   model = NULL;
-  if(connssl_next) /* steal the NSS handle we just imported successfully */
-    connssl_next->backend->handle = NULL;
+  if(connssl_next) { /* steal the NSS handle we just imported successfully */
+    struct nss_ssl_backend_data *backend_next =
+      (struct nss_ssl_backend_data *)connssl_next->backend;
+    backend_next->handle = NULL;
+  }
 
   /* This is the password associated with the cert that we're using */
   if(ssl_config->key_passwd) {
@@ -2154,7 +2173,8 @@ static CURLcode nss_do_connect(struct Curl_cfilter *cf,
                                struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   CURLcode result = CURLE_SSL_CONNECT_ERROR;
@@ -2299,7 +2319,8 @@ static ssize_t nss_send(struct Curl_cfilter *cf,
                         CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
   ssize_t rc;
 
   (void)data;
@@ -2337,7 +2358,9 @@ static bool
 nss_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  PRFileDesc *fd = connssl->backend->handle->lower;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
+  PRFileDesc *fd = backend->handle->lower;
   char buf;
 
   (void) data;
@@ -2353,7 +2376,8 @@ static ssize_t nss_recv(struct Curl_cfilter *cf,
                         CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
   ssize_t nread;
 
   (void)data;
@@ -2455,7 +2479,8 @@ static bool nss_false_start(void)
 static void *nss_get_internals(struct ssl_connect_data *connssl,
                                CURLINFO info UNUSED_PARAM)
 {
-  struct ssl_backend_data *backend = connssl->backend;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
   (void)info;
   DEBUGASSERT(backend);
   return backend->handle;
@@ -2465,9 +2490,11 @@ static bool nss_attach_data(struct Curl_cfilter *cf,
                             struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
 
-  if(!connssl->backend->data)
-    connssl->backend->data = data;
+  if(!backend->data)
+    backend->data = data;
   return TRUE;
 }
 
@@ -2475,9 +2502,11 @@ static void nss_detach_data(struct Curl_cfilter *cf,
                             struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
+  struct nss_ssl_backend_data *backend =
+    (struct nss_ssl_backend_data *)connssl->backend;
 
-  if(connssl->backend->data == data)
-    connssl->backend->data = NULL;
+  if(backend->data == data)
+    backend->data = NULL;
 }
 
 const struct Curl_ssl Curl_ssl_nss = {
@@ -2488,7 +2517,7 @@ const struct Curl_ssl Curl_ssl_nss = {
   SSLSUPP_PINNEDPUBKEY |
   SSLSUPP_HTTPS_PROXY,
 
-  sizeof(struct ssl_backend_data),
+  sizeof(struct nss_ssl_backend_data),
 
   nss_init,                     /* init */
   nss_cleanup,                  /* cleanup */

+ 49 - 32
Utilities/cmcurl/lib/vtls/openssl.c

@@ -296,7 +296,7 @@ typedef unsigned long sslerr_t;
 #define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
 #endif /* !LIBRESSL_VERSION_NUMBER */
 
-struct ssl_backend_data {
+struct ossl_ssl_backend_data {
   /* these ones requires specific SSL-types */
   SSL_CTX* ctx;
   SSL*     handle;
@@ -722,6 +722,8 @@ static int bio_cf_out_write(BIO *bio, const char *buf, int blen)
 {
   struct Curl_cfilter *cf = BIO_get_data(bio);
   struct ssl_connect_data *connssl = cf->ctx;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nwritten;
   CURLcode result = CURLE_SEND_ERROR;
@@ -731,7 +733,7 @@ static int bio_cf_out_write(BIO *bio, const char *buf, int blen)
   DEBUGF(LOG_CF(data, cf, "bio_cf_out_write(len=%d) -> %d, err=%d",
                 blen, (int)nwritten, result));
   BIO_clear_retry_flags(bio);
-  connssl->backend->io_result = result;
+  backend->io_result = result;
   if(nwritten < 0) {
     if(CURLE_AGAIN == result)
       BIO_set_retry_write(bio);
@@ -743,6 +745,8 @@ static int bio_cf_in_read(BIO *bio, char *buf, int blen)
 {
   struct Curl_cfilter *cf = BIO_get_data(bio);
   struct ssl_connect_data *connssl = cf->ctx;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nread;
   CURLcode result = CURLE_RECV_ERROR;
@@ -756,7 +760,7 @@ static int bio_cf_in_read(BIO *bio, char *buf, int blen)
   DEBUGF(LOG_CF(data, cf, "bio_cf_in_read(len=%d) -> %d, err=%d",
                 blen, (int)nread, result));
   BIO_clear_retry_flags(bio);
-  connssl->backend->io_result = result;
+  backend->io_result = result;
   if(nread < 0) {
     if(CURLE_AGAIN == result)
       BIO_set_retry_read(bio);
@@ -764,13 +768,13 @@ static int bio_cf_in_read(BIO *bio, char *buf, int blen)
 
   /* Before returning server replies to the SSL instance, we need
    * to have setup the x509 store or verification will fail. */
-  if(!connssl->backend->x509_store_setup) {
-    result = Curl_ssl_setup_x509_store(cf, data, connssl->backend->ctx);
+  if(!backend->x509_store_setup) {
+    result = Curl_ssl_setup_x509_store(cf, data, backend->ctx);
     if(result) {
-      connssl->backend->io_result = result;
+      backend->io_result = result;
       return -1;
     }
-    connssl->backend->x509_store_setup = TRUE;
+    backend->x509_store_setup = TRUE;
   }
 
   return (int)nread;
@@ -1885,7 +1889,8 @@ static struct curl_slist *ossl_engines_list(struct Curl_easy *data)
 static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
 
   (void)data;
   DEBUGASSERT(backend);
@@ -1931,7 +1936,8 @@ static int ossl_shutdown(struct Curl_cfilter *cf,
   int buffsize;
   int err;
   bool done = FALSE;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
   int loop = 10;
 
   DEBUGASSERT(backend);
@@ -2329,7 +2335,8 @@ static CURLcode verifystatus(struct Curl_cfilter *cf,
   OCSP_BASICRESP *br = NULL;
   X509_STORE     *st = NULL;
   STACK_OF(X509) *ch = NULL;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
   X509 *cert;
   OCSP_CERTID *id = NULL;
   int cert_status, crl_reason;
@@ -2729,7 +2736,7 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
 
 #ifdef HAS_MODERN_SET_PROTO_VER
 static CURLcode
-set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx)
+ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx)
 {
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   /* first, TLS min version... */
@@ -2826,9 +2833,9 @@ typedef long ctx_option_t;
 
 #if !defined(HAS_MODERN_SET_PROTO_VER)
 static CURLcode
-set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
-                               struct Curl_cfilter *cf,
-                               struct Curl_easy *data)
+ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
+                                       struct Curl_cfilter *cf,
+                                       struct Curl_easy *data)
 {
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   long ssl_version = conn_config->version;
@@ -2841,8 +2848,10 @@ set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
 #ifdef TLS1_3_VERSION
   {
     struct ssl_connect_data *connssl = cf->ctx;
-    DEBUGASSERT(connssl->backend);
-    SSL_CTX_set_max_proto_version(connssl->backend->ctx, TLS1_3_VERSION);
+    struct ossl_ssl_backend_data *backend =
+      (struct ossl_ssl_backend_data *)connssl->backend;
+    DEBUGASSERT(backend);
+    SSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION);
     *ctx_options |= SSL_OP_NO_TLSv1_2;
   }
 #else
@@ -3447,7 +3456,8 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
   const char * const ssl_cert_type = ssl_config->cert_type;
   const bool verifypeer = conn_config->verifypeer;
   char error_buffer[256];
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
 
   DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
   DEBUGASSERT(backend);
@@ -3589,9 +3599,9 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
     ctx_options |= SSL_OP_NO_SSLv3;
 
 #if HAS_MODERN_SET_PROTO_VER /* 1.1.0 */
-    result = set_ssl_version_min_max(cf, backend->ctx);
+    result = ossl_set_ssl_version_min_max(cf, backend->ctx);
 #else
-    result = set_ssl_version_min_max_legacy(&ctx_options, cf, data);
+    result = ossl_set_ssl_version_min_max_legacy(&ctx_options, cf, data);
 #endif
     if(result != CURLE_OK)
       return result;
@@ -3820,7 +3830,8 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
 {
   int err;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   DEBUGASSERT(ssl_connect_2 == connssl->connecting_state
               || ssl_connect_2_reading == connssl->connecting_state
@@ -3983,8 +3994,8 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
  * Heavily modified from:
  * https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning#OpenSSL
  */
-static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert,
-                                    const char *pinnedpubkey)
+static CURLcode ossl_pkp_pin_peer_pubkey(struct Curl_easy *data, X509* cert,
+                                         const char *pinnedpubkey)
 {
   /* Scratch */
   int len1 = 0, len2 = 0;
@@ -4062,7 +4073,8 @@ static CURLcode servercert(struct Curl_cfilter *cf,
   char buffer[2048];
   const char *ptr;
   BIO *mem = BIO_new(BIO_s_mem());
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
 
   DEBUGASSERT(backend);
 
@@ -4077,7 +4089,7 @@ static CURLcode servercert(struct Curl_cfilter *cf,
 
   if(data->set.ssl.certinfo)
     /* asked to gather certificate info */
-    (void)Curl_ossl_certchain(data, connssl->backend->handle);
+    (void)Curl_ossl_certchain(data, backend->handle);
 
   backend->server_cert = SSL_get1_peer_certificate(backend->handle);
   if(!backend->server_cert) {
@@ -4245,7 +4257,7 @@ static CURLcode servercert(struct Curl_cfilter *cf,
     data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
     data->set.str[STRING_SSL_PINNEDPUBLICKEY];
   if(!result && ptr) {
-    result = pkp_pin_peer_pubkey(data, backend->server_cert, ptr);
+    result = ossl_pkp_pin_peer_pubkey(data, backend->server_cert, ptr);
     if(result)
       failf(data, "SSL: public key does not match pinned public key");
   }
@@ -4414,11 +4426,13 @@ static CURLcode ossl_connect(struct Curl_cfilter *cf,
 static bool ossl_data_pending(struct Curl_cfilter *cf,
                               const struct Curl_easy *data)
 {
-  struct ssl_connect_data *ctx = cf->ctx;
+  struct ssl_connect_data *connssl = cf->ctx;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
 
   (void)data;
-  DEBUGASSERT(ctx && ctx->backend);
-  if(ctx->backend->handle && SSL_pending(ctx->backend->handle))
+  DEBUGASSERT(connssl && backend);
+  if(backend->handle && SSL_pending(backend->handle))
     return TRUE;
   return FALSE;
 }
@@ -4437,7 +4451,8 @@ static ssize_t ossl_send(struct Curl_cfilter *cf,
   int memlen;
   int rc;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
 
   (void)data;
   DEBUGASSERT(backend);
@@ -4533,7 +4548,8 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf,
   int buffsize;
   struct connectdata *conn = cf->conn;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
 
   (void)data;
   DEBUGASSERT(backend);
@@ -4756,7 +4772,8 @@ static void *ossl_get_internals(struct ssl_connect_data *connssl,
                                 CURLINFO info)
 {
   /* Legacy: CURLINFO_TLS_SESSION must return an SSL_CTX pointer. */
-  struct ssl_backend_data *backend = connssl->backend;
+  struct ossl_ssl_backend_data *backend =
+    (struct ossl_ssl_backend_data *)connssl->backend;
   DEBUGASSERT(backend);
   return info == CURLINFO_TLS_SESSION ?
     (void *)backend->ctx : (void *)backend->handle;
@@ -4789,7 +4806,7 @@ const struct Curl_ssl Curl_ssl_openssl = {
 #endif
   SSLSUPP_HTTPS_PROXY,
 
-  sizeof(struct ssl_backend_data),
+  sizeof(struct ossl_ssl_backend_data),
 
   ossl_init,                /* init */
   ossl_cleanup,             /* cleanup */

+ 22 - 12
Utilities/cmcurl/lib/vtls/rustls.c

@@ -40,7 +40,7 @@
 #include "strerror.h"
 #include "multiif.h"
 
-struct ssl_backend_data
+struct rustls_ssl_backend_data
 {
   const struct rustls_client_config *config;
   struct rustls_connection *conn;
@@ -67,10 +67,12 @@ static bool
 cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data)
 {
   struct ssl_connect_data *ctx = cf->ctx;
+  struct rustls_ssl_backend_data *backend;
 
   (void)data;
   DEBUGASSERT(ctx && ctx->backend);
-  return ctx->backend->data_pending;
+  backend = (struct rustls_ssl_backend_data *)ctx->backend;
+  return backend->data_pending;
 }
 
 static CURLcode
@@ -136,7 +138,8 @@ static ssize_t tls_recv_more(struct Curl_cfilter *cf,
                              struct Curl_easy *data, CURLcode *err)
 {
   struct ssl_connect_data *const connssl = cf->ctx;
-  struct ssl_backend_data *const backend = connssl->backend;
+  struct rustls_ssl_backend_data *const backend =
+    (struct rustls_ssl_backend_data *)connssl->backend;
   struct io_ctx io_ctx;
   size_t tls_bytes_read = 0;
   rustls_io_result io_error;
@@ -191,7 +194,8 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
             char *plainbuf, size_t plainlen, CURLcode *err)
 {
   struct ssl_connect_data *const connssl = cf->ctx;
-  struct ssl_backend_data *const backend = connssl->backend;
+  struct rustls_ssl_backend_data *const backend =
+    (struct rustls_ssl_backend_data *)connssl->backend;
   struct rustls_connection *rconn = NULL;
   size_t n = 0;
   size_t plain_bytes_copied = 0;
@@ -283,7 +287,8 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
         const void *plainbuf, size_t plainlen, CURLcode *err)
 {
   struct ssl_connect_data *const connssl = cf->ctx;
-  struct ssl_backend_data *const backend = connssl->backend;
+  struct rustls_ssl_backend_data *const backend =
+    (struct rustls_ssl_backend_data *)connssl->backend;
   struct rustls_connection *rconn = NULL;
   struct io_ctx io_ctx;
   size_t plainwritten = 0;
@@ -373,7 +378,7 @@ cr_hostname_is_ip(const char *hostname)
 
 static CURLcode
 cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
-                struct ssl_backend_data *const backend)
+                struct rustls_ssl_backend_data *const backend)
 {
   struct ssl_connect_data *connssl = cf->ctx;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
@@ -491,7 +496,8 @@ cr_connect_nonblocking(struct Curl_cfilter *cf,
 {
   struct ssl_connect_data *const connssl = cf->ctx;
   curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
-  struct ssl_backend_data *const backend = connssl->backend;
+  struct rustls_ssl_backend_data *const backend =
+    (struct rustls_ssl_backend_data *)connssl->backend;
   struct rustls_connection *rconn = NULL;
   CURLcode tmperr = CURLE_OK;
   int result;
@@ -504,7 +510,8 @@ cr_connect_nonblocking(struct Curl_cfilter *cf,
   DEBUGASSERT(backend);
 
   if(ssl_connection_none == connssl->state) {
-    result = cr_init_backend(cf, data, connssl->backend);
+    result = cr_init_backend(cf, data,
+               (struct rustls_ssl_backend_data *)connssl->backend);
     if(result != CURLE_OK) {
       return result;
     }
@@ -594,7 +601,8 @@ cr_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data,
 {
   struct ssl_connect_data *const connssl = cf->ctx;
   curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
-  struct ssl_backend_data *const backend = connssl->backend;
+  struct rustls_ssl_backend_data *const backend =
+    (struct rustls_ssl_backend_data *)connssl->backend;
   struct rustls_connection *rconn = NULL;
 
   (void)data;
@@ -617,7 +625,8 @@ static void *
 cr_get_internals(struct ssl_connect_data *connssl,
                  CURLINFO info UNUSED_PARAM)
 {
-  struct ssl_backend_data *backend = connssl->backend;
+  struct rustls_ssl_backend_data *backend =
+    (struct rustls_ssl_backend_data *)connssl->backend;
   DEBUGASSERT(backend);
   return &backend->conn;
 }
@@ -626,7 +635,8 @@ static void
 cr_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct rustls_ssl_backend_data *backend =
+    (struct rustls_ssl_backend_data *)connssl->backend;
   CURLcode tmperr = CURLE_OK;
   ssize_t n = 0;
 
@@ -659,7 +669,7 @@ const struct Curl_ssl Curl_ssl_rustls = {
   SSLSUPP_CAINFO_BLOB |            /* supports */
   SSLSUPP_TLS13_CIPHERSUITES |
   SSLSUPP_HTTPS_PROXY,
-  sizeof(struct ssl_backend_data),
+  sizeof(struct rustls_ssl_backend_data),
 
   Curl_none_init,                  /* init */
   Curl_none_cleanup,               /* cleanup */

+ 37 - 27
Utilities/cmcurl/lib/vtls/schannel.c

@@ -33,13 +33,12 @@
 
 #ifdef USE_SCHANNEL
 
-#define EXPOSE_SCHANNEL_INTERNAL_STRUCTS
-
 #ifndef USE_WINDOWS_SSPI
 #  error "Can't compile SCHANNEL support without SSPI."
 #endif
 
 #include "schannel.h"
+#include "schannel_int.h"
 #include "vtls.h"
 #include "vtls_int.h"
 #include "strcase.h"
@@ -186,9 +185,9 @@
 #define PKCS12_NO_PERSIST_KEY 0x00008000
 #endif
 
-static CURLcode pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
-                                    struct Curl_easy *data,
-                                    const char *pinnedpubkey);
+static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
+                                             struct Curl_easy *data,
+                                             const char *pinnedpubkey);
 
 static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType,
                           void *BufDataPtr, unsigned long BufByteSize)
@@ -207,9 +206,9 @@ static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr,
 }
 
 static CURLcode
-set_ssl_version_min_max(DWORD *enabled_protocols,
-                        struct Curl_cfilter *cf,
-                        struct Curl_easy *data)
+schannel_set_ssl_version_min_max(DWORD *enabled_protocols,
+                                 struct Curl_cfilter *cf,
+                                 struct Curl_easy *data)
 {
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   long ssl_version = conn_config->version;
@@ -500,7 +499,8 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
   DWORD flags = 0;
   DWORD enabled_protocols = 0;
 
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)(connssl->backend);
 
   DEBUGASSERT(backend);
 
@@ -563,7 +563,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
   case CURL_SSLVERSION_TLSv1_2:
   case CURL_SSLVERSION_TLSv1_3:
   {
-    result = set_ssl_version_min_max(&enabled_protocols, cf, data);
+    result = schannel_set_ssl_version_min_max(&enabled_protocols, cf, data);
     if(result != CURLE_OK)
       return result;
     break;
@@ -1075,7 +1075,8 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   ssize_t written = -1;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   SecBuffer outbuf;
@@ -1349,7 +1350,8 @@ static CURLcode
 schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   int i;
   ssize_t nread = -1, written = -1;
@@ -1607,7 +1609,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
     data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
     data->set.str[STRING_SSL_PINNEDPUBLICKEY];
   if(pubkey_ptr) {
-    result = pkp_pin_peer_pubkey(cf, data, pubkey_ptr);
+    result = schannel_pkp_pin_peer_pubkey(cf, data, pubkey_ptr);
     if(result) {
       failf(data, "SSL: public key does not match pinned public key");
       return result;
@@ -1686,7 +1688,8 @@ static CURLcode
 schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   CURLcode result = CURLE_OK;
   SECURITY_STATUS sspi_status = SEC_E_OK;
@@ -1931,7 +1934,8 @@ schannel_connect_common(struct Curl_cfilter *cf,
      * Available on Windows 7 or later.
      */
     {
-      struct ssl_backend_data *backend = connssl->backend;
+      struct schannel_ssl_backend_data *backend =
+        (struct schannel_ssl_backend_data *)connssl->backend;
       DEBUGASSERT(backend);
       cf->conn->sslContext = &backend->ctxt->ctxt_handle;
     }
@@ -1960,7 +1964,8 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data,
   SecBufferDesc outbuf_desc;
   SECURITY_STATUS sspi_status = SEC_E_OK;
   CURLcode result;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
 
   DEBUGASSERT(backend);
 
@@ -2110,7 +2115,8 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
   /* we want the length of the encrypted buffer to be at least large enough
      that it can hold all the bytes requested and some TLS record overhead. */
   size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
 
   DEBUGASSERT(backend);
 
@@ -2443,12 +2449,13 @@ static bool schannel_data_pending(struct Curl_cfilter *cf,
                                   const struct Curl_easy *data)
 {
   const struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
 
   (void)data;
   DEBUGASSERT(backend);
 
-  if(connssl->backend->ctxt) /* SSL/TLS is in use */
+  if(backend->ctxt) /* SSL/TLS is in use */
     return (backend->decdata_offset > 0 ||
             (backend->encdata_offset > 0 && !backend->encdata_is_incomplete));
   else
@@ -2486,12 +2493,13 @@ static int schannel_shutdown(struct Curl_cfilter *cf,
    * Shutting Down an Schannel Connection
    */
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
 
   DEBUGASSERT(data);
   DEBUGASSERT(backend);
 
-  if(connssl->backend->ctxt) {
+  if(backend->ctxt) {
     infof(data, "schannel: shutting down SSL/TLS connection with %s port %d",
           connssl->hostname, connssl->port);
   }
@@ -2611,12 +2619,13 @@ static CURLcode schannel_random(struct Curl_easy *data UNUSED_PARAM,
   return Curl_win32_random(entropy, length);
 }
 
-static CURLcode pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
-                                    struct Curl_easy *data,
-                                    const char *pinnedpubkey)
+static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
+                                             struct Curl_easy *data,
+                                             const char *pinnedpubkey)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
   CERT_CONTEXT *pCertContextServer = NULL;
 
   /* Result is returned to caller */
@@ -2742,7 +2751,8 @@ static CURLcode schannel_sha256sum(const unsigned char *input,
 static void *schannel_get_internals(struct ssl_connect_data *connssl,
                                     CURLINFO info UNUSED_PARAM)
 {
-  struct ssl_backend_data *backend = connssl->backend;
+  struct schannel_ssl_backend_data *backend =
+    (struct schannel_ssl_backend_data *)connssl->backend;
   (void)info;
   DEBUGASSERT(backend);
   return &backend->ctxt->ctxt_handle;
@@ -2759,7 +2769,7 @@ const struct Curl_ssl Curl_ssl_schannel = {
   SSLSUPP_TLS13_CIPHERSUITES |
   SSLSUPP_HTTPS_PROXY,
 
-  sizeof(struct ssl_backend_data),
+  sizeof(struct schannel_ssl_backend_data),
 
   schannel_init,                     /* init */
   schannel_cleanup,                  /* cleanup */

+ 0 - 116
Utilities/cmcurl/lib/vtls/schannel.h

@@ -28,8 +28,6 @@
 
 #ifdef USE_SCHANNEL
 
-#define SCHANNEL_USE_BLACKLISTS 1
-
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable: 4201)
@@ -81,119 +79,5 @@ extern const struct Curl_ssl Curl_ssl_schannel;
 CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
                                  struct Curl_easy *data);
 
-/* structs to expose only in schannel.c and schannel_verify.c */
-#ifdef EXPOSE_SCHANNEL_INTERNAL_STRUCTS
-
-#ifdef __MINGW32__
-#ifdef __MINGW64_VERSION_MAJOR
-#define HAS_MANUAL_VERIFY_API
-#endif
-#else
-#ifdef CERT_CHAIN_REVOCATION_CHECK_CHAIN
-#define HAS_MANUAL_VERIFY_API
-#endif
-#endif
-
-#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX)   \
-  && !defined(DISABLE_SCHANNEL_CLIENT_CERT)
-#define HAS_CLIENT_CERT_PATH
-#endif
-
-#ifndef SCH_CREDENTIALS_VERSION
-
-#define SCH_CREDENTIALS_VERSION  0x00000005
-
-typedef enum _eTlsAlgorithmUsage
-{
-    TlsParametersCngAlgUsageKeyExchange,
-    TlsParametersCngAlgUsageSignature,
-    TlsParametersCngAlgUsageCipher,
-    TlsParametersCngAlgUsageDigest,
-    TlsParametersCngAlgUsageCertSig
-} eTlsAlgorithmUsage;
-
-typedef struct _CRYPTO_SETTINGS
-{
-    eTlsAlgorithmUsage  eAlgorithmUsage;
-    UNICODE_STRING      strCngAlgId;
-    DWORD               cChainingModes;
-    PUNICODE_STRING     rgstrChainingModes;
-    DWORD               dwMinBitLength;
-    DWORD               dwMaxBitLength;
-} CRYPTO_SETTINGS, * PCRYPTO_SETTINGS;
-
-typedef struct _TLS_PARAMETERS
-{
-    DWORD               cAlpnIds;
-    PUNICODE_STRING     rgstrAlpnIds;
-    DWORD               grbitDisabledProtocols;
-    DWORD               cDisabledCrypto;
-    PCRYPTO_SETTINGS    pDisabledCrypto;
-    DWORD               dwFlags;
-} TLS_PARAMETERS, * PTLS_PARAMETERS;
-
-typedef struct _SCH_CREDENTIALS
-{
-    DWORD               dwVersion;
-    DWORD               dwCredFormat;
-    DWORD               cCreds;
-    PCCERT_CONTEXT* paCred;
-    HCERTSTORE          hRootStore;
-
-    DWORD               cMappers;
-    struct _HMAPPER **aphMappers;
-
-    DWORD               dwSessionLifespan;
-    DWORD               dwFlags;
-    DWORD               cTlsParameters;
-    PTLS_PARAMETERS     pTlsParameters;
-} SCH_CREDENTIALS, * PSCH_CREDENTIALS;
-
-#define SCH_CRED_MAX_SUPPORTED_PARAMETERS 16
-#define SCH_CRED_MAX_SUPPORTED_ALPN_IDS 16
-#define SCH_CRED_MAX_SUPPORTED_CRYPTO_SETTINGS 16
-#define SCH_CRED_MAX_SUPPORTED_CHAINING_MODES 16
-
-#endif
-
-struct Curl_schannel_cred {
-  CredHandle cred_handle;
-  TimeStamp time_stamp;
-  TCHAR *sni_hostname;
-#ifdef HAS_CLIENT_CERT_PATH
-  HCERTSTORE client_cert_store;
-#endif
-  int refcount;
-};
-
-struct Curl_schannel_ctxt {
-  CtxtHandle ctxt_handle;
-  TimeStamp time_stamp;
-};
-
-struct ssl_backend_data {
-  struct Curl_schannel_cred *cred;
-  struct Curl_schannel_ctxt *ctxt;
-  SecPkgContext_StreamSizes stream_sizes;
-  size_t encdata_length, decdata_length;
-  size_t encdata_offset, decdata_offset;
-  unsigned char *encdata_buffer, *decdata_buffer;
-  /* encdata_is_incomplete: if encdata contains only a partial record that
-     can't be decrypted without another recv() (that is, status is
-     SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds
-     more bytes into encdata then set this back to false. */
-  bool encdata_is_incomplete;
-  unsigned long req_flags, ret_flags;
-  CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
-  bool recv_sspi_close_notify; /* true if connection closed by close_notify */
-  bool recv_connection_closed; /* true if connection closed, regardless how */
-  bool recv_renegotiating;     /* true if recv is doing renegotiation */
-  bool use_alpn; /* true if ALPN is used for this connection */
-#ifdef HAS_MANUAL_VERIFY_API
-  bool use_manual_cred_validation; /* true if manual cred validation is used */
-#endif
-};
-#endif /* EXPOSE_SCHANNEL_INTERNAL_STRUCTS */
-
 #endif /* USE_SCHANNEL */
 #endif /* HEADER_CURL_SCHANNEL_H */

+ 142 - 0
Utilities/cmcurl/lib/vtls/schannel_int.h

@@ -0,0 +1,142 @@
+#ifndef HEADER_CURL_SCHANNEL_INT_H
+#define HEADER_CURL_SCHANNEL_INT_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Marc Hoersken, <[email protected]>, et al.
+ * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#ifdef USE_SCHANNEL
+
+#ifdef __MINGW32__
+#ifdef __MINGW64_VERSION_MAJOR
+#define HAS_MANUAL_VERIFY_API
+#endif
+#else
+#ifdef CERT_CHAIN_REVOCATION_CHECK_CHAIN
+#define HAS_MANUAL_VERIFY_API
+#endif
+#endif
+
+#if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX)   \
+  && !defined(DISABLE_SCHANNEL_CLIENT_CERT)
+#define HAS_CLIENT_CERT_PATH
+#endif
+
+#ifndef SCH_CREDENTIALS_VERSION
+
+#define SCH_CREDENTIALS_VERSION  0x00000005
+
+typedef enum _eTlsAlgorithmUsage
+{
+    TlsParametersCngAlgUsageKeyExchange,
+    TlsParametersCngAlgUsageSignature,
+    TlsParametersCngAlgUsageCipher,
+    TlsParametersCngAlgUsageDigest,
+    TlsParametersCngAlgUsageCertSig
+} eTlsAlgorithmUsage;
+
+typedef struct _CRYPTO_SETTINGS
+{
+    eTlsAlgorithmUsage  eAlgorithmUsage;
+    UNICODE_STRING      strCngAlgId;
+    DWORD               cChainingModes;
+    PUNICODE_STRING     rgstrChainingModes;
+    DWORD               dwMinBitLength;
+    DWORD               dwMaxBitLength;
+} CRYPTO_SETTINGS, * PCRYPTO_SETTINGS;
+
+typedef struct _TLS_PARAMETERS
+{
+    DWORD               cAlpnIds;
+    PUNICODE_STRING     rgstrAlpnIds;
+    DWORD               grbitDisabledProtocols;
+    DWORD               cDisabledCrypto;
+    PCRYPTO_SETTINGS    pDisabledCrypto;
+    DWORD               dwFlags;
+} TLS_PARAMETERS, * PTLS_PARAMETERS;
+
+typedef struct _SCH_CREDENTIALS
+{
+    DWORD               dwVersion;
+    DWORD               dwCredFormat;
+    DWORD               cCreds;
+    PCCERT_CONTEXT* paCred;
+    HCERTSTORE          hRootStore;
+
+    DWORD               cMappers;
+    struct _HMAPPER **aphMappers;
+
+    DWORD               dwSessionLifespan;
+    DWORD               dwFlags;
+    DWORD               cTlsParameters;
+    PTLS_PARAMETERS     pTlsParameters;
+} SCH_CREDENTIALS, * PSCH_CREDENTIALS;
+
+#define SCH_CRED_MAX_SUPPORTED_PARAMETERS 16
+#define SCH_CRED_MAX_SUPPORTED_ALPN_IDS 16
+#define SCH_CRED_MAX_SUPPORTED_CRYPTO_SETTINGS 16
+#define SCH_CRED_MAX_SUPPORTED_CHAINING_MODES 16
+
+#endif /* SCH_CREDENTIALS_VERSION */
+
+struct Curl_schannel_cred {
+  CredHandle cred_handle;
+  TimeStamp time_stamp;
+  TCHAR *sni_hostname;
+#ifdef HAS_CLIENT_CERT_PATH
+  HCERTSTORE client_cert_store;
+#endif
+  int refcount;
+};
+
+struct Curl_schannel_ctxt {
+  CtxtHandle ctxt_handle;
+  TimeStamp time_stamp;
+};
+
+struct schannel_ssl_backend_data {
+  struct Curl_schannel_cred *cred;
+  struct Curl_schannel_ctxt *ctxt;
+  SecPkgContext_StreamSizes stream_sizes;
+  size_t encdata_length, decdata_length;
+  size_t encdata_offset, decdata_offset;
+  unsigned char *encdata_buffer, *decdata_buffer;
+  /* encdata_is_incomplete: if encdata contains only a partial record that
+     can't be decrypted without another recv() (that is, status is
+     SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds
+     more bytes into encdata then set this back to false. */
+  bool encdata_is_incomplete;
+  unsigned long req_flags, ret_flags;
+  CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
+  bool recv_sspi_close_notify; /* true if connection closed by close_notify */
+  bool recv_connection_closed; /* true if connection closed, regardless how */
+  bool recv_renegotiating;     /* true if recv is doing renegotiation */
+  bool use_alpn; /* true if ALPN is used for this connection */
+#ifdef HAS_MANUAL_VERIFY_API
+  bool use_manual_cred_validation; /* true if manual cred validation is used */
+#endif
+};
+
+#endif /* USE_SCHANNEL */
+#endif /* HEADER_CURL_SCHANNEL_INT_H */

+ 2 - 2
Utilities/cmcurl/lib/vtls/schannel_verify.c

@@ -36,8 +36,8 @@
 #  error "Can't compile SCHANNEL support without SSPI."
 #endif
 
-#define EXPOSE_SCHANNEL_INTERNAL_STRUCTS
 #include "schannel.h"
+#include "schannel_int.h"
 
 #ifdef HAS_MANUAL_VERIFY_API
 
@@ -54,7 +54,7 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-#define BACKEND connssl->backend
+#define BACKEND ((struct schannel_ssl_backend_data *)connssl->backend)
 
 #define MAX_CAFILE_SIZE 1048576 /* 1 MiB */
 #define BEGIN_CERT "-----BEGIN CERTIFICATE-----"

+ 29 - 14
Utilities/cmcurl/lib/vtls/sectransp.c

@@ -146,7 +146,7 @@
 #define ioErr -36
 #define paramErr -50
 
-struct ssl_backend_data {
+struct st_ssl_backend_data {
   SSLContextRef ssl_ctx;
   bool ssl_direction; /* true if writing, false if reading */
   size_t ssl_write_buffered_length;
@@ -836,7 +836,8 @@ static OSStatus bio_cf_in_read(SSLConnectionRef connection,
 {
   struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nread;
   CURLcode result;
@@ -859,6 +860,9 @@ static OSStatus bio_cf_in_read(SSLConnectionRef connection,
     }
     nread = 0;
   }
+  else if(nread == 0) {
+    rtn = errSSLClosedGraceful;
+  }
   else if((size_t)nread < *dataLength) {
     rtn = errSSLWouldBlock;
   }
@@ -872,7 +876,8 @@ static OSStatus bio_cf_out_write(SSLConnectionRef connection,
 {
   struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nwritten;
   CURLcode result;
@@ -1338,7 +1343,8 @@ static CURLcode set_ssl_version_min_max(struct Curl_cfilter *cf,
                                         struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   long ssl_version = conn_config->version;
   long ssl_version_max = conn_config->version_max;
@@ -1633,7 +1639,8 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
                                         struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   const struct curl_blob *ssl_cablob = conn_config->ca_info_blob;
@@ -2515,7 +2522,8 @@ static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf,
                                         struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   OSStatus err;
   SSLCipherSuite cipher;
@@ -2896,7 +2904,8 @@ static CURLcode collect_server_cert(struct Curl_cfilter *cf,
   CURLcode result = ssl_config->certinfo ?
     CURLE_PEER_FAILED_VERIFICATION : CURLE_OK;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   CFArrayRef server_certs = NULL;
   SecCertificateRef server_cert;
   OSStatus err;
@@ -3139,7 +3148,8 @@ static CURLcode sectransp_connect(struct Curl_cfilter *cf,
 static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
 
   (void) data;
 
@@ -3166,7 +3176,8 @@ static int sectransp_shutdown(struct Curl_cfilter *cf,
                               struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   ssize_t nread;
   int what;
   int rc;
@@ -3244,7 +3255,8 @@ static bool sectransp_data_pending(struct Curl_cfilter *cf,
                                    const struct Curl_easy *data)
 {
   const struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   OSStatus err;
   size_t buffer;
 
@@ -3308,7 +3320,8 @@ static ssize_t sectransp_send(struct Curl_cfilter *cf,
                               CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   size_t processed = 0UL;
   OSStatus err;
 
@@ -3376,7 +3389,8 @@ static ssize_t sectransp_recv(struct Curl_cfilter *cf,
                               CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   size_t processed = 0UL;
   OSStatus err;
@@ -3434,7 +3448,8 @@ again:
 static void *sectransp_get_internals(struct ssl_connect_data *connssl,
                                      CURLINFO info UNUSED_PARAM)
 {
-  struct ssl_backend_data *backend = connssl->backend;
+  struct st_ssl_backend_data *backend =
+    (struct st_ssl_backend_data *)connssl->backend;
   (void)info;
   DEBUGASSERT(backend);
   return backend->ssl_ctx;
@@ -3450,7 +3465,7 @@ const struct Curl_ssl Curl_ssl_sectransp = {
 #endif /* SECTRANSP_PINNEDPUBKEY */
   SSLSUPP_HTTPS_PROXY,
 
-  sizeof(struct ssl_backend_data),
+  sizeof(struct st_ssl_backend_data),
 
   Curl_none_init,                     /* init */
   Curl_none_cleanup,                  /* cleanup */

+ 13 - 14
Utilities/cmcurl/lib/vtls/vtls.c

@@ -453,7 +453,7 @@ bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
     }
   }
 
-  DEBUGF(infof(data, DMSG(data, "%s Session ID in cache for %s %s://%s:%d"),
+  DEBUGF(infof(data, "%s Session ID in cache for %s %s://%s:%d",
                no_match? "Didn't find": "Found",
                Curl_ssl_cf_is_proxy(cf) ? "proxy" : "host",
                cf->conn->handler->scheme, connssl->hostname, connssl->port));
@@ -601,8 +601,8 @@ CURLcode Curl_ssl_addsessionid(struct Curl_cfilter *cf,
   if(added)
     *added = TRUE;
 
-  DEBUGF(infof(data, DMSG(data, "Added Session ID to cache for %s://%s:%d"
-               " [%s]"), store->scheme, store->name, store->remote_port,
+  DEBUGF(infof(data, "Added Session ID to cache for %s://%s:%d [%s]",
+               store->scheme, store->name, store->remote_port,
                Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server"));
   return CURLE_OK;
 }
@@ -893,8 +893,8 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
   /* only do this if pinnedpubkey starts with "sha256//", length 8 */
   if(strncmp(pinnedpubkey, "sha256//", 8) == 0) {
     CURLcode encode;
-    size_t encodedlen, pinkeylen;
-    char *encoded, *pinkeycopy, *begin_pos, *end_pos;
+    size_t encodedlen = 0, pinkeylen;
+    char *encoded = NULL, *pinkeycopy, *begin_pos, *end_pos;
     unsigned char *sha256sumdigest;
 
     if(!Curl_ssl->sha256sum) {
@@ -907,14 +907,12 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
     if(!sha256sumdigest)
       return CURLE_OUT_OF_MEMORY;
     encode = Curl_ssl->sha256sum(pubkey, pubkeylen,
-                        sha256sumdigest, CURL_SHA256_DIGEST_LENGTH);
+                                 sha256sumdigest, CURL_SHA256_DIGEST_LENGTH);
 
-    if(encode != CURLE_OK)
-      return encode;
-
-    encode = Curl_base64_encode((char *)sha256sumdigest,
-                                CURL_SHA256_DIGEST_LENGTH, &encoded,
-                                &encodedlen);
+    if(!encode)
+      encode = Curl_base64_encode((char *)sha256sumdigest,
+                                  CURL_SHA256_DIGEST_LENGTH, &encoded,
+                                  &encodedlen);
     Curl_safefree(sha256sumdigest);
 
     if(encode)
@@ -1506,7 +1504,7 @@ static void ssl_cf_close(struct Curl_cfilter *cf,
 
   CF_DATA_SAVE(save, cf, data);
   cf_close(cf, data);
-  cf->next->cft->close(cf->next, data);
+  cf->next->cft->do_close(cf->next, data);
   CF_DATA_RESTORE(cf, save);
 }
 
@@ -1530,7 +1528,7 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
   DEBUGASSERT(connssl);
   DEBUGASSERT(cf->conn->host.name);
 
-  result = cf->next->cft->connect(cf->next, data, blocking, done);
+  result = cf->next->cft->do_connect(cf->next, data, blocking, done);
   if(result || !*done)
     goto out;
 
@@ -1594,6 +1592,7 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf,
   ssize_t nread;
 
   CF_DATA_SAVE(save, cf, data);
+  *err = CURLE_OK;
   nread = Curl_ssl->recv_plain(cf, data, buf, len, err);
   if(nread > 0) {
     DEBUGASSERT((size_t)nread <= len);

+ 2 - 1
Utilities/cmcurl/lib/vtls/vtls_int.h

@@ -73,7 +73,7 @@ struct ssl_connect_data {
   char *hostname;                   /* hostname for verification */
   char *dispname;                   /* display version of hostname */
   const struct alpn_spec *alpn;     /* ALPN to use or NULL for none */
-  struct ssl_backend_data *backend; /* vtls backend specific props */
+  void *backend;                    /* vtls backend specific props */
   struct cf_call_data call_data;    /* data handle used in current call */
   struct curltime handshake_done;   /* time when handshake finished */
   int port;                         /* remote port at origin */
@@ -81,6 +81,7 @@ struct ssl_connect_data {
 };
 
 
+#undef CF_CTX_CALL_DATA
 #define CF_CTX_CALL_DATA(cf)  \
   ((struct ssl_connect_data *)(cf)->ctx)->call_data
 

+ 56 - 17
Utilities/cmcurl/lib/vtls/wolfssl.c

@@ -91,7 +91,7 @@
 #undef USE_BIO_CHAIN
 #endif
 
-struct ssl_backend_data {
+struct wolfssl_ssl_backend_data {
   SSL_CTX* ctx;
   SSL*     handle;
   CURLcode io_result;       /* result of last BIO cfilter operation */
@@ -281,13 +281,15 @@ static int bio_cf_out_write(WOLFSSL_BIO *bio, const char *buf, int blen)
 {
   struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
   struct ssl_connect_data *connssl = cf->ctx;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nwritten;
   CURLcode result = CURLE_OK;
 
   DEBUGASSERT(data);
   nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
-  connssl->backend->io_result = result;
+  backend->io_result = result;
   DEBUGF(LOG_CF(data, cf, "bio_write(len=%d) -> %zd, %d",
                 blen, nwritten, result));
   wolfSSL_BIO_clear_retry_flags(bio);
@@ -300,6 +302,8 @@ static int bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
 {
   struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
   struct ssl_connect_data *connssl = cf->ctx;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
   struct Curl_easy *data = CF_DATA_CURRENT(cf);
   ssize_t nread;
   CURLcode result = CURLE_OK;
@@ -310,7 +314,7 @@ static int bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
     return 0;
 
   nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
-  connssl->backend->io_result = result;
+  backend->io_result = result;
   DEBUGF(LOG_CF(data, cf, "bio_read(len=%d) -> %zd, %d",
                 blen, nread, result));
   wolfSSL_BIO_clear_retry_flags(bio);
@@ -352,8 +356,10 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   char *ciphers, *curves;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+  const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
   const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
   SSL_METHOD* req_method = NULL;
 #ifdef HAVE_LIBOQS
@@ -366,6 +372,7 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
 #else
 #define use_sni(x)  Curl_nop_stmt
 #endif
+  bool imported_ca_info_blob = false;
 
   DEBUGASSERT(backend);
 
@@ -410,8 +417,13 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
 #endif
     break;
   case CURL_SSLVERSION_TLSv1_2:
+#ifndef WOLFSSL_NO_TLS12
     req_method = TLSv1_2_client_method();
     use_sni(TRUE);
+#else
+    failf(data, "wolfSSL does not support TLS 1.2");
+    return CURLE_NOT_BUILT_IN;
+#endif
     break;
   case CURL_SSLVERSION_TLSv1_3:
 #ifdef WOLFSSL_TLS13
@@ -494,13 +506,28 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
       }
     }
   }
+
+  if(ca_info_blob) {
+    if(wolfSSL_CTX_load_verify_buffer(
+      backend->ctx, ca_info_blob->data, ca_info_blob->len,
+      SSL_FILETYPE_PEM
+    ) != SSL_SUCCESS) {
+      failf(data, "error importing CA certificate blob");
+      return CURLE_SSL_CACERT_BADFILE;
+    }
+    else {
+      imported_ca_info_blob = true;
+      infof(data, "successfully imported CA certificate blob");
+    }
+  }
+
 #ifndef NO_FILESYSTEM
   /* load trusted cacert */
   if(conn_config->CAfile) {
     if(1 != SSL_CTX_load_verify_locations(backend->ctx,
                                       conn_config->CAfile,
                                       conn_config->CApath)) {
-      if(conn_config->verifypeer) {
+      if(conn_config->verifypeer && !imported_ca_info_blob) {
         /* Fail if we insist on successfully verifying the server. */
         failf(data, "error setting certificate verify locations:"
               " CAfile: %s CApath: %s",
@@ -699,7 +726,8 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   int ret = -1;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
   struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
   const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf)?
     data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
@@ -892,7 +920,8 @@ wolfssl_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   CURLcode result = CURLE_OK;
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
   const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
 
   DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
@@ -950,7 +979,8 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf,
                             CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
   char error_buffer[WOLFSSL_MAX_ERROR_SZ];
   int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
   int rc;
@@ -992,7 +1022,8 @@ static ssize_t wolfssl_send(struct Curl_cfilter *cf,
 static void wolfssl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
 
   (void) data;
 
@@ -1019,7 +1050,8 @@ static ssize_t wolfssl_recv(struct Curl_cfilter *cf,
                             CURLcode *curlcode)
 {
   struct ssl_connect_data *connssl = cf->ctx;
-  struct ssl_backend_data *backend = connssl->backend;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
   char error_buffer[WOLFSSL_MAX_ERROR_SZ];
   int buffsize = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen;
   int nread;
@@ -1108,11 +1140,14 @@ static bool wolfssl_data_pending(struct Curl_cfilter *cf,
                                  const struct Curl_easy *data)
 {
   struct ssl_connect_data *ctx = cf->ctx;
+  struct wolfssl_ssl_backend_data *backend;
 
   (void)data;
   DEBUGASSERT(ctx && ctx->backend);
-  if(ctx->backend->handle)   /* SSL is in use */
-    return (0 != SSL_pending(ctx->backend->handle)) ? TRUE : FALSE;
+
+  backend = (struct wolfssl_ssl_backend_data *)ctx->backend;
+  if(backend->handle)   /* SSL is in use */
+    return (0 != SSL_pending(backend->handle)) ? TRUE : FALSE;
   else
     return FALSE;
 }
@@ -1126,15 +1161,17 @@ static int wolfssl_shutdown(struct Curl_cfilter *cf,
                             struct Curl_easy *data)
 {
   struct ssl_connect_data *ctx = cf->ctx;
+  struct wolfssl_ssl_backend_data *backend;
   int retval = 0;
 
   (void)data;
   DEBUGASSERT(ctx && ctx->backend);
 
-  if(ctx->backend->handle) {
+  backend = (struct wolfssl_ssl_backend_data *)ctx->backend;
+  if(backend->handle) {
     ERR_clear_error();
-    SSL_free(ctx->backend->handle);
-    ctx->backend->handle = NULL;
+    SSL_free(backend->handle);
+    backend->handle = NULL;
   }
   return retval;
 }
@@ -1305,7 +1342,8 @@ static CURLcode wolfssl_sha256sum(const unsigned char *tmp, /* input */
 static void *wolfssl_get_internals(struct ssl_connect_data *connssl,
                                    CURLINFO info UNUSED_PARAM)
 {
-  struct ssl_backend_data *backend = connssl->backend;
+  struct wolfssl_ssl_backend_data *backend =
+    (struct wolfssl_ssl_backend_data *)connssl->backend;
   (void)info;
   DEBUGASSERT(backend);
   return backend->handle;
@@ -1320,9 +1358,10 @@ const struct Curl_ssl Curl_ssl_wolfssl = {
 #ifdef USE_BIO_CHAIN
   SSLSUPP_HTTPS_PROXY |
 #endif
+  SSLSUPP_CAINFO_BLOB |
   SSLSUPP_SSL_CTX,
 
-  sizeof(struct ssl_backend_data),
+  sizeof(struct wolfssl_ssl_backend_data),
 
   wolfssl_init,                    /* init */
   wolfssl_cleanup,                 /* cleanup */

+ 8 - 2
Utilities/cmcurl/lib/warnless.c

@@ -35,10 +35,13 @@
 
 #endif /* __INTEL_COMPILER && __unix__ */
 
-#define BUILDING_WARNLESS_C 1
-
 #include "warnless.h"
 
+#ifdef WIN32
+#undef read
+#undef write
+#endif
+
 #include <limits.h>
 
 #define CURL_MASK_UCHAR   ((unsigned char)~0)
@@ -376,6 +379,9 @@ ssize_t curlx_write(int fd, const void *buf, size_t count)
   return (ssize_t)write(fd, buf, curlx_uztoui(count));
 }
 
+/* Ensure that warnless.h continues to have an effect in "unity" builds. */
+#undef HEADER_CURL_WARNLESS_H
+
 #endif /* WIN32 */
 
 #if defined(__INTEL_COMPILER) && defined(__unix__)

+ 4 - 6
Utilities/cmcurl/lib/warnless.h

@@ -75,12 +75,10 @@ ssize_t curlx_read(int fd, void *buf, size_t count);
 
 ssize_t curlx_write(int fd, const void *buf, size_t count);
 
-#ifndef BUILDING_WARNLESS_C
-#  undef  read
-#  define read(fd, buf, count)  curlx_read(fd, buf, count)
-#  undef  write
-#  define write(fd, buf, count) curlx_write(fd, buf, count)
-#endif
+#undef  read
+#define read(fd, buf, count)  curlx_read(fd, buf, count)
+#undef  write
+#define write(fd, buf, count) curlx_write(fd, buf, count)
 
 #endif /* WIN32 */
 

+ 30 - 26
Utilities/cmcurl/lib/ws.c

@@ -126,8 +126,9 @@ static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data,
             dec->head_len, dec->head_total);
     }
     else {
-      infof(data, "WS-DEC: %s [%s%s payload=%zd/%zd]", msg,
-            ws_frame_name_of_op(dec->head[0]),
+      infof(data, "WS-DEC: %s [%s%s payload=%" CURL_FORMAT_CURL_OFF_T
+                  "/%" CURL_FORMAT_CURL_OFF_T "]",
+            msg, ws_frame_name_of_op(dec->head[0]),
             (dec->head[0] & WSBIT_FIN)? "" : " NON-FINAL",
             dec->payload_offset, dec->payload_len);
     }
@@ -272,7 +273,8 @@ static CURLcode ws_dec_pass_payload(struct ws_decoder *dec,
     Curl_bufq_skip(inraw, (size_t)nwritten);
     dec->payload_offset += (curl_off_t)nwritten;
     remain = dec->payload_len - dec->payload_offset;
-    /* infof(data, "WS-DEC: passed  %zd bytes payload, %zd remain",
+    /* infof(data, "WS-DEC: passed  %zd bytes payload, %"
+                CURL_FORMAT_CURL_OFF_T " remain",
           nwritten, remain); */
   }
 
@@ -351,8 +353,9 @@ static void update_meta(struct websocket *ws,
 static void ws_enc_info(struct ws_encoder *enc, struct Curl_easy *data,
                         const char *msg)
 {
-  infof(data, "WS-ENC: %s [%s%s%s payload=%zd/%zd]", msg,
-        ws_frame_name_of_op(enc->firstbyte),
+  infof(data, "WS-ENC: %s [%s%s%s payload=%" CURL_FORMAT_CURL_OFF_T
+              "/%" CURL_FORMAT_CURL_OFF_T "]",
+        msg, ws_frame_name_of_op(enc->firstbyte),
         (enc->firstbyte & WSBIT_OPCODE_MASK) == WSBIT_OPCODE_CONT ?
         " CONT" : "",
         (enc->firstbyte & WSBIT_FIN)? "" : " NON-FIN",
@@ -839,7 +842,7 @@ static ssize_t nw_in_recv(void *reader_ctx,
 
 CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
                                   size_t buflen, size_t *nread,
-                                  struct curl_ws_frame **metap)
+                                  const struct curl_ws_frame **metap)
 {
   struct connectdata *conn = data->conn;
   struct websocket *ws;
@@ -921,7 +924,8 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
               ctx.payload_len, ctx.bufidx);
   *metap = &ws->frame;
   *nread = ws->frame.len;
-  /* infof(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %zd, %zd left)",
+  /* infof(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %"
+              CURL_FORMAT_CURL_OFF_T ", %" CURL_FORMAT_CURL_OFF_T " left)",
         buflen, *nread, ws->frame.offset, ws->frame.bytesleft); */
   return CURLE_OK;
 }
@@ -966,10 +970,10 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws,
   return CURLE_OK;
 }
 
-CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer,
+CURL_EXTERN CURLcode curl_ws_send(CURL *data, const void *buffer,
                                   size_t buflen, size_t *sent,
-                                  curl_off_t totalsize,
-                                  unsigned int sendflags)
+                                  curl_off_t fragsize,
+                                  unsigned int flags)
 {
   struct websocket *ws;
   ssize_t nwritten, n;
@@ -987,14 +991,13 @@ CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer,
     return CURLE_SEND_ERROR;
   }
   if(!data->conn->proto.ws) {
-    failf(data, "Not a websocket transfer on connection #%ld",
-          data->conn->connection_id);
+    failf(data, "Not a websocket transfer");
     return CURLE_SEND_ERROR;
   }
   ws = data->conn->proto.ws;
 
   if(data->set.ws_raw_mode) {
-    if(totalsize || sendflags)
+    if(fragsize || flags)
       return CURLE_BAD_FUNCTION_ARGUMENT;
     if(!buflen)
       /* nothing to do */
@@ -1027,23 +1030,24 @@ CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer,
   if(space < 14)
     return CURLE_AGAIN;
 
-  if(sendflags & CURLWS_OFFSET) {
-    if(totalsize) {
-      /* a frame series 'totalsize' bytes big, this is the first */
-      n = ws_enc_write_head(data, &ws->enc, sendflags, totalsize,
+  if(flags & CURLWS_OFFSET) {
+    if(fragsize) {
+      /* a frame series 'fragsize' bytes big, this is the first */
+      n = ws_enc_write_head(data, &ws->enc, flags, fragsize,
                             &ws->sendbuf, &result);
       if(n < 0)
         return result;
     }
     else {
       if((curl_off_t)buflen > ws->enc.payload_remain) {
-        infof(data, "WS: unaligned frame size (sending %zu instead of %zd)",
+        infof(data, "WS: unaligned frame size (sending %zu instead of %"
+                    CURL_FORMAT_CURL_OFF_T ")",
               buflen, ws->enc.payload_remain);
       }
     }
   }
   else if(!ws->enc.payload_remain) {
-    n = ws_enc_write_head(data, &ws->enc, sendflags, (curl_off_t)buflen,
+    n = ws_enc_write_head(data, &ws->enc, flags, (curl_off_t)buflen,
                           &ws->sendbuf, &result);
     if(n < 0)
       return result;
@@ -1082,7 +1086,7 @@ CURLcode Curl_ws_disconnect(struct Curl_easy *data,
   return CURLE_OK;
 }
 
-CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
+CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
 {
   /* we only return something for websocket, called from within the callback
      when not using raw mode */
@@ -1096,7 +1100,7 @@ CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
 
 CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen,
                                   size_t *nread,
-                                  struct curl_ws_frame **metap)
+                                  const struct curl_ws_frame **metap)
 {
   (void)curl;
   (void)buffer;
@@ -1108,19 +1112,19 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen,
 
 CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer,
                                   size_t buflen, size_t *sent,
-                                  curl_off_t framesize,
-                                  unsigned int sendflags)
+                                  curl_off_t fragsize,
+                                  unsigned int flags)
 {
   (void)curl;
   (void)buffer;
   (void)buflen;
   (void)sent;
-  (void)framesize;
-  (void)sendflags;
+  (void)fragsize;
+  (void)flags;
   return CURLE_NOT_BUILT_IN;
 }
 
-CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
+CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)
 {
   (void)data;
   return NULL;