Browse Source

curl 2021-09-14 (8e82f2a0)

Code extracted from:

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

at commit 8e82f2a04a238c54ba91e553e9a8452e6d405965 (curl-7_79_0).
Curl Upstream 4 years ago
parent
commit
386467c9dc
100 changed files with 2453 additions and 1833 deletions
  1. 6 69
      CMake/CurlTests.c
  2. 83 87
      CMake/OtherTests.cmake
  3. 0 14
      CMake/Platforms/WindowsCache.cmake
  4. 85 96
      CMakeLists.txt
  5. 10 9
      include/curl/curl.h
  6. 3 3
      include/curl/curlver.h
  7. 2 1
      include/curl/urlapi.h
  8. 6 6
      lib/altsvc.c
  9. 166 19
      lib/asyn-ares.c
  10. 0 1
      lib/asyn-thread.c
  11. 214 43
      lib/c-hyper.c
  12. 1 0
      lib/c-hyper.h
  13. 7 5
      lib/conncache.c
  14. 46 38
      lib/connect.c
  15. 48 39
      lib/cookie.c
  16. 1 0
      lib/cookie.h
  17. 0 6
      lib/curl_addrinfo.c
  18. 50 89
      lib/curl_config.h.cmake
  19. 0 42
      lib/curl_endian.c
  20. 2 2
      lib/curl_gssapi.c
  21. 28 22
      lib/curl_multibyte.c
  22. 6 10
      lib/curl_ntlm_core.c
  23. 5 4
      lib/curl_ntlm_wb.c
  24. 4 4
      lib/curl_range.c
  25. 7 3
      lib/curl_sasl.c
  26. 57 34
      lib/curl_setup.h
  27. 0 20
      lib/curl_setup_once.h
  28. 2 2
      lib/dict.c
  29. 25 25
      lib/doh.c
  30. 1 1
      lib/doh.h
  31. 14 7
      lib/easy.c
  32. 11 5
      lib/formdata.c
  33. 66 55
      lib/ftp.c
  34. 0 1
      lib/hostasyn.c
  35. 2 10
      lib/hostcheck.c
  36. 156 29
      lib/hostip.c
  37. 3 1
      lib/hostip.h
  38. 1 2
      lib/hostip4.c
  39. 1 30
      lib/hostip6.c
  40. 1 2
      lib/hostsyn.c
  41. 8 1
      lib/hsts.c
  42. 157 122
      lib/http.c
  43. 3 0
      lib/http.h
  44. 96 99
      lib/http2.c
  45. 2 3
      lib/http2.h
  46. 6 6
      lib/http_aws_sigv4.c
  47. 2 1
      lib/http_digest.c
  48. 3 3
      lib/http_negotiate.c
  49. 3 3
      lib/http_ntlm.c
  50. 85 45
      lib/http_proxy.c
  51. 4 3
      lib/http_proxy.h
  52. 27 28
      lib/imap.c
  53. 1 1
      lib/inet_ntop.c
  54. 11 12
      lib/krb5.c
  55. 10 10
      lib/ldap.c
  56. 5 1
      lib/md4.c
  57. 5 4
      lib/md5.c
  58. 3 1
      lib/mprintf.c
  59. 207 51
      lib/mqtt.c
  60. 97 55
      lib/multi.c
  61. 3 0
      lib/multihandle.h
  62. 21 4
      lib/netrc.c
  63. 10 6
      lib/non-ascii.c
  64. 5 4
      lib/openldap.c
  65. 2 2
      lib/pingpong.c
  66. 28 29
      lib/pop3.c
  67. 6 1
      lib/progress.c
  68. 1 1
      lib/quic.h
  69. 2 2
      lib/rand.c
  70. 3 3
      lib/rtsp.c
  71. 1 1
      lib/rtsp.h
  72. 4 0
      lib/select.h
  73. 9 17
      lib/sendf.c
  74. 7 7
      lib/setopt.c
  75. 9 8
      lib/sha256.c
  76. 3 3
      lib/smb.c
  77. 13 9
      lib/smtp.c
  78. 26 10
      lib/socketpair.c
  79. 37 35
      lib/socks.c
  80. 3 3
      lib/socks_gssapi.c
  81. 3 3
      lib/socks_sspi.c
  82. 27 1
      lib/strdup.c
  83. 4 1
      lib/strdup.h
  84. 3 2
      lib/strerror.c
  85. 18 15
      lib/telnet.c
  86. 20 20
      lib/tftp.c
  87. 37 29
      lib/transfer.c
  88. 90 59
      lib/url.c
  89. 13 14
      lib/urlapi.c
  90. 16 10
      lib/urldata.h
  91. 8 4
      lib/vauth/digest_sspi.c
  92. 31 53
      lib/vauth/krb5_gssapi.c
  93. 36 46
      lib/vauth/krb5_sspi.c
  94. 4 4
      lib/vauth/ntlm.c
  95. 5 2
      lib/vauth/ntlm_sspi.c
  96. 2 3
      lib/vauth/spnego_gssapi.c
  97. 2 3
      lib/vauth/spnego_sspi.c
  98. 1 0
      lib/vauth/vauth.h
  99. 6 8
      lib/version.c
  100. 79 126
      lib/vquic/ngtcp2.c

+ 6 - 69
CMake/CurlTests.c

@@ -71,21 +71,15 @@ main ()
 }
 #endif
 
-/* tests for gethostbyaddr_r or gethostbyname_r */
-#if defined(HAVE_GETHOSTBYADDR_R_5_REENTRANT) || \
-    defined(HAVE_GETHOSTBYADDR_R_7_REENTRANT) || \
-    defined(HAVE_GETHOSTBYADDR_R_8_REENTRANT) || \
-    defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT) || \
+/* tests for gethostbyname_r */
+#if defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT) || \
     defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT) || \
     defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT)
 #   define _REENTRANT
     /* no idea whether _REENTRANT is always set, just invent a new flag */
 #   define TEST_GETHOSTBYFOO_REENTRANT
 #endif
-#if defined(HAVE_GETHOSTBYADDR_R_5) || \
-    defined(HAVE_GETHOSTBYADDR_R_7) || \
-    defined(HAVE_GETHOSTBYADDR_R_8) || \
-    defined(HAVE_GETHOSTBYNAME_R_3) || \
+#if defined(HAVE_GETHOSTBYNAME_R_3) || \
     defined(HAVE_GETHOSTBYNAME_R_5) || \
     defined(HAVE_GETHOSTBYNAME_R_6) || \
     defined(TEST_GETHOSTBYFOO_REENTRANT)
@@ -98,18 +92,10 @@ int main(void)
   int type = 0;
   struct hostent h;
   int rc = 0;
-#if defined(HAVE_GETHOSTBYADDR_R_5) || \
-    defined(HAVE_GETHOSTBYADDR_R_5_REENTRANT) || \
-    \
-    defined(HAVE_GETHOSTBYNAME_R_3) || \
+#if defined(HAVE_GETHOSTBYNAME_R_3) || \
     defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT)
   struct hostent_data hdata;
-#elif defined(HAVE_GETHOSTBYADDR_R_7) || \
-      defined(HAVE_GETHOSTBYADDR_R_7_REENTRANT) || \
-      defined(HAVE_GETHOSTBYADDR_R_8) || \
-      defined(HAVE_GETHOSTBYADDR_R_8_REENTRANT) || \
-      \
-      defined(HAVE_GETHOSTBYNAME_R_5) || \
+#elif defined(HAVE_GETHOSTBYNAME_R_5) || \
       defined(HAVE_GETHOSTBYNAME_R_5_REENTRANT) || \
       defined(HAVE_GETHOSTBYNAME_R_6) || \
       defined(HAVE_GETHOSTBYNAME_R_6_REENTRANT)
@@ -118,24 +104,6 @@ int main(void)
   struct hostent *hp;
 #endif
 
-#ifndef gethostbyaddr_r
-  (void)gethostbyaddr_r;
-#endif
-
-#if   defined(HAVE_GETHOSTBYADDR_R_5) || \
-      defined(HAVE_GETHOSTBYADDR_R_5_REENTRANT)
-  rc = gethostbyaddr_r(address, length, type, &h, &hdata);
-  (void)rc;
-#elif defined(HAVE_GETHOSTBYADDR_R_7) || \
-      defined(HAVE_GETHOSTBYADDR_R_7_REENTRANT)
-  hp = gethostbyaddr_r(address, length, type, &h, buffer, 8192, &h_errnop);
-  (void)hp;
-#elif defined(HAVE_GETHOSTBYADDR_R_8) || \
-      defined(HAVE_GETHOSTBYADDR_R_8_REENTRANT)
-  rc = gethostbyaddr_r(address, length, type, &h, buffer, 8192, &hp, &h_errnop);
-  (void)rc;
-#endif
-
 #if   defined(HAVE_GETHOSTBYNAME_R_3) || \
       defined(HAVE_GETHOSTBYNAME_R_3_REENTRANT)
   rc = gethostbyname_r(address, &h, &hdata);
@@ -214,37 +182,6 @@ if (sizeof (bool *) )
 #include <float.h>
 int main() { return 0; }
 #endif
-#ifdef HAVE_INET_NTOA_R_DECL
-#include <arpa/inet.h>
-
-typedef void (*func_type)();
-
-int main()
-{
-#ifndef inet_ntoa_r
-  func_type func;
-  func = (func_type)inet_ntoa_r;
-  (void)func;
-#endif
-  return 0;
-}
-#endif
-#ifdef HAVE_INET_NTOA_R_DECL_REENTRANT
-#define _REENTRANT
-#include <arpa/inet.h>
-
-typedef void (*func_type)();
-
-int main()
-{
-#ifndef inet_ntoa_r
-  func_type func;
-  func = (func_type)&inet_ntoa_r;
-  (void)func;
-#endif
-  return 0;
-}
-#endif
 #ifdef HAVE_GETADDRINFO
 #include <netdb.h>
 #include <sys/types.h>
@@ -361,7 +298,7 @@ main ()
 
 /* IoctlSocket source code */
         long flags = 0;
-        if(0 != ioctlsocket(0, FIONBIO, &flags))
+        if(0 != IoctlSocket(0, FIONBIO, &flags))
           return 1;
   ;
   return 0;

+ 83 - 87
CMake/OtherTests.cmake

@@ -5,7 +5,7 @@
 #                            | (__| |_| |  _ <| |___
 #                             \___|\___/|_| \_\_____|
 #
-# Copyright (C) 1998 - 2020, Daniel Stenberg, <[email protected]>, et al.
+# Copyright (C) 1998 - 2021, 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
@@ -47,6 +47,40 @@ endif()
 
 set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
 
+function(curl_cv_func_recv_run_test recv_retv recv_arg1 recv_arg2 recv_arg3 recv_arg4)
+  unset(curl_cv_func_recv_test CACHE)
+  check_c_source_compiles("
+    ${_source_epilogue}
+    #ifdef WINSOCK_API_LINKAGE
+    WINSOCK_API_LINKAGE
+    #endif
+    extern ${recv_retv} ${signature_call_conv}
+    recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4});
+    int main(void) {
+      ${recv_arg1} s=0;
+      ${recv_arg2} buf=0;
+      ${recv_arg3} len=0;
+      ${recv_arg4} flags=0;
+      ${recv_retv} res = recv(s, buf, len, flags);
+      (void) res;
+      return 0;
+    }"
+    curl_cv_func_recv_test)
+  message(STATUS
+    "Tested: ${recv_retv} recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4})")
+  if(curl_cv_func_recv_test)
+    set(curl_cv_func_recv_args
+      "${recv_arg1},${recv_arg2},${recv_arg3},${recv_arg4},${recv_retv}" PARENT_SCOPE)
+    set(RECV_TYPE_ARG1 "${recv_arg1}" PARENT_SCOPE)
+    set(RECV_TYPE_ARG2 "${recv_arg2}" PARENT_SCOPE)
+    set(RECV_TYPE_ARG3 "${recv_arg3}" PARENT_SCOPE)
+    set(RECV_TYPE_ARG4 "${recv_arg4}" PARENT_SCOPE)
+    set(RECV_TYPE_RETV "${recv_retv}" PARENT_SCOPE)
+    set(HAVE_RECV 1 PARENT_SCOPE)
+    set(curl_cv_func_recv_done 1 PARENT_SCOPE)
+  endif()
+endfunction()
+
 check_c_source_compiles("${_source_epilogue}
 int main(void) {
     recv(0, 0, 0, 0);
@@ -54,43 +88,16 @@ int main(void) {
 }" curl_cv_recv)
 if(curl_cv_recv)
   if(NOT DEFINED curl_cv_func_recv_args OR curl_cv_func_recv_args STREQUAL "unknown")
+    if(APPLE)
+      curl_cv_func_recv_run_test("ssize_t" "int" "void *" "size_t" "int")
+    endif()
     foreach(recv_retv "int" "ssize_t" )
       foreach(recv_arg1 "SOCKET" "int" )
         foreach(recv_arg2 "char *" "void *" )
           foreach(recv_arg3 "int" "size_t" "socklen_t" "unsigned int")
             foreach(recv_arg4 "int" "unsigned int")
               if(NOT curl_cv_func_recv_done)
-                unset(curl_cv_func_recv_test CACHE)
-                check_c_source_compiles("
-                  ${_source_epilogue}
-                  #ifdef WINSOCK_API_LINKAGE
-                  WINSOCK_API_LINKAGE
-                  #endif
-                  extern ${recv_retv} ${signature_call_conv}
-                  recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4});
-                  int main(void) {
-                    ${recv_arg1} s=0;
-                    ${recv_arg2} buf=0;
-                    ${recv_arg3} len=0;
-                    ${recv_arg4} flags=0;
-                    ${recv_retv} res = recv(s, buf, len, flags);
-                    (void) res;
-                    return 0;
-                  }"
-                  curl_cv_func_recv_test)
-                message(STATUS
-                  "Tested: ${recv_retv} recv(${recv_arg1}, ${recv_arg2}, ${recv_arg3}, ${recv_arg4})")
-                if(curl_cv_func_recv_test)
-                  set(curl_cv_func_recv_args
-                    "${recv_arg1},${recv_arg2},${recv_arg3},${recv_arg4},${recv_retv}")
-                  set(RECV_TYPE_ARG1 "${recv_arg1}")
-                  set(RECV_TYPE_ARG2 "${recv_arg2}")
-                  set(RECV_TYPE_ARG3 "${recv_arg3}")
-                  set(RECV_TYPE_ARG4 "${recv_arg4}")
-                  set(RECV_TYPE_RETV "${recv_retv}")
-                  set(HAVE_RECV 1)
-                  set(curl_cv_func_recv_done 1)
-                endif()
+                curl_cv_func_recv_run_test(${recv_retv} ${recv_arg1} ${recv_arg2} ${recv_arg3} ${recv_arg4})
               endif()
             endforeach()
           endforeach()
@@ -114,6 +121,42 @@ endif()
 set(curl_cv_func_recv_args "${curl_cv_func_recv_args}" CACHE INTERNAL "Arguments for recv")
 set(HAVE_RECV 1)
 
+function(curl_cv_func_send_run_test send_retv send_arg1 send_arg2 send_arg3 send_arg4)
+  unset(curl_cv_func_send_test CACHE)
+  check_c_source_compiles("
+    ${_source_epilogue}
+    #ifdef WINSOCK_API_LINKAGE
+    WINSOCK_API_LINKAGE
+    #endif
+    extern ${send_retv} ${signature_call_conv}
+    send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4});
+    int main(void) {
+      ${send_arg1} s=0;
+      ${send_arg2} buf=0;
+      ${send_arg3} len=0;
+      ${send_arg4} flags=0;
+      ${send_retv} res = send(s, buf, len, flags);
+      (void) res;
+      return 0;
+    }"
+    curl_cv_func_send_test)
+  message(STATUS
+    "Tested: ${send_retv} send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4})")
+  if(curl_cv_func_send_test)
+    string(REGEX REPLACE "(const) .*" "\\1" send_qual_arg2 "${send_arg2}")
+    string(REGEX REPLACE "const (.*)" "\\1" send_arg2 "${send_arg2}")
+    set(curl_cv_func_send_args
+      "${send_arg1},${send_arg2},${send_arg3},${send_arg4},${send_retv},${send_qual_arg2}" PARENT_SCOPE)
+    set(SEND_TYPE_ARG1 "${send_arg1}" PARENT_SCOPE)
+    set(SEND_TYPE_ARG2 "${send_arg2}" PARENT_SCOPE)
+    set(SEND_TYPE_ARG3 "${send_arg3}" PARENT_SCOPE)
+    set(SEND_TYPE_ARG4 "${send_arg4}" PARENT_SCOPE)
+    set(SEND_TYPE_RETV "${send_retv}" PARENT_SCOPE)
+    set(HAVE_SEND 1 PARENT_SCOPE)
+    set(curl_cv_func_send_done 1 PARENT_SCOPE)
+  endif()
+endfunction()
+
 check_c_source_compiles("${_source_epilogue}
 int main(void) {
     send(0, 0, 0, 0);
@@ -121,45 +164,16 @@ int main(void) {
 }" curl_cv_send)
 if(curl_cv_send)
   if(NOT DEFINED curl_cv_func_send_args OR "${curl_cv_func_send_args}" STREQUAL "unknown")
+    if(APPLE)
+      curl_cv_func_send_run_test("ssize_t" "int" "const void *" "size_t" "int")
+    endif()
     foreach(send_retv "int" "ssize_t" )
       foreach(send_arg1 "SOCKET" "int" "ssize_t" )
         foreach(send_arg2 "const char *" "const void *" "void *" "char *")
           foreach(send_arg3 "int" "size_t" "socklen_t" "unsigned int")
             foreach(send_arg4 "int" "unsigned int")
               if(NOT curl_cv_func_send_done)
-                unset(curl_cv_func_send_test CACHE)
-                check_c_source_compiles("
-                  ${_source_epilogue}
-                  #ifdef WINSOCK_API_LINKAGE
-                  WINSOCK_API_LINKAGE
-                  #endif
-                  extern ${send_retv} ${signature_call_conv}
-                  send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4});
-                  int main(void) {
-                    ${send_arg1} s=0;
-                    ${send_arg2} buf=0;
-                    ${send_arg3} len=0;
-                    ${send_arg4} flags=0;
-                    ${send_retv} res = send(s, buf, len, flags);
-                    (void) res;
-                    return 0;
-                  }"
-                  curl_cv_func_send_test)
-                message(STATUS
-                  "Tested: ${send_retv} send(${send_arg1}, ${send_arg2}, ${send_arg3}, ${send_arg4})")
-                if(curl_cv_func_send_test)
-                  string(REGEX REPLACE "(const) .*" "\\1" send_qual_arg2 "${send_arg2}")
-                  string(REGEX REPLACE "const (.*)" "\\1" send_arg2 "${send_arg2}")
-                  set(curl_cv_func_send_args
-                    "${send_arg1},${send_arg2},${send_arg3},${send_arg4},${send_retv},${send_qual_arg2}")
-                  set(SEND_TYPE_ARG1 "${send_arg1}")
-                  set(SEND_TYPE_ARG2 "${send_arg2}")
-                  set(SEND_TYPE_ARG3 "${send_arg3}")
-                  set(SEND_TYPE_ARG4 "${send_arg4}")
-                  set(SEND_TYPE_RETV "${send_retv}")
-                  set(HAVE_SEND 1)
-                  set(curl_cv_func_send_done 1)
-                endif()
+                curl_cv_func_send_run_test("${send_retv}" "${send_arg1}" "${send_arg2}" "${send_arg3}" "${send_arg4}")
               endif()
             endforeach()
           endforeach()
@@ -206,28 +220,6 @@ int main(void) {
   return 0;
 }" HAVE_STRUCT_TIMEVAL)
 
-set(HAVE_SIG_ATOMIC_T 1)
-set(CMAKE_REQUIRED_FLAGS)
-if(HAVE_SIGNAL_H)
-  set(CMAKE_REQUIRED_FLAGS "-DHAVE_SIGNAL_H")
-  set(CMAKE_EXTRA_INCLUDE_FILES "signal.h")
-endif()
-check_type_size("sig_atomic_t" SIZEOF_SIG_ATOMIC_T)
-if(HAVE_SIZEOF_SIG_ATOMIC_T)
-  check_c_source_compiles("
-    #ifdef HAVE_SIGNAL_H
-    #  include <signal.h>
-    #endif
-    int main(void) {
-      static volatile sig_atomic_t dummy = 0;
-      (void)dummy;
-      return 0;
-    }" HAVE_SIG_ATOMIC_T_NOT_VOLATILE)
-  if(NOT HAVE_SIG_ATOMIC_T_NOT_VOLATILE)
-    set(HAVE_SIG_ATOMIC_T_VOLATILE 1)
-  endif()
-endif()
-
 if(HAVE_WINDOWS_H)
   set(CMAKE_EXTRA_INCLUDE_FILES winsock2.h)
 else()
@@ -245,6 +237,9 @@ endif()
 unset(CMAKE_TRY_COMPILE_TARGET_TYPE)
 
 if(NOT DEFINED CMAKE_TOOLCHAIN_FILE)
+  if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+  # only try this on non-macOS
+
   # if not cross-compilation...
   include(CheckCSourceRuns)
   set(CMAKE_REQUIRED_FLAGS "")
@@ -287,5 +282,6 @@ if(NOT DEFINED CMAKE_TOOLCHAIN_FILE)
         }
         return 0;
     }" HAVE_POLL_FINE)
+  endif()
 endif()
 

+ 0 - 14
CMake/Platforms/WindowsCache.cmake

@@ -46,7 +46,6 @@ if(NOT UNIX)
     set(HAVE_PROCESS_H 1)
     set(HAVE_PWD_H 0)
     set(HAVE_SETJMP_H 1)
-    set(HAVE_SGTTY_H 0)
     set(HAVE_SIGNAL_H 1)
     set(HAVE_SOCKIO_H 0)
     set(HAVE_STDINT_H 0)
@@ -84,12 +83,8 @@ if(NOT UNIX)
     set(HAVE_STRCASECMP 0)
     set(HAVE_STRICMP 1)
     set(HAVE_STRCMPI 1)
-    set(HAVE_GETHOSTBYADDR 1)
     set(HAVE_GETTIMEOFDAY 0)
     set(HAVE_INET_ADDR 1)
-    set(HAVE_INET_NTOA 1)
-    set(HAVE_INET_NTOA_R 0)
-    set(HAVE_PERROR 1)
     set(HAVE_CLOSESOCKET 1)
     set(HAVE_SETVBUF 0)
     set(HAVE_SIGSETJMP 0)
@@ -103,17 +98,10 @@ if(NOT UNIX)
     set(HAVE_RAND_STATUS 0)
     set(HAVE_GMTIME_R 0)
     set(HAVE_LOCALTIME_R 0)
-    set(HAVE_GETHOSTBYADDR_R 0)
     set(HAVE_GETHOSTBYNAME_R 0)
     set(HAVE_SIGNAL_FUNC 1)
     set(HAVE_SIGNAL_MACRO 0)
 
-    set(HAVE_GETHOSTBYADDR_R_5 0)
-    set(HAVE_GETHOSTBYADDR_R_5_REENTRANT 0)
-    set(HAVE_GETHOSTBYADDR_R_7 0)
-    set(HAVE_GETHOSTBYADDR_R_7_REENTRANT 0)
-    set(HAVE_GETHOSTBYADDR_R_8 0)
-    set(HAVE_GETHOSTBYADDR_R_8_REENTRANT 0)
     set(HAVE_GETHOSTBYNAME_R_3 0)
     set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0)
     set(HAVE_GETHOSTBYNAME_R_5 0)
@@ -124,8 +112,6 @@ if(NOT UNIX)
     set(TIME_WITH_SYS_TIME 0)
     set(HAVE_O_NONBLOCK 0)
     set(HAVE_IN_ADDR_T 0)
-    set(HAVE_INET_NTOA_R_DECL 0)
-    set(HAVE_INET_NTOA_R_DECL_REENTRANT 0)
     if(ENABLE_IPV6)
       set(HAVE_GETADDRINFO 1)
     else()

+ 85 - 96
CMakeLists.txt

@@ -156,43 +156,77 @@ endif()
 
 include(CurlSymbolHiding)
 
-option(HTTP_ONLY "disables all protocols except HTTP (This overrides all CURL_DISABLE_* options)" OFF)
-mark_as_advanced(HTTP_ONLY)
-option(CURL_DISABLE_FTP "disables FTP" OFF)
-mark_as_advanced(CURL_DISABLE_FTP)
-option(CURL_DISABLE_LDAP "disables LDAP" OFF)
-mark_as_advanced(CURL_DISABLE_LDAP)
-option(CURL_DISABLE_TELNET "disables Telnet" OFF)
-mark_as_advanced(CURL_DISABLE_TELNET)
+option(CURL_ENABLE_EXPORT_TARGET "to enable cmake export target" ON)
+mark_as_advanced(CURL_ENABLE_EXPORT_TARGET)
+
+option(CURL_DISABLE_ALTSVC "disables alt-svc support" OFF)
+mark_as_advanced(CURL_DISABLE_ALTSVC)
+option(CURL_DISABLE_COOKIES "disables cookies support" OFF)
+mark_as_advanced(CURL_DISABLE_COOKIES)
+option(CURL_DISABLE_CRYPTO_AUTH "disables cryptographic authentication" OFF)
+mark_as_advanced(CURL_DISABLE_CRYPTO_AUTH)
 option(CURL_DISABLE_DICT "disables DICT" OFF)
 mark_as_advanced(CURL_DISABLE_DICT)
+option(CURL_DISABLE_DOH "disables DNS-over-HTTPS" OFF)
+mark_as_advanced(CURL_DISABLE_DOH)
 option(CURL_DISABLE_FILE "disables FILE" OFF)
 mark_as_advanced(CURL_DISABLE_FILE)
-option(CURL_DISABLE_TFTP "disables TFTP" OFF)
-mark_as_advanced(CURL_DISABLE_TFTP)
+option(CURL_DISABLE_FTP "disables FTP" OFF)
+mark_as_advanced(CURL_DISABLE_FTP)
+option(CURL_DISABLE_GETOPTIONS "disables curl_easy_options API for existing options to curl_easy_setopt" OFF)
+mark_as_advanced(CURL_DISABLE_GETOPTIONS)
+option(CURL_DISABLE_GOPHER "disables Gopher" OFF)
+mark_as_advanced(CURL_DISABLE_GOPHER)
+option(CURL_DISABLE_HSTS "disables HSTS support" OFF)
+mark_as_advanced(CURL_DISABLE_HSTS)
 option(CURL_DISABLE_HTTP "disables HTTP" OFF)
 mark_as_advanced(CURL_DISABLE_HTTP)
-
-option(CURL_DISABLE_LDAPS "to disable LDAPS" OFF)
+option(CURL_DISABLE_HTTP_AUTH "disables all HTTP authentication methods" OFF)
+mark_as_advanced(CURL_DISABLE_HTTP_AUTH)
+option(CURL_DISABLE_IMAP "disables IMAP" OFF)
+mark_as_advanced(CURL_DISABLE_IMAP)
+option(CURL_DISABLE_LDAP "disables LDAP" OFF)
+mark_as_advanced(CURL_DISABLE_LDAP)
+option(CURL_DISABLE_LDAPS "disables LDAPS" OFF)
 mark_as_advanced(CURL_DISABLE_LDAPS)
-
-option(CURL_DISABLE_RTSP "to disable RTSP" OFF)
-mark_as_advanced(CURL_DISABLE_RTSP)
-option(CURL_DISABLE_PROXY "to disable proxy" OFF)
-mark_as_advanced(CURL_DISABLE_PROXY)
-option(CURL_DISABLE_POP3 "to disable POP3" OFF)
+option(CURL_DISABLE_LIBCURL_OPTION "disables --libcurl option from the curl tool" OFF)
+mark_as_advanced(CURL_DISABLE_LIBCURL_OPTION)
+option(CURL_DISABLE_MIME "disables MIME support" OFF)
+mark_as_advanced(CURL_DISABLE_MIME)
+option(CURL_DISABLE_MQTT "disables MQTT" OFF)
+mark_as_advanced(CURL_DISABLE_MQTT)
+option(CURL_DISABLE_NETRC "disables netrc parser" OFF)
+mark_as_advanced(CURL_DISABLE_NETRC)
+option(CURL_DISABLE_NTLM "disables NTLM support" OFF)
+mark_as_advanced(CURL_DISABLE_NTLM)
+option(CURL_DISABLE_PARSEDATE "disables date parsing" OFF)
+mark_as_advanced(CURL_DISABLE_PARSEDATE)
+option(CURL_DISABLE_POP3 "disables POP3" OFF)
 mark_as_advanced(CURL_DISABLE_POP3)
-option(CURL_DISABLE_IMAP "to disable IMAP" OFF)
-mark_as_advanced(CURL_DISABLE_IMAP)
-option(CURL_DISABLE_SMTP "to disable SMTP" OFF)
+option(CURL_DISABLE_PROGRESS_METER "disables built-in progress meter" OFF)
+mark_as_advanced(CURL_DISABLE_PROGRESS_METER)
+option(CURL_DISABLE_PROXY "disables proxy support" OFF)
+mark_as_advanced(CURL_DISABLE_PROXY)
+option(CURL_DISABLE_RTSP "disables RTSP" OFF)
+mark_as_advanced(CURL_DISABLE_RTSP)
+option(CURL_DISABLE_SHUFFLE_DNS "disables shuffle DNS feature" OFF)
+mark_as_advanced(CURL_DISABLE_SHUFFLE_DNS)
+option(CURL_DISABLE_SMB "disables SMB" OFF)
+mark_as_advanced(CURL_DISABLE_SMB)
+option(CURL_DISABLE_SMTP "disables SMTP" OFF)
 mark_as_advanced(CURL_DISABLE_SMTP)
-option(CURL_DISABLE_GOPHER "to disable Gopher" OFF)
-mark_as_advanced(CURL_DISABLE_GOPHER)
-option(CURL_DISABLE_MQTT "to disable MQTT" OFF)
-mark_as_advanced(CURL_DISABLE_MQTT)
+option(CURL_DISABLE_SOCKETPAIR "disables use of socketpair for curl_multi_poll" OFF)
+mark_as_advanced(CURL_DISABLE_SOCKETPAIR)
+option(CURL_DISABLE_TELNET "disables Telnet" OFF)
+mark_as_advanced(CURL_DISABLE_TELNET)
+option(CURL_DISABLE_TFTP "disables TFTP" OFF)
+mark_as_advanced(CURL_DISABLE_TFTP)
+option(CURL_DISABLE_VERBOSE_STRINGS "disables verbose strings" OFF)
+mark_as_advanced(CURL_DISABLE_VERBOSE_STRINGS)
 
-option(CURL_ENABLE_EXPORT_TARGET "to enable cmake export target" ON)
-mark_as_advanced(CURL_ENABLE_EXPORT_TARGET)
+# Corresponds to HTTP_ONLY in lib/curl_setup.h
+option(HTTP_ONLY "disables all protocols except HTTP (This overrides all CURL_DISABLE_* options)" OFF)
+mark_as_advanced(HTTP_ONLY)
 
 if(HTTP_ONLY)
   set(CURL_DISABLE_DICT ON)
@@ -211,16 +245,6 @@ if(HTTP_ONLY)
   set(CURL_DISABLE_TFTP ON)
 endif()
 
-option(CURL_DISABLE_ALTSVC "to disable alt-svc support" OFF)
-mark_as_advanced(CURL_DISABLE_ALTSVC)
-option(CURL_DISABLE_HSTS "to disable HSTS support" OFF)
-mark_as_advanced(CURL_DISABLE_HSTS)
-option(CURL_DISABLE_COOKIES "to disable cookies support" OFF)
-mark_as_advanced(CURL_DISABLE_COOKIES)
-option(CURL_DISABLE_CRYPTO_AUTH "to disable cryptographic authentication" OFF)
-mark_as_advanced(CURL_DISABLE_CRYPTO_AUTH)
-option(CURL_DISABLE_VERBOSE_STRINGS "to disable verbose strings" OFF)
-mark_as_advanced(CURL_DISABLE_VERBOSE_STRINGS)
 option(ENABLE_IPV6 "Define if you want to enable IPv6 support" ON)
 mark_as_advanced(ENABLE_IPV6)
 if(ENABLE_IPV6 AND NOT WIN32)
@@ -370,28 +394,29 @@ if(CMAKE_USE_DARWINSSL)
   message(FATAL_ERROR "The cmake option CMAKE_USE_DARWINSSL was renamed to CMAKE_USE_SECTRANSP.")
 endif()
 
-if(CMAKE_USE_SECTRANSP)
+if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
   find_library(COREFOUNDATION_FRAMEWORK "CoreFoundation")
   if(NOT COREFOUNDATION_FRAMEWORK)
       message(FATAL_ERROR "CoreFoundation framework not found")
   endif()
 
-  find_library(SECURITY_FRAMEWORK "Security")
-  if(NOT SECURITY_FRAMEWORK)
-     message(FATAL_ERROR "Security framework not found")
-  endif()
-
-  set(SSL_ENABLED ON)
-  set(USE_SECTRANSP ON)
-  list(APPEND CURL_LIBS "${COREFOUNDATION_FRAMEWORK}" "${SECURITY_FRAMEWORK}")
-endif()
-
-if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
   find_library(SYSTEMCONFIGURATION_FRAMEWORK "SystemConfiguration")
   if(NOT SYSTEMCONFIGURATION_FRAMEWORK)
      message(FATAL_ERROR "SystemConfiguration framework not found")
   endif()
-  list(APPEND CURL_LIBS "${SYSTEMCONFIGURATION_FRAMEWORK}")
+
+  list(APPEND CURL_LIBS "-framework CoreFoundation" "-framework SystemConfiguration")
+
+  if(CMAKE_USE_SECTRANSP)
+    find_library(SECURITY_FRAMEWORK "Security")
+    if(NOT SECURITY_FRAMEWORK)
+       message(FATAL_ERROR "Security framework not found")
+    endif()
+
+    set(SSL_ENABLED ON)
+    set(USE_SECTRANSP ON)
+    list(APPEND CURL_LIBS "-framework Security")
+  endif()
 endif()
 
 if(CMAKE_USE_OPENSSL)
@@ -713,15 +738,6 @@ if(CMAKE_USE_LIBSSH2)
     set(HAVE_LIBSSH2_H ON)
     set(CURL_INCLUDES ${CURL_INCLUDES} "${LIBSSH2_INCLUDE_DIR}/libssh2.h")
     set(CURL_TEST_DEFINES "${CURL_TEST_DEFINES} -DHAVE_LIBSSH2_H")
-
-    # now check for specific libssh2 symbols as they were added in different versions
-    set(CMAKE_EXTRA_INCLUDE_FILES "libssh2.h")
-    check_function_exists(libssh2_version           HAVE_LIBSSH2_VERSION)
-    check_function_exists(libssh2_init              HAVE_LIBSSH2_INIT)
-    check_function_exists(libssh2_exit              HAVE_LIBSSH2_EXIT)
-    check_function_exists(libssh2_scp_send64        HAVE_LIBSSH2_SCP_SEND64)
-    check_function_exists(libssh2_session_handshake HAVE_LIBSSH2_SESSION_HANDSHAKE)
-    set(CMAKE_EXTRA_INCLUDE_FILES "")
     unset(CMAKE_REQUIRED_LIBRARIES)
   endif()
 endif()
@@ -804,7 +820,11 @@ endif()
 option(ENABLE_UNIX_SOCKETS "Define if you want Unix domain sockets support" ON)
 if(ENABLE_UNIX_SOCKETS)
   include(CheckStructHasMember)
-  check_struct_has_member("struct sockaddr_un" sun_path "sys/un.h" USE_UNIX_SOCKETS)
+  if(WIN32)
+    set(USE_UNIX_SOCKETS ON)
+  else()
+    check_struct_has_member("struct sockaddr_un" sun_path "sys/un.h" USE_UNIX_SOCKETS)
+  endif()
 else()
   unset(USE_UNIX_SOCKETS CACHE)
 endif()
@@ -915,8 +935,6 @@ check_include_file_concat("alloca.h"         HAVE_ALLOCA_H)
 check_include_file_concat("arpa/inet.h"      HAVE_ARPA_INET_H)
 check_include_file_concat("arpa/tftp.h"      HAVE_ARPA_TFTP_H)
 check_include_file_concat("assert.h"         HAVE_ASSERT_H)
-check_include_file_concat("crypto.h"         HAVE_CRYPTO_H)
-check_include_file_concat("err.h"            HAVE_ERR_H)
 check_include_file_concat("errno.h"          HAVE_ERRNO_H)
 check_include_file_concat("fcntl.h"          HAVE_FCNTL_H)
 check_include_file_concat("idn2.h"           HAVE_IDN2_H)
@@ -934,9 +952,7 @@ check_include_file("linux/tcp.h"      HAVE_LINUX_TCP_H)
 check_include_file_concat("pem.h"            HAVE_PEM_H)
 check_include_file_concat("poll.h"           HAVE_POLL_H)
 check_include_file_concat("pwd.h"            HAVE_PWD_H)
-check_include_file_concat("rsa.h"            HAVE_RSA_H)
 check_include_file_concat("setjmp.h"         HAVE_SETJMP_H)
-check_include_file_concat("sgtty.h"          HAVE_SGTTY_H)
 check_include_file_concat("signal.h"         HAVE_SIGNAL_H)
 check_include_file_concat("ssl.h"            HAVE_SSL_H)
 check_include_file_concat("stdbool.h"        HAVE_STDBOOL_H)
@@ -1017,12 +1033,8 @@ endif()
 check_symbol_exists(getppid       "${CURL_INCLUDES}" HAVE_GETPPID)
 check_symbol_exists(utimes        "${CURL_INCLUDES}" HAVE_UTIMES)
 
-check_symbol_exists(gethostbyaddr "${CURL_INCLUDES}" HAVE_GETHOSTBYADDR)
-check_symbol_exists(gethostbyaddr_r "${CURL_INCLUDES}" HAVE_GETHOSTBYADDR_R)
 check_symbol_exists(gettimeofday  "${CURL_INCLUDES}" HAVE_GETTIMEOFDAY)
 check_symbol_exists(inet_addr     "${CURL_INCLUDES}" HAVE_INET_ADDR)
-check_symbol_exists(inet_ntoa     "${CURL_INCLUDES}" HAVE_INET_NTOA)
-check_symbol_exists(inet_ntoa_r   "${CURL_INCLUDES}" HAVE_INET_NTOA_R)
 check_symbol_exists(closesocket   "${CURL_INCLUDES}" HAVE_CLOSESOCKET)
 check_symbol_exists(sigsetjmp     "${CURL_INCLUDES}" HAVE_SIGSETJMP)
 check_symbol_exists(getpass_r     "${CURL_INCLUDES}" HAVE_GETPASS_R)
@@ -1047,11 +1059,8 @@ check_symbol_exists(strtoll        "${CURL_INCLUDES}" HAVE_STRTOLL)
 check_symbol_exists(_strtoi64      "${CURL_INCLUDES}" HAVE__STRTOI64)
 check_symbol_exists(strerror_r     "${CURL_INCLUDES}" HAVE_STRERROR_R)
 check_symbol_exists(siginterrupt   "${CURL_INCLUDES}" HAVE_SIGINTERRUPT)
-check_symbol_exists(perror         "${CURL_INCLUDES}" HAVE_PERROR)
-check_symbol_exists(fork           "${CURL_INCLUDES}" HAVE_FORK)
 check_symbol_exists(getaddrinfo    "${CURL_INCLUDES}" HAVE_GETADDRINFO)
 check_symbol_exists(freeaddrinfo   "${CURL_INCLUDES}" HAVE_FREEADDRINFO)
-check_symbol_exists(freeifaddrs    "${CURL_INCLUDES}" HAVE_FREEIFADDRS)
 check_symbol_exists(pipe           "${CURL_INCLUDES}" HAVE_PIPE)
 check_symbol_exists(ftruncate      "${CURL_INCLUDES}" HAVE_FTRUNCATE)
 check_symbol_exists(getprotobyname "${CURL_INCLUDES}" HAVE_GETPROTOBYNAME)
@@ -1118,12 +1127,6 @@ foreach(CURL_TEST
     HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
     TIME_WITH_SYS_TIME
     HAVE_O_NONBLOCK
-    HAVE_GETHOSTBYADDR_R_5
-    HAVE_GETHOSTBYADDR_R_7
-    HAVE_GETHOSTBYADDR_R_8
-    HAVE_GETHOSTBYADDR_R_5_REENTRANT
-    HAVE_GETHOSTBYADDR_R_7_REENTRANT
-    HAVE_GETHOSTBYADDR_R_8_REENTRANT
     HAVE_GETHOSTBYNAME_R_3
     HAVE_GETHOSTBYNAME_R_5
     HAVE_GETHOSTBYNAME_R_6
@@ -1133,8 +1136,6 @@ foreach(CURL_TEST
     HAVE_IN_ADDR_T
     HAVE_BOOL_T
     STDC_HEADERS
-    HAVE_INET_NTOA_R_DECL
-    HAVE_INET_NTOA_R_DECL_REENTRANT
     HAVE_GETADDRINFO
     HAVE_FILE_OFFSET_BITS
     HAVE_VARIADIC_MACROS_C99
@@ -1166,13 +1167,9 @@ endforeach()
 
 # Check for reentrant
 foreach(CURL_TEST
-    HAVE_GETHOSTBYADDR_R_5
-    HAVE_GETHOSTBYADDR_R_7
-    HAVE_GETHOSTBYADDR_R_8
     HAVE_GETHOSTBYNAME_R_3
     HAVE_GETHOSTBYNAME_R_5
-    HAVE_GETHOSTBYNAME_R_6
-    HAVE_INET_NTOA_R_DECL_REENTRANT)
+    HAVE_GETHOSTBYNAME_R_6)
   if(NOT ${CURL_TEST})
     if(${CURL_TEST}_REENTRANT)
       set(NEED_REENTRANT 1)
@@ -1182,9 +1179,6 @@ endforeach()
 
 if(NEED_REENTRANT)
   foreach(CURL_TEST
-      HAVE_GETHOSTBYADDR_R_5
-      HAVE_GETHOSTBYADDR_R_7
-      HAVE_GETHOSTBYADDR_R_8
       HAVE_GETHOSTBYNAME_R_3
       HAVE_GETHOSTBYNAME_R_5
       HAVE_GETHOSTBYNAME_R_6)
@@ -1195,11 +1189,6 @@ if(NEED_REENTRANT)
   endforeach()
 endif()
 
-if(HAVE_INET_NTOA_R_DECL_REENTRANT)
-  set(HAVE_INET_NTOA_R_DECL 1)
-  set(NEED_REENTRANT 1)
-endif()
-
 # Check clock_gettime(CLOCK_MONOTONIC, x) support
 curl_internal_test(HAVE_CLOCK_GETTIME_MONOTONIC)
 
@@ -1380,8 +1369,8 @@ endmacro()
 
 # NTLM support requires crypto function adaptions from various SSL libs
 # TODO alternative SSL libs tests for SSP1, GNUTLS, NSS
-if(NOT CURL_DISABLE_CRYPTO_AUTH AND (USE_OPENSSL OR USE_MBEDTLS OR
-                                     USE_DARWINSSL OR USE_WIN32_CRYPTO))
+if(NOT (CURL_DISABLE_CRYPTO_AUTH OR CURL_DISABLE_NTLM) AND
+    (USE_OPENSSL OR USE_MBEDTLS OR USE_DARWINSSL OR USE_WIN32_CRYPTO))
   set(use_curl_ntlm_core ON)
 endif()
 
@@ -1409,10 +1398,10 @@ _add_if("Kerberos"      NOT CURL_DISABLE_CRYPTO_AUTH AND
                         (HAVE_GSSAPI OR USE_WINDOWS_SSPI))
 # NTLM support requires crypto function adaptions from various SSL libs
 # TODO alternative SSL libs tests for SSP1, GNUTLS, NSS
-_add_if("NTLM"          NOT CURL_DISABLE_CRYPTO_AUTH AND
+_add_if("NTLM"          NOT (CURL_DISABLE_CRYPTO_AUTH OR CURL_DISABLE_NTLM) AND
                         (use_curl_ntlm_core OR USE_WINDOWS_SSPI))
 # TODO missing option (autoconf: --enable-ntlm-wb)
-_add_if("NTLM_WB"       NOT CURL_DISABLE_CRYPTO_AUTH AND
+_add_if("NTLM_WB"       NOT (CURL_DISABLE_CRYPTO_AUTH OR CURL_DISABLE_NTLM) AND
                         (use_curl_ntlm_core OR USE_WINDOWS_SSPI) AND
                         NOT CURL_DISABLE_HTTP AND NTLM_WB_ENABLED)
 # TODO missing option (--enable-tls-srp), depends on GNUTLS_SRP/OPENSSL_SRP

+ 10 - 9
include/curl/curl.h

@@ -25,9 +25,6 @@
 /*
  * If you have libcurl problems, all docs and details are found here:
  *   https://curl.se/libcurl/
- *
- * curl-library mailing list subscription and unsubscription web interface:
- *   https://cool.haxx.se/mailman/listinfo/curl-library/
  */
 
 #ifdef CURL_NO_OLDIES
@@ -74,8 +71,9 @@
 #if defined(_AIX) || defined(__NOVELL_LIBC__) || defined(__NetBSD__) || \
     defined(__minix) || defined(__SYMBIAN32__) || defined(__INTEGRITY) || \
     defined(ANDROID) || defined(__ANDROID__) || defined(__OpenBSD__) || \
-    defined(__CYGWIN__) || defined(AMIGA) || \
-   (defined(__FreeBSD_version) && (__FreeBSD_version < 800000))
+    defined(__CYGWIN__) || defined(AMIGA) || defined(__NuttX__) || \
+   (defined(__FreeBSD_version) && (__FreeBSD_version < 800000)) || \
+    defined(__VXWORKS__)
 #include <sys/select.h>
 #endif
 
@@ -541,7 +539,7 @@ typedef enum {
   CURLE_OBSOLETE46,              /* 46 - NOT USED */
   CURLE_TOO_MANY_REDIRECTS,      /* 47 - catch endless re-direct loops */
   CURLE_UNKNOWN_OPTION,          /* 48 - User specified an unknown option */
-  CURLE_TELNET_OPTION_SYNTAX,    /* 49 - Malformed telnet option */
+  CURLE_SETOPT_OPTION_SYNTAX,    /* 49 - Malformed setopt option */
   CURLE_OBSOLETE50,              /* 50 - NOT USED */
   CURLE_OBSOLETE51,              /* 51 - NOT USED */
   CURLE_GOT_NOTHING,             /* 52 - when this is a specific error */
@@ -636,6 +634,9 @@ typedef enum {
 /* The following were added in 7.21.5, April 2011 */
 #define CURLE_UNKNOWN_TELNET_OPTION CURLE_UNKNOWN_OPTION
 
+/* Added for 7.78.0 */
+#define CURLE_TELNET_OPTION_SYNTAX CURLE_SETOPT_OPTION_SYNTAX
+
 /* The following were added in 7.17.1 */
 /* These are scheduled to disappear by 2009 */
 #define CURLE_SSL_PEER_CERTIFICATE CURLE_PEER_FAILED_VERIFICATION
@@ -2084,13 +2085,13 @@ typedef enum {
   /* Parameters for V4 signature */
   CURLOPT(CURLOPT_AWS_SIGV4, CURLOPTTYPE_STRINGPOINT, 305),
 
-  /* Same as CURLOPT_SSL_VERIFYPEER but for DOH (DNS-over-HTTPS) servers. */
+  /* Same as CURLOPT_SSL_VERIFYPEER but for DoH (DNS-over-HTTPS) servers. */
   CURLOPT(CURLOPT_DOH_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 306),
 
-  /* Same as CURLOPT_SSL_VERIFYHOST but for DOH (DNS-over-HTTPS) servers. */
+  /* Same as CURLOPT_SSL_VERIFYHOST but for DoH (DNS-over-HTTPS) servers. */
   CURLOPT(CURLOPT_DOH_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 307),
 
-  /* Same as CURLOPT_SSL_VERIFYSTATUS but for DOH (DNS-over-HTTPS) servers. */
+  /* Same as CURLOPT_SSL_VERIFYSTATUS but for DoH (DNS-over-HTTPS) servers. */
   CURLOPT(CURLOPT_DOH_SSL_VERIFYSTATUS, CURLOPTTYPE_LONG, 308),
 
   /* The CA certificates as "blob" used to validate the peer certificate

+ 3 - 3
include/curl/curlver.h

@@ -30,12 +30,12 @@
 
 /* This is the version number of the libcurl package from which this header
    file origins: */
-#define LIBCURL_VERSION "7.77.0-DEV"
+#define LIBCURL_VERSION "7.79.0-DEV"
 
 /* The numeric version number is also available "in parts" by using these
    defines: */
 #define LIBCURL_VERSION_MAJOR 7
-#define LIBCURL_VERSION_MINOR 77
+#define LIBCURL_VERSION_MINOR 79
 #define LIBCURL_VERSION_PATCH 0
 
 /* This is the numeric version of the libcurl version number, meant for easier
@@ -57,7 +57,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 0x074d00
+#define LIBCURL_VERSION_NUM 0x074f00
 
 /*
  * This is the date and time when the full source package was created. The

+ 2 - 1
include/curl/urlapi.h

@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2018 - 2020, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 2018 - 2021, 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
@@ -79,6 +79,7 @@ typedef enum {
 #define CURLU_GUESS_SCHEME (1<<9)       /* legacy curl-style guessing */
 #define CURLU_NO_AUTHORITY (1<<10)      /* Allow empty authority when the
                                            scheme is unknown. */
+#define CURLU_ALLOW_SPACE (1<<11)       /* Allow spaces in the URL */
 
 typedef struct Curl_URL CURLU;
 

+ 6 - 6
lib/altsvc.c

@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2019 - 2020, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 2019 - 2021, 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
@@ -460,7 +460,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
   (void)data;
 #endif
   if(result) {
-    infof(data, "Excessive alt-svc header, ignoring...\n");
+    infof(data, "Excessive alt-svc header, ignoring.");
     return CURLE_OK;
   }
 
@@ -496,7 +496,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
             p++;
           len = p - hostp;
           if(!len || (len >= MAX_ALTSVC_HOSTLEN)) {
-            infof(data, "Excessive alt-svc host name, ignoring...\n");
+            infof(data, "Excessive alt-svc host name, ignoring.");
             dstalpnid = ALPN_none;
           }
           else {
@@ -513,7 +513,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
           /* a port number */
           unsigned long port = strtoul(++p, &end_ptr, 10);
           if(port > USHRT_MAX || end_ptr == p || *end_ptr != '\"') {
-            infof(data, "Unknown alt-svc port number, ignoring...\n");
+            infof(data, "Unknown alt-svc port number, ignoring.");
             dstalpnid = ALPN_none;
           }
           p = end_ptr;
@@ -579,12 +579,12 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
             as->expires = maxage + time(NULL);
             as->persist = persist;
             Curl_llist_insert_next(&asi->list, asi->list.tail, as, &as->node);
-            infof(data, "Added alt-svc: %s:%d over %s\n", dsthost, dstport,
+            infof(data, "Added alt-svc: %s:%d over %s", dsthost, dstport,
                   Curl_alpnid2str(dstalpnid));
           }
         }
         else {
-          infof(data, "Unknown alt-svc protocol \"%s\", skipping...\n",
+          infof(data, "Unknown alt-svc protocol \"%s\", skipping.",
                 alpnbuf);
         }
       }

+ 166 - 19
lib/asyn-ares.c

@@ -59,7 +59,6 @@
 #include "hostip.h"
 #include "hash.h"
 #include "share.h"
-#include "strerror.h"
 #include "url.h"
 #include "multiif.h"
 #include "inet_pton.h"
@@ -80,13 +79,33 @@
 #define HAVE_CARES_CALLBACK_TIMEOUTS 1
 #endif
 
+#if ARES_VERSION >= 0x010601
+/* IPv6 supported since 1.6.1 */
+#define HAVE_CARES_IPV6 1
+#endif
+
+#if ARES_VERSION >= 0x010704
+#define HAVE_CARES_SERVERS_CSV 1
+#define HAVE_CARES_LOCAL_DEV 1
+#define HAVE_CARES_SET_LOCAL 1
+#endif
+
+#if ARES_VERSION >= 0x010b00
+#define HAVE_CARES_PORTS_CSV 1
+#endif
+
+#if ARES_VERSION >= 0x011000
+/* 1.16.0 or later has ares_getaddrinfo */
+#define HAVE_CARES_GETADDRINFO 1
+#endif
+
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
 #include "memdebug.h"
 
 struct thread_data {
-  int num_pending; /* number of ares_gethostbyname() requests */
+  int num_pending; /* number of outstanding c-ares requests */
   struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
                                     parts */
   int last_status;
@@ -206,7 +225,8 @@ static void destroy_async_data(struct Curl_async *async);
  */
 void Curl_resolver_cancel(struct Curl_easy *data)
 {
-  if(data && data->state.async.resolver)
+  DEBUGASSERT(data);
+  if(data->state.async.resolver)
     ares_cancel((ares_channel)data->state.async.resolver);
   destroy_async_data(&data->state.async);
 }
@@ -489,21 +509,37 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
   return result;
 }
 
+#ifndef HAVE_CARES_GETADDRINFO
+
 /* Connects results to the list */
 static void compound_results(struct thread_data *res,
                              struct Curl_addrinfo *ai)
 {
-  struct Curl_addrinfo *ai_tail;
   if(!ai)
     return;
-  ai_tail = ai;
 
-  while(ai_tail->ai_next)
-    ai_tail = ai_tail->ai_next;
+#ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
+  if(res->temp_ai && res->temp_ai->ai_family == PF_INET6) {
+    /* We have results already, put the new IPv6 entries at the head of the
+       list. */
+    struct Curl_addrinfo *temp_ai_tail = res->temp_ai;
+
+    while(temp_ai_tail->ai_next)
+      temp_ai_tail = temp_ai_tail->ai_next;
 
-  /* Add the new results to the list of old results. */
-  ai_tail->ai_next = res->temp_ai;
-  res->temp_ai = ai;
+    temp_ai_tail->ai_next = ai;
+  }
+  else
+#endif /* CURLRES_IPV6 */
+  {
+    /* Add the new results to the list of old results. */
+    struct Curl_addrinfo *ai_tail = ai;
+    while(ai_tail->ai_next)
+      ai_tail = ai_tail->ai_next;
+
+    ai_tail->ai_next = res->temp_ai;
+    res->temp_ai = ai;
+  }
 }
 
 /*
@@ -605,7 +641,98 @@ static void query_completed_cb(void *arg,  /* (struct connectdata *) */
     }
   }
 }
+#else
+/* c-ares 1.16.0 or later */
+
+/*
+ * ares2addr() converts an address list provided by c-ares to an internal
+ * libcurl compatible list
+ */
+static struct Curl_addrinfo *ares2addr(struct ares_addrinfo_node *node)
+{
+  /* traverse the ares_addrinfo_node list */
+  struct ares_addrinfo_node *ai;
+  struct Curl_addrinfo *cafirst = NULL;
+  struct Curl_addrinfo *calast = NULL;
+  int error = 0;
+
+  for(ai = node; ai != NULL; ai = ai->ai_next) {
+    size_t ss_size;
+    struct Curl_addrinfo *ca;
+    /* ignore elements with unsupported address family, */
+    /* settle family-specific sockaddr structure size.  */
+    if(ai->ai_family == AF_INET)
+      ss_size = sizeof(struct sockaddr_in);
+#ifdef ENABLE_IPV6
+    else if(ai->ai_family == AF_INET6)
+      ss_size = sizeof(struct sockaddr_in6);
+#endif
+    else
+      continue;
+
+    /* ignore elements without required address info */
+    if(!ai->ai_addr || !(ai->ai_addrlen > 0))
+      continue;
+
+    /* ignore elements with bogus address size */
+    if((size_t)ai->ai_addrlen < ss_size)
+      continue;
+
+    ca = malloc(sizeof(struct Curl_addrinfo) + ss_size);
+    if(!ca) {
+      error = EAI_MEMORY;
+      break;
+    }
+
+    /* copy each structure member individually, member ordering, */
+    /* size, or padding might be different for each platform.    */
+
+    ca->ai_flags     = ai->ai_flags;
+    ca->ai_family    = ai->ai_family;
+    ca->ai_socktype  = ai->ai_socktype;
+    ca->ai_protocol  = ai->ai_protocol;
+    ca->ai_addrlen   = (curl_socklen_t)ss_size;
+    ca->ai_addr      = NULL;
+    ca->ai_canonname = NULL;
+    ca->ai_next      = NULL;
+
+    ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
+    memcpy(ca->ai_addr, ai->ai_addr, ss_size);
+
+    /* if the return list is empty, this becomes the first element */
+    if(!cafirst)
+      cafirst = ca;
+
+    /* add this element last in the return list */
+    if(calast)
+      calast->ai_next = ca;
+    calast = ca;
+  }
+
+  /* if we failed, destroy the Curl_addrinfo list */
+  if(error) {
+    Curl_freeaddrinfo(cafirst);
+    cafirst = NULL;
+  }
+
+  return cafirst;
+}
+
+static void addrinfo_cb(void *arg, int status, int timeouts,
+                        struct ares_addrinfo *result)
+{
+  struct Curl_easy *data = (struct Curl_easy *)arg;
+  struct thread_data *res = data->state.async.tdata;
+  (void)timeouts;
+  if(ARES_SUCCESS == status) {
+    res->temp_ai = ares2addr(result->nodes);
+    res->last_status = CURL_ASYNC_SUCCESS;
+    ares_freeaddrinfo(result);
+  }
+  res->num_pending--;
+}
 
+#endif
 /*
  * Curl_resolver_getaddrinfo() - when using ares
  *
@@ -643,8 +770,28 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
     /* initial status - failed */
     res->last_status = ARES_ENOTFOUND;
 
-#if ARES_VERSION >= 0x010601
-    /* IPv6 supported by c-ares since 1.6.1 */
+#ifdef HAVE_CARES_GETADDRINFO
+    {
+      struct ares_addrinfo_hints hints;
+      char service[12];
+      int pf = PF_INET;
+      memset(&hints, 0, sizeof(hints));
+#ifdef CURLRES_IPV6
+      if(Curl_ipv6works(data))
+        /* The stack seems to be IPv6-enabled */
+        pf = PF_UNSPEC;
+#endif /* CURLRES_IPV6 */
+      hints.ai_family = pf;
+      hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
+        SOCK_STREAM : SOCK_DGRAM;
+      msnprintf(service, sizeof(service), "%d", port);
+      res->num_pending = 1;
+      ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname,
+                       service, &hints, addrinfo_cb, data);
+    }
+#else
+
+#ifdef HAVE_CARES_IPV6
     if(Curl_ipv6works(data)) {
       /* The stack seems to be IPv6-enabled */
       res->num_pending = 2;
@@ -656,7 +803,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
                           PF_INET6, query_completed_cb, data);
     }
     else
-#endif /* ARES_VERSION >= 0x010601 */
+#endif
     {
       res->num_pending = 1;
 
@@ -665,7 +812,7 @@ struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
                          hostname, PF_INET,
                          query_completed_cb, data);
     }
-
+#endif
     *waitp = 1; /* expect asynchronous response */
   }
   return NULL; /* no struct yet */
@@ -686,8 +833,8 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data,
   if(!(servers && servers[0]))
     return CURLE_OK;
 
-#if (ARES_VERSION >= 0x010704)
-#if (ARES_VERSION >= 0x010b00)
+#ifdef HAVE_CARES_SERVERS_CSV
+#ifdef HAVE_CARES_PORTS_CSV
   ares_result = ares_set_servers_ports_csv(data->state.async.resolver,
                                            servers);
 #else
@@ -717,7 +864,7 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data,
 CURLcode Curl_set_dns_interface(struct Curl_easy *data,
                                 const char *interf)
 {
-#if (ARES_VERSION >= 0x010704)
+#ifdef HAVE_CARES_LOCAL_DEV
   if(!interf)
     interf = "";
 
@@ -734,7 +881,7 @@ CURLcode Curl_set_dns_interface(struct Curl_easy *data,
 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
                                 const char *local_ip4)
 {
-#if (ARES_VERSION >= 0x010704)
+#ifdef HAVE_CARES_SET_LOCAL
   struct in_addr a4;
 
   if((!local_ip4) || (local_ip4[0] == 0)) {
@@ -760,7 +907,7 @@ CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
                                 const char *local_ip6)
 {
-#if (ARES_VERSION >= 0x010704) && defined(ENABLE_IPV6)
+#if defined(HAVE_CARES_SET_LOCAL) && defined(ENABLE_IPV6)
   unsigned char a6[INET6_ADDRSTRLEN];
 
   if((!local_ip6) || (local_ip6[0] == 0)) {

+ 0 - 1
lib/asyn-thread.c

@@ -68,7 +68,6 @@
 #include "hostip.h"
 #include "hash.h"
 #include "share.h"
-#include "strerror.h"
 #include "url.h"
 #include "multiif.h"
 #include "inet_ntop.h"

+ 214 - 43
lib/c-hyper.c

@@ -124,6 +124,17 @@ static int hyper_each_header(void *userdata,
   size_t len;
   char *headp;
   CURLcode result;
+  int writetype;
+
+  if(name_len + value_len + 2 > CURL_MAX_HTTP_HEADER) {
+    failf(data, "Too long response header");
+    data->state.hresult = CURLE_OUT_OF_MEMORY;
+    return HYPER_ITER_BREAK;
+  }
+
+  if(!data->req.bytecount)
+    Curl_pgrsTime(data, TIMER_STARTTRANSFER);
+
   Curl_dyn_reset(&data->state.headerb);
   if(name_len) {
     if(Curl_dyn_addf(&data->state.headerb, "%.*s: %.*s\r\n",
@@ -145,7 +156,10 @@ static int hyper_each_header(void *userdata,
 
   Curl_debug(data, CURLINFO_HEADER_IN, headp, len);
 
-  result = Curl_client_write(data, CLIENTWRITE_HEADER, headp, len);
+  writetype = CLIENTWRITE_HEADER;
+  if(data->set.include_header)
+    writetype |= CLIENTWRITE_BODY;
+  result = Curl_client_write(data, writetype, headp, len);
   if(result) {
     data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
     return HYPER_ITER_BREAK;
@@ -162,13 +176,43 @@ static int hyper_body_chunk(void *userdata, const hyper_buf *chunk)
   size_t len = hyper_buf_len(chunk);
   struct Curl_easy *data = (struct Curl_easy *)userdata;
   struct SingleRequest *k = &data->req;
-  CURLcode result;
+  CURLcode result = CURLE_OK;
 
   if(0 == k->bodywrites++) {
     bool done = FALSE;
-    result = Curl_http_firstwrite(data, data->conn, &done);
+#if defined(USE_NTLM)
+    struct connectdata *conn = data->conn;
+    if(conn->bits.close &&
+       (((data->req.httpcode == 401) &&
+         (conn->http_ntlm_state == NTLMSTATE_TYPE2)) ||
+        ((data->req.httpcode == 407) &&
+         (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) {
+      infof(data, "Connection closed while negotiating NTLM");
+      data->state.authproblem = TRUE;
+      Curl_safefree(data->req.newurl);
+    }
+#endif
+    if(data->state.expect100header) {
+      Curl_expire_done(data, EXPIRE_100_TIMEOUT);
+      if(data->req.httpcode < 400) {
+        k->exp100 = EXP100_SEND_DATA;
+        if(data->hyp.exp100_waker) {
+          hyper_waker_wake(data->hyp.exp100_waker);
+          data->hyp.exp100_waker = NULL;
+        }
+      }
+      else { /* >= 4xx */
+        k->exp100 = EXP100_FAILED;
+      }
+    }
+    if(data->state.hconnect && (data->req.httpcode/100 != 2)) {
+      done = TRUE;
+      result = CURLE_OK;
+    }
+    else
+      result = Curl_http_firstwrite(data, data->conn, &done);
     if(result || done) {
-      infof(data, "Return early from hyper_body_chunk\n");
+      infof(data, "Return early from hyper_body_chunk");
       data->state.hresult = result;
       return HYPER_ITER_BREAK;
     }
@@ -207,11 +251,15 @@ static CURLcode status_line(struct Curl_easy *data,
   CURLcode result;
   size_t len;
   const char *vstr;
+  int writetype;
   vstr = http_version == HYPER_HTTP_VERSION_1_1 ? "1.1" :
     (http_version == HYPER_HTTP_VERSION_2 ? "2" : "1.0");
   conn->httpversion =
     http_version == HYPER_HTTP_VERSION_1_1 ? 11 :
     (http_version == HYPER_HTTP_VERSION_2 ? 20 : 10);
+  if(http_version == HYPER_HTTP_VERSION_1_0)
+    data->state.httpwant = CURL_HTTP_VERSION_1_0;
+
   data->req.httpcode = http_status;
 
   result = Curl_http_statusline(data, conn);
@@ -229,7 +277,10 @@ static CURLcode status_line(struct Curl_easy *data,
   len = Curl_dyn_len(&data->state.headerb);
   Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&data->state.headerb),
              len);
-  result = Curl_client_write(data, CLIENTWRITE_HEADER,
+  writetype = CLIENTWRITE_HEADER;
+  if(data->set.include_header)
+    writetype |= CLIENTWRITE_BODY;
+  result = Curl_client_write(data, writetype,
                              Curl_dyn_ptr(&data->state.headerb), len);
   if(result) {
     data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
@@ -270,8 +321,25 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
   const uint8_t *reasonp;
   size_t reason_len;
   CURLcode result = CURLE_OK;
+  struct SingleRequest *k = &data->req;
   (void)conn;
 
+  if(k->exp100 > EXP100_SEND_DATA) {
+    struct curltime now = Curl_now();
+    timediff_t ms = Curl_timediff(now, k->start100);
+    if(ms >= data->set.expect_100_timeout) {
+      /* we've waited long enough, continue anyway */
+      k->exp100 = EXP100_SEND_DATA;
+      k->keepon |= KEEP_SEND;
+      Curl_expire_done(data, EXPIRE_100_TIMEOUT);
+      infof(data, "Done waiting for 100-continue");
+      if(data->hyp.exp100_waker) {
+        hyper_waker_wake(data->hyp.exp100_waker);
+        data->hyp.exp100_waker = NULL;
+      }
+    }
+  }
+
   if(select_res & CURL_CSELECT_IN) {
     if(h->read_waker)
       hyper_waker_wake(h->read_waker);
@@ -305,19 +373,22 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
     hyper_task_free(task);
 
     if(t == HYPER_TASK_ERROR) {
-      hyper_code errnum = hyper_error_code(hypererr);
-      if(errnum == HYPERE_ABORTED_BY_CALLBACK) {
+      if(data->state.hresult) {
         /* override Hyper's view, might not even be an error */
         result = data->state.hresult;
-        infof(data, "hyperstream is done (by early callback)\n");
+        infof(data, "hyperstream is done (by early callback)");
       }
       else {
         uint8_t errbuf[256];
         size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
         hyper_code code = hyper_error_code(hypererr);
         failf(data, "Hyper: [%d] %.*s", (int)code, (int)errlen, errbuf);
-        if((code == HYPERE_UNEXPECTED_EOF) && !data->req.bytecount)
+        if(code == HYPERE_ABORTED_BY_CALLBACK)
+          result = CURLE_OK;
+        else if((code == HYPERE_UNEXPECTED_EOF) && !data->req.bytecount)
           result = CURLE_GOT_NOTHING;
+        else if(code == HYPERE_INVALID_PEER_MESSAGE)
+          result = CURLE_UNSUPPORTED_PROTOCOL; /* maybe */
         else
           result = CURLE_RECV_ERROR;
       }
@@ -328,10 +399,15 @@ CURLcode Curl_hyper_stream(struct Curl_easy *data,
     else if(h->endtask == task) {
       /* end of transfer */
       *done = TRUE;
-      infof(data, "hyperstream is done!\n");
+      infof(data, "hyperstream is done!");
+      if(!k->bodywrites) {
+        /* hyper doesn't always call the body write callback */
+        bool stilldone;
+        result = Curl_http_firstwrite(data, data->conn, &stilldone);
+      }
       break;
     }
-    else if(t != HYPER_TASK_RESPONSE && t != HYPER_TASK_EMPTY) {
+    else if(t != HYPER_TASK_RESPONSE) {
       *didwhat = KEEP_RECV;
       break;
     }
@@ -472,7 +548,7 @@ CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers,
 
     if(HYPERE_OK != hyper_headers_add(headers, (uint8_t *)n, nlen,
                                       (uint8_t *)v, vlen)) {
-      failf(data, "hyper_headers_add host");
+      failf(data, "hyper refused to add header '%s'", line);
       return CURLE_OUT_OF_MEMORY;
     }
     if(data->set.verbose) {
@@ -485,7 +561,7 @@ CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers,
         free(ptr);
       }
       else
-        Curl_debug(data, CURLINFO_HEADER_OUT, (char *)line, linelen);
+        Curl_debug(data, CURLINFO_HEADER_OUT, (char *)n, linelen);
     }
     numh++;
     n += linelen;
@@ -526,12 +602,32 @@ static int uploadpostfields(void *userdata, hyper_context *ctx,
 {
   struct Curl_easy *data = (struct Curl_easy *)userdata;
   (void)ctx;
+  if(data->req.exp100 > EXP100_SEND_DATA) {
+    if(data->req.exp100 == EXP100_FAILED)
+      return HYPER_POLL_ERROR;
+
+    /* still waiting confirmation */
+    if(data->hyp.exp100_waker)
+      hyper_waker_free(data->hyp.exp100_waker);
+    data->hyp.exp100_waker = hyper_context_waker(ctx);
+    return HYPER_POLL_PENDING;
+  }
   if(data->req.upload_done)
     *chunk = NULL; /* nothing more to deliver */
   else {
     /* send everything off in a single go */
-    *chunk = hyper_buf_copy(data->set.postfields,
-                            (size_t)data->req.p.http->postsize);
+    hyper_buf *copy = hyper_buf_copy(data->set.postfields,
+                                     (size_t)data->req.p.http->postsize);
+    if(copy)
+      *chunk = copy;
+    else {
+      data->state.hresult = CURLE_OUT_OF_MEMORY;
+      return HYPER_POLL_ERROR;
+    }
+    /* increasing the writebytecount here is a little premature but we
+       don't know exactly when the body is sent*/
+    data->req.writebytecount += (size_t)data->req.p.http->postsize;
+    Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
     data->req.upload_done = TRUE;
   }
   return HYPER_POLL_READY;
@@ -542,16 +638,41 @@ static int uploadstreamed(void *userdata, hyper_context *ctx,
 {
   size_t fillcount;
   struct Curl_easy *data = (struct Curl_easy *)userdata;
-  CURLcode result =
-    Curl_fillreadbuffer(data, data->set.upload_buffer_size, &fillcount);
+  CURLcode result;
   (void)ctx;
-  if(result)
+
+  if(data->req.exp100 > EXP100_SEND_DATA) {
+    if(data->req.exp100 == EXP100_FAILED)
+      return HYPER_POLL_ERROR;
+
+    /* still waiting confirmation */
+    if(data->hyp.exp100_waker)
+      hyper_waker_free(data->hyp.exp100_waker);
+    data->hyp.exp100_waker = hyper_context_waker(ctx);
+    return HYPER_POLL_PENDING;
+  }
+
+  result = Curl_fillreadbuffer(data, data->set.upload_buffer_size, &fillcount);
+  if(result) {
+    data->state.hresult = result;
     return HYPER_POLL_ERROR;
+  }
   if(!fillcount)
     /* done! */
     *chunk = NULL;
-  else
-    *chunk = hyper_buf_copy((uint8_t *)data->state.ulbuf, fillcount);
+  else {
+    hyper_buf *copy = hyper_buf_copy((uint8_t *)data->state.ulbuf, fillcount);
+    if(copy)
+      *chunk = copy;
+    else {
+      data->state.hresult = CURLE_OUT_OF_MEMORY;
+      return HYPER_POLL_ERROR;
+    }
+    /* increasing the writebytecount here is a little premature but we
+       don't know exactly when the body is sent*/
+    data->req.writebytecount += fillcount;
+    Curl_pgrsSetUploadCounter(data, fillcount);
+  }
   return HYPER_POLL_READY;
 }
 
@@ -566,6 +687,7 @@ static CURLcode bodysend(struct Curl_easy *data,
                          hyper_request *hyperreq,
                          Curl_HttpReq httpreq)
 {
+  struct HTTP *http = data->req.p.http;
   CURLcode result = CURLE_OK;
   struct dynbuf req;
   if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD))
@@ -598,6 +720,7 @@ static CURLcode bodysend(struct Curl_easy *data,
       result = CURLE_OUT_OF_MEMORY;
     }
   }
+  http->sending = HTTPSEND_BODY;
   return result;
 }
 
@@ -616,6 +739,48 @@ static CURLcode cookies(struct Curl_easy *data,
   return result;
 }
 
+/* called on 1xx responses */
+static void http1xx_cb(void *arg, struct hyper_response *resp)
+{
+  struct Curl_easy *data = (struct Curl_easy *)arg;
+  hyper_headers *headers = NULL;
+  CURLcode result = CURLE_OK;
+  uint16_t http_status;
+  int http_version;
+  const uint8_t *reasonp;
+  size_t reason_len;
+
+  infof(data, "Got HTTP 1xx informational");
+
+  http_status = hyper_response_status(resp);
+  http_version = hyper_response_version(resp);
+  reasonp = hyper_response_reason_phrase(resp);
+  reason_len = hyper_response_reason_phrase_len(resp);
+
+  result = status_line(data, data->conn,
+                       http_status, http_version, reasonp, reason_len);
+  if(!result) {
+    headers = hyper_response_headers(resp);
+    if(!headers) {
+      failf(data, "hyperstream: couldn't get 1xx response headers");
+      result = CURLE_RECV_ERROR;
+    }
+  }
+  data->state.hresult = result;
+
+  if(!result) {
+    /* the headers are already received */
+    hyper_headers_foreach(headers, hyper_each_header, data);
+    /* this callback also sets data->state.hresult on error */
+
+    if(empty_header(data))
+      result = CURLE_OUT_OF_MEMORY;
+  }
+
+  if(data->state.hresult)
+    infof(data, "ERROR in 1xx, bail out!");
+}
+
 /*
  * Curl_http() gets called from the generic multi_do() function when a HTTP
  * request is to be performed. This creates and sends a properly constructed
@@ -633,20 +798,20 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   hyper_request *req = NULL;
   hyper_headers *headers = NULL;
   hyper_task *handshake = NULL;
-  hyper_error *hypererr = NULL;
   CURLcode result;
   const char *p_accept; /* Accept: string */
   const char *method;
   Curl_HttpReq httpreq;
   bool h2 = FALSE;
   const char *te = NULL; /* transfer-encoding */
+  hyper_code rc;
 
   /* Always consider the DO phase done after this function call, even if there
      may be parts of the request that is not yet sent, since we can deal with
      the rest of the request in the PERFORM phase. */
   *done = TRUE;
 
-  infof(data, "Time for the Hyper dance\n");
+  infof(data, "Time for the Hyper dance");
   memset(h, 0, sizeof(struct hyptransfer));
 
   result = Curl_http_host(data, conn);
@@ -743,7 +908,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     goto error;
   }
 
-  if(data->state.httpwant == CURL_HTTP_VERSION_1_0) {
+  if(!Curl_use_http_1_1plus(data, conn)) {
     if(HYPERE_OK != hyper_request_set_version(req,
                                               HYPER_HTTP_VERSION_1_0)) {
       failf(data, "error setting HTTP version");
@@ -766,6 +931,10 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     goto error;
   }
 
+  rc = hyper_request_on_informational(req, http1xx_cb, data);
+  if(rc)
+    return CURLE_OUT_OF_MEMORY;
+
   result = Curl_http_body(data, conn, httpreq, &te);
   if(result)
     return result;
@@ -830,6 +999,15 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   else
     Curl_safefree(data->state.aptr.accept_encoding);
 
+#ifdef HAVE_LIBZ
+  /* we only consider transfer-encoding magic if libz support is built-in */
+  result = Curl_transferencode(data);
+  if(result)
+    return result;
+  if(Curl_hyper_header(data, headers, data->state.aptr.te))
+    goto error;
+#endif
+
   result = cookies(data, conn, headers);
   if(result)
     return result;
@@ -862,25 +1040,21 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
 
   hyper_clientconn_free(client);
 
-  do {
-    task = hyper_executor_poll(h->exec);
-    if(task) {
-      bool error = hyper_task_type(task) == HYPER_TASK_ERROR;
-      if(error)
-        hypererr = hyper_task_value(task);
-      hyper_task_free(task);
-      if(error)
-        goto error;
-    }
-  } while(task);
-
   if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) {
     /* HTTP GET/HEAD download */
     Curl_pgrsSetUploadSize(data, 0); /* nothing */
     Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1);
   }
   conn->datastream = Curl_hyper_stream;
-
+  if(data->state.expect100header)
+    /* Timeout count starts now since with Hyper we don't know exactly when
+       the full request has been sent. */
+    data->req.start100 = Curl_now();
+
+  /* clear userpwd and proxyuserpwd to avoid re-using old credentials
+   * from re-used connections */
+  Curl_safefree(data->state.aptr.userpwd);
+  Curl_safefree(data->state.aptr.proxyuserpwd);
   return CURLE_OK;
   error:
 
@@ -893,13 +1067,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   if(handshake)
     hyper_task_free(handshake);
 
-  if(hypererr) {
-    uint8_t errbuf[256];
-    size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
-    hyper_code code = hyper_error_code(hypererr);
-    failf(data, "Hyper: [%d] %.*s", (int)code, (int)errlen, errbuf);
-    hyper_error_free(hypererr);
-  }
   return CURLE_OUT_OF_MEMORY;
 }
 
@@ -918,6 +1085,10 @@ void Curl_hyper_done(struct Curl_easy *data)
     hyper_waker_free(h->write_waker);
     h->write_waker = NULL;
   }
+  if(h->exp100_waker) {
+    hyper_waker_free(h->exp100_waker);
+    h->exp100_waker = NULL;
+  }
 }
 
 #endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */

+ 1 - 0
lib/c-hyper.h

@@ -33,6 +33,7 @@ struct hyptransfer {
   hyper_waker *read_waker;
   const hyper_executor *exec;
   hyper_task *endtask;
+  hyper_waker *exp100_waker;
 };
 
 size_t Curl_hyper_recv(void *userp, hyper_context *ctx,

+ 7 - 5
lib/conncache.c

@@ -34,6 +34,7 @@
 #include "share.h"
 #include "sigpipe.h"
 #include "connect.h"
+#include "strcase.h"
 
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -161,6 +162,7 @@ static void hashkey(struct connectdata *conn, char *buf,
 
   /* put the number first so that the hostname gets cut off if too long */
   msnprintf(buf, len, "%ld%s", port, hostname);
+  Curl_strntolower(buf, buf, len);
 }
 
 /* Returns number of connections currently held in the connection cache.
@@ -264,7 +266,7 @@ CURLcode Curl_conncache_add_conn(struct Curl_easy *data)
   connc->num_conn++;
 
   DEBUGF(infof(data, "Added connection %ld. "
-               "The cache now contains %zu members\n",
+               "The cache now contains %zu members",
                conn->connection_id, connc->num_conn));
 
   unlock:
@@ -298,7 +300,7 @@ void Curl_conncache_remove_conn(struct Curl_easy *data,
     conn->bundle = NULL; /* removed from it */
     if(connc) {
       connc->num_conn--;
-      DEBUGF(infof(data, "The cache now contains %zu members\n",
+      DEBUGF(infof(data, "The cache now contains %zu members",
                    connc->num_conn));
     }
     if(lock) {
@@ -408,7 +410,7 @@ bool Curl_conncache_return_conn(struct Curl_easy *data,
   conn->lastused = Curl_now(); /* it was used up until now */
   if(maxconnects > 0 &&
      Curl_conncache_size(data) > maxconnects) {
-    infof(data, "Connection cache is full, closing the oldest one.\n");
+    infof(data, "Connection cache is full, closing the oldest one");
 
     conn_candidate = Curl_conncache_extract_oldest(data);
     if(conn_candidate) {
@@ -464,7 +466,7 @@ Curl_conncache_extract_bundle(struct Curl_easy *data,
     /* remove it to prevent another thread from nicking it */
     bundle_remove_conn(bundle, conn_candidate);
     data->state.conn_cache->num_conn--;
-    DEBUGF(infof(data, "The cache now contains %zu members\n",
+    DEBUGF(infof(data, "The cache now contains %zu members",
                  data->state.conn_cache->num_conn));
   }
 
@@ -526,7 +528,7 @@ Curl_conncache_extract_oldest(struct Curl_easy *data)
     /* remove it to prevent another thread from nicking it */
     bundle_remove_conn(bundle_candidate, conn_candidate);
     connc->num_conn--;
-    DEBUGF(infof(data, "The cache now contains %zu members\n",
+    DEBUGF(infof(data, "The cache now contains %zu members",
                  connc->num_conn));
   }
   CONNCACHE_UNLOCK(data);

+ 46 - 38
lib/connect.c

@@ -111,7 +111,7 @@ tcpkeepalive(struct Curl_easy *data,
   /* only set IDLE and INTVL if setting KEEPALIVE is successful */
   if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
         (void *)&optval, sizeof(optval)) < 0) {
-    infof(data, "Failed to set SO_KEEPALIVE on fd %d\n", sockfd);
+    infof(data, "Failed to set SO_KEEPALIVE on fd %d", sockfd);
   }
   else {
 #if defined(SIO_KEEPALIVE_VALS)
@@ -126,7 +126,7 @@ tcpkeepalive(struct Curl_easy *data,
     vals.keepaliveinterval = optval;
     if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
                 NULL, 0, &dummy, NULL, NULL) != 0) {
-      infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d\n",
+      infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd %d: %d",
             (int)sockfd, WSAGetLastError());
     }
 #else
@@ -135,7 +135,7 @@ tcpkeepalive(struct Curl_easy *data,
     KEEPALIVE_FACTOR(optval);
     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
           (void *)&optval, sizeof(optval)) < 0) {
-      infof(data, "Failed to set TCP_KEEPIDLE on fd %d\n", sockfd);
+      infof(data, "Failed to set TCP_KEEPIDLE on fd %d", sockfd);
     }
 #endif
 #ifdef TCP_KEEPINTVL
@@ -143,7 +143,7 @@ tcpkeepalive(struct Curl_easy *data,
     KEEPALIVE_FACTOR(optval);
     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
           (void *)&optval, sizeof(optval)) < 0) {
-      infof(data, "Failed to set TCP_KEEPINTVL on fd %d\n", sockfd);
+      infof(data, "Failed to set TCP_KEEPINTVL on fd %d", sockfd);
     }
 #endif
 #ifdef TCP_KEEPALIVE
@@ -152,7 +152,7 @@ tcpkeepalive(struct Curl_easy *data,
     KEEPALIVE_FACTOR(optval);
     if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
           (void *)&optval, sizeof(optval)) < 0) {
-      infof(data, "Failed to set TCP_KEEPALIVE on fd %d\n", sockfd);
+      infof(data, "Failed to set TCP_KEEPALIVE on fd %d", sockfd);
     }
 #endif
 #endif
@@ -331,7 +331,7 @@ static CURLcode bindlocal(struct Curl_easy *data,
           /*
            * We now have the numerical IP address in the 'myhost' buffer
            */
-          infof(data, "Local Interface %s is ip %s using address family %i\n",
+          infof(data, "Local Interface %s is ip %s using address family %i",
                 dev, myhost, af);
           done = 1;
           break;
@@ -364,7 +364,7 @@ static CURLcode bindlocal(struct Curl_easy *data,
       if(h) {
         /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
         Curl_printable_address(h->addr, myhost, sizeof(myhost));
-        infof(data, "Name '%s' family %i resolved to '%s' family %i\n",
+        infof(data, "Name '%s' family %i resolved to '%s' family %i",
               dev, af, myhost, h->addr->ai_family);
         Curl_resolv_unlock(data, h);
         if(af != h->addr->ai_family) {
@@ -458,13 +458,13 @@ static CURLcode bindlocal(struct Curl_easy *data,
               error, Curl_strerror(error, buffer, sizeof(buffer)));
         return CURLE_INTERFACE_FAILED;
       }
-      infof(data, "Local port: %hu\n", port);
+      infof(data, "Local port: %hu", port);
       conn->bits.bound = TRUE;
       return CURLE_OK;
     }
 
     if(--portnum > 0) {
-      infof(data, "Bind to local port %hu failed, trying next\n", port);
+      infof(data, "Bind to local port %hu failed, trying next", port);
       port++; /* try next port */
       /* We re-use/clobber the port variable here below */
       if(sock->sa_family == AF_INET)
@@ -589,12 +589,10 @@ static CURLcode trynextip(struct Curl_easy *data,
     struct Curl_addrinfo *ai = conn->tempaddr[tempindex];
 
     while(ai) {
-      if(ai) {
-        result = singleipconnect(data, conn, ai, tempindex);
-        if(result == CURLE_COULDNT_CONNECT) {
-          ai = ainext(conn, tempindex, TRUE);
-          continue;
-        }
+      result = singleipconnect(data, conn, ai, tempindex);
+      if(result == CURLE_COULDNT_CONNECT) {
+        ai = ainext(conn, tempindex, TRUE);
+        continue;
       }
       break;
     }
@@ -753,10 +751,9 @@ void Curl_updateconninfo(struct Curl_easy *data, struct connectdata *conn,
   int local_port = -1;
 
   if(conn->transport == TRNSPRT_TCP) {
-    if(!conn->bits.reuse && !conn->bits.tcp_fastopen) {
+    if(!conn->bits.reuse && !conn->bits.tcp_fastopen)
       Curl_conninfo_remote(data, conn, sockfd);
-      Curl_conninfo_local(data, sockfd, local_ip, &local_port);
-    }
+    Curl_conninfo_local(data, sockfd, local_ip, &local_port);
   } /* end of TCP-only section */
 
   /* persist connection info in session handle */
@@ -872,15 +869,6 @@ CURLcode Curl_is_connected(struct Curl_easy *data,
 
   now = Curl_now();
 
-  /* figure out how long time we have left to connect */
-  allow = Curl_timeleft(data, &now, TRUE);
-
-  if(allow < 0) {
-    /* time-out, bail out, go home */
-    failf(data, "Connection time-out");
-    return CURLE_OPERATION_TIMEDOUT;
-  }
-
   if(SOCKS_STATE(conn->cnnct.state)) {
     /* still doing SOCKS */
     result = connect_SOCKS(data, sockindex, connected);
@@ -930,7 +918,7 @@ CURLcode Curl_is_connected(struct Curl_easy *data,
       if(Curl_timediff(now, conn->connecttime) >=
          conn->timeoutms_per_addr[i]) {
         infof(data, "After %" CURL_FORMAT_TIMEDIFF_T
-              "ms connect time, move on!\n", conn->timeoutms_per_addr[i]);
+              "ms connect time, move on!", conn->timeoutms_per_addr[i]);
         error = ETIMEDOUT;
       }
 
@@ -989,11 +977,12 @@ CURLcode Curl_is_connected(struct Curl_easy *data,
         char buffer[STRERROR_LEN];
         Curl_printable_address(conn->tempaddr[i], ipaddress,
                                sizeof(ipaddress));
-        infof(data, "connect to %s port %ld failed: %s\n",
+        infof(data, "connect to %s port %u failed: %s",
               ipaddress, conn->port,
               Curl_strerror(error, buffer, sizeof(buffer)));
 #endif
 
+        allow = Curl_timeleft(data, &now, TRUE);
         conn->timeoutms_per_addr[i] = conn->tempaddr[i]->ai_next == NULL ?
           allow : allow / 2;
         ainext(conn, i, TRUE);
@@ -1006,6 +995,21 @@ CURLcode Curl_is_connected(struct Curl_easy *data,
     }
   }
 
+  /*
+   * Now that we've checked whether we are connected, check whether we've
+   * already timed out.
+   *
+   * First figure out how long time we have left to connect */
+
+  allow = Curl_timeleft(data, &now, TRUE);
+
+  if(allow < 0) {
+    /* time-out, bail out, go home */
+    failf(data, "Connection timeout after %ld ms",
+          Curl_timediff(now, data->progress.t_startsingle));
+    return CURLE_OPERATION_TIMEDOUT;
+  }
+
   if(result &&
      (conn->tempsock[0] == CURL_SOCKET_BAD) &&
      (conn->tempsock[1] == CURL_SOCKET_BAD)) {
@@ -1031,9 +1035,11 @@ CURLcode Curl_is_connected(struct Curl_easy *data,
     else
       hostname = conn->host.name;
 
-    failf(data, "Failed to connect to %s port %ld: %s",
-          hostname, conn->port,
-          Curl_strerror(error, buffer, sizeof(buffer)));
+    failf(data, "Failed to connect to %s port %u after "
+                "%" CURL_FORMAT_TIMEDIFF_T " ms: %s",
+        hostname, conn->port,
+        Curl_timediff(now, data->progress.t_startsingle),
+        Curl_strerror(error, buffer, sizeof(buffer)));
 
     Curl_quic_disconnect(data, conn, 0);
     Curl_quic_disconnect(data, conn, 1);
@@ -1065,7 +1071,7 @@ static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
 
   if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
                 sizeof(onoff)) < 0)
-    infof(data, "Could not set TCP_NODELAY: %s\n",
+    infof(data, "Could not set TCP_NODELAY: %s",
           Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
 #else
   (void)data;
@@ -1084,9 +1090,11 @@ static void nosigpipe(struct Curl_easy *data,
   int onoff = 1;
   if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
                 sizeof(onoff)) < 0) {
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
     char buffer[STRERROR_LEN];
-    infof(data, "Could not set SO_NOSIGPIPE: %s\n",
+    infof(data, "Could not set SO_NOSIGPIPE: %s",
           Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+#endif
   }
 }
 #else
@@ -1180,7 +1188,7 @@ static CURLcode singleipconnect(struct Curl_easy *data,
     Curl_closesocket(data, conn, sockfd);
     return CURLE_OK;
   }
-  infof(data, "  Trying %s:%d...\n", ipaddress, port);
+  infof(data, "  Trying %s:%d...", ipaddress, port);
 
 #ifdef ENABLE_IPV6
   is_tcp = (addr.family == AF_INET || addr.family == AF_INET6) &&
@@ -1271,7 +1279,7 @@ static CURLcode singleipconnect(struct Curl_easy *data,
 #elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
       if(setsockopt(sockfd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
                     (void *)&optval, sizeof(optval)) < 0)
-        infof(data, "Failed to enable TCP Fast Open on fd %d\n", sockfd);
+        infof(data, "Failed to enable TCP Fast Open on fd %d", sockfd);
 
       rc = connect(sockfd, &addr.sa_addr, addr.addrlen);
 #elif defined(MSG_FASTOPEN) /* old Linux */
@@ -1321,7 +1329,7 @@ static CURLcode singleipconnect(struct Curl_easy *data,
 
     default:
       /* unknown error, fallthrough and try another address! */
-      infof(data, "Immediate connect fail for %s: %s\n",
+      infof(data, "Immediate connect fail for %s: %s",
             ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
       data->state.os_errno = error;
 
@@ -1394,7 +1402,7 @@ CURLcode Curl_connecthost(struct Curl_easy *data,
 
   ainext(conn, 1, FALSE); /* assigns conn->tempaddr[1] accordingly */
 
-  DEBUGF(infof(data, "family0 == %s, family1 == %s\n",
+  DEBUGF(infof(data, "family0 == %s, family1 == %s",
                conn->tempfamily[0] == AF_INET ? "v4" : "v6",
                conn->tempfamily[1] == AF_INET ? "v4" : "v6"));
 

+ 48 - 39
lib/cookie.c

@@ -95,7 +95,6 @@ Example set of cookies:
 #include "strcase.h"
 #include "curl_get_line.h"
 #include "curl_memrchr.h"
-#include "inet_pton.h"
 #include "parsedate.h"
 #include "rand.h"
 #include "rename.h"
@@ -146,31 +145,6 @@ static bool tailmatch(const char *cooke_domain, const char *hostname)
   return FALSE;
 }
 
-/*
- * isip
- *
- * Returns true if the given string is an IPv4 or IPv6 address (if IPv6 has
- * been enabled while building libcurl, and false otherwise.
- */
-static bool isip(const char *domain)
-{
-  struct in_addr addr;
-#ifdef ENABLE_IPV6
-  struct in6_addr addr6;
-#endif
-
-  if(Curl_inet_pton(AF_INET, domain, &addr)
-#ifdef ENABLE_IPV6
-     || Curl_inet_pton(AF_INET6, domain, &addr6)
-#endif
-    ) {
-    /* domain name given as IP address */
-    return TRUE;
-  }
-
-  return FALSE;
-}
-
 /*
  * matching cookie path and url path
  * RFC6265 5.1.4 Paths and Path-Match
@@ -303,7 +277,7 @@ static size_t cookiehash(const char * const domain)
   const char *top;
   size_t len;
 
-  if(!domain || isip(domain))
+  if(!domain || Curl_host_is_ipnum(domain))
     return 0;
 
   top = get_top_domain(domain, &len);
@@ -366,7 +340,7 @@ void Curl_cookie_loadfiles(struct Curl_easy *data)
          * Failure may be due to OOM or a bad cookie; both are ignored
          * but only the first should be
          */
-        infof(data, "ignoring failed cookie_init for %s\n", list->data);
+        infof(data, "ignoring failed cookie_init for %s", list->data);
       else
         data->cookies = newcookies;
       list = list->next;
@@ -397,7 +371,9 @@ static void strstore(char **str, const char *newstr)
  *
  * Remove expired cookies from the hash by inspecting the expires timestamp on
  * each cookie in the hash, freeing and deleting any where the timestamp is in
- * the past.
+ * the past.  If the cookiejar has recorded the next timestamp at which one or
+ * more cookies expire, then processing will exit early in case this timestamp
+ * is in the future.
  */
 static void remove_expired(struct CookieInfo *cookies)
 {
@@ -405,6 +381,20 @@ static void remove_expired(struct CookieInfo *cookies)
   curl_off_t now = (curl_off_t)time(NULL);
   unsigned int i;
 
+  /*
+   * If the earliest expiration timestamp in the jar is in the future we can
+   * skip scanning the whole jar and instead exit early as there won't be any
+   * cookies to evict.  If we need to evict however, reset the next_expiration
+   * counter in order to track the next one. In case the recorded first
+   * expiration is the max offset, then perform the safe fallback of checking
+   * all cookies.
+   */
+  if(now < cookies->next_expiration &&
+      cookies->next_expiration != CURL_OFF_T_MAX)
+    return;
+  else
+    cookies->next_expiration = CURL_OFF_T_MAX;
+
   for(i = 0; i < COOKIE_HASH_SIZE; i++) {
     struct Cookie *pv = NULL;
     co = cookies->cookies[i];
@@ -421,6 +411,12 @@ static void remove_expired(struct CookieInfo *cookies)
         freecookie(co);
       }
       else {
+        /*
+         * If this cookie has an expiration timestamp earlier than what we've
+         * seen so far then record it for the next round of expirations.
+         */
+        if(co->expires && co->expires < cookies->next_expiration)
+          cookies->next_expiration = co->expires;
         pv = co;
       }
       co = nx;
@@ -524,7 +520,7 @@ Curl_cookie_add(struct Curl_easy *data,
         if(nlen >= (MAX_NAME-1) || len >= (MAX_NAME-1) ||
            ((nlen + len) > MAX_NAME)) {
           freecookie(co);
-          infof(data, "oversized cookie dropped, name/val %zu + %zu bytes\n",
+          infof(data, "oversized cookie dropped, name/val %zu + %zu bytes",
                 nlen, len);
           return NULL;
         }
@@ -645,7 +641,7 @@ Curl_cookie_add(struct Curl_easy *data,
             domain = ":";
 #endif
 
-          is_ip = isip(domain ? domain : whatptr);
+          is_ip = Curl_host_is_ipnum(domain ? domain : whatptr);
 
           if(!domain
              || (is_ip && !strcmp(whatptr, domain))
@@ -665,7 +661,7 @@ Curl_cookie_add(struct Curl_easy *data,
              * not a domain to which the current host belongs. Mark as bad.
              */
             badcookie = TRUE;
-            infof(data, "skipped cookie with bad tailmatch domain: %s\n",
+            infof(data, "skipped cookie with bad tailmatch domain: %s",
                   whatptr);
           }
         }
@@ -996,7 +992,7 @@ Curl_cookie_add(struct Curl_easy *data,
    * must also check that the data handle isn't NULL since the psl code will
    * dereference it.
    */
-  if(data && (domain && co->domain && !isip(co->domain))) {
+  if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) {
     const psl_ctx_t *psl = Curl_psl_use(data);
     int acceptable;
 
@@ -1009,7 +1005,7 @@ Curl_cookie_add(struct Curl_easy *data,
 
     if(!acceptable) {
       infof(data, "cookie '%s' dropped, domain '%s' must not "
-                  "set cookies for '%s'\n", co->name, domain, co->domain);
+                  "set cookies for '%s'", co->name, domain, co->domain);
       freecookie(co);
       return NULL;
     }
@@ -1121,7 +1117,7 @@ Curl_cookie_add(struct Curl_easy *data,
   if(c->running)
     /* Only show this when NOT reading the cookies from a file */
     infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
-          "expire %" CURL_FORMAT_CURL_OFF_T "\n",
+          "expire %" CURL_FORMAT_CURL_OFF_T,
           replace_old?"Replaced":"Added", co->name, co->value,
           co->domain, co->path, co->expires);
 
@@ -1134,6 +1130,13 @@ Curl_cookie_add(struct Curl_easy *data,
     c->numcookies++; /* one more cookie in the jar */
   }
 
+  /*
+   * Now that we've added a new cookie to the jar, update the expiration
+   * tracker in case it is the next one to expire.
+   */
+  if(co->expires && (co->expires < c->next_expiration))
+    c->next_expiration = co->expires;
+
   return co;
 }
 
@@ -1169,6 +1172,11 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
     c->filename = strdup(file?file:"none"); /* copy the name just in case */
     if(!c->filename)
       goto fail; /* failed to get memory */
+    /*
+     * Initialize the next_expiration time to signal that we don't have enough
+     * information yet.
+     */
+    c->next_expiration = CURL_OFF_T_MAX;
   }
   else {
     /* we got an already existing one, use that */
@@ -1215,7 +1223,7 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
 
     /*
      * Remove expired cookies from the hash. We must make sure to run this
-     * after reading the file, and not not on every cookie.
+     * after reading the file, and not on every cookie.
      */
     remove_expired(c);
 
@@ -1247,7 +1255,8 @@ fail:
  *
  * Helper function to sort cookies such that the longest path gets before the
  * shorter path. Path, domain and name lengths are considered in that order,
- * with tge creationtime as the tiebreaker.
+ * with the creationtime as the tiebreaker. The creationtime is guaranteed to
+ * be unique per cookie, so we know we will get an ordering at that point.
  */
 static int cookie_sort(const void *p1, const void *p2)
 {
@@ -1355,7 +1364,7 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
   remove_expired(c);
 
   /* check if host is an IP(v4|v6) address */
-  is_ip = isip(host);
+  is_ip = Curl_host_is_ipnum(host);
 
   co = c->cookies[myhash];
 
@@ -1734,7 +1743,7 @@ void Curl_flush_cookies(struct Curl_easy *data, bool cleanup)
     /* if we have a destination file for all the cookies to get dumped to */
     res = cookie_output(data, data->cookies, data->set.str[STRING_COOKIEJAR]);
     if(res)
-      infof(data, "WARNING: failed to save cookies in %s: %s\n",
+      infof(data, "WARNING: failed to save cookies in %s: %s",
             data->set.str[STRING_COOKIEJAR], curl_easy_strerror(res));
   }
   else {

+ 1 - 0
lib/cookie.h

@@ -65,6 +65,7 @@ struct CookieInfo {
   bool running;    /* state info, for cookie adding information */
   bool newsession; /* new session, discard session cookies on load */
   int lastct;      /* last creation-time used in the jar */
+  curl_off_t next_expiration; /* the next time at which expiration happens */
 };
 
 /* This is the maximum line length we accept for a cookie line. RFC 2109

+ 0 - 6
lib/curl_addrinfo.c

@@ -50,12 +50,6 @@
 #  define in_addr_t unsigned long
 #endif
 
-#if defined(USE_UNIX_SOCKETS) && defined(WINAPI_FAMILY) && \
-    (WINAPI_FAMILY == WINAPI_FAMILY_APP)
-   /* Required for sockaddr_un type */
-#  include <afunix.h>
-#endif
-
 #include <stddef.h>
 
 #include "curl_addrinfo.h"

+ 50 - 89
lib/curl_config.h.cmake

@@ -33,61 +33,91 @@
 /* Location of default ca path */
 #cmakedefine CURL_CA_PATH "${CURL_CA_PATH}"
 
-/* to disable cookies support */
+/* disables alt-svc */
+#cmakedefine CURL_DISABLE_ALTSVC 1
+
+/* disables cookies support */
 #cmakedefine CURL_DISABLE_COOKIES 1
 
-/* to disable cryptographic authentication */
+/* disables cryptographic authentication */
 #cmakedefine CURL_DISABLE_CRYPTO_AUTH 1
 
-/* to disable DICT */
+/* disables DICT */
 #cmakedefine CURL_DISABLE_DICT 1
 
-/* to disable FILE */
+/* disables DNS-over-HTTPS */
+#cmakedefine CURL_DISABLE_DOH 1
+
+/* disables FILE */
 #cmakedefine CURL_DISABLE_FILE 1
 
-/* to disable FTP */
+/* disables FTP */
 #cmakedefine CURL_DISABLE_FTP 1
 
-/* to disable GOPHER */
+/* disables GOPHER */
 #cmakedefine CURL_DISABLE_GOPHER 1
 
-/* to disable IMAP */
-#cmakedefine CURL_DISABLE_IMAP 1
+/* disables HSTS support */
+#cmakedefine CURL_DISABLE_HSTS 1
 
-/* to disable HTTP */
+/* disables HTTP */
 #cmakedefine CURL_DISABLE_HTTP 1
 
-/* to disable LDAP */
+/* disables IMAP */
+#cmakedefine CURL_DISABLE_IMAP 1
+
+/* disables LDAP */
 #cmakedefine CURL_DISABLE_LDAP 1
 
-/* to disable LDAPS */
+/* disables LDAPS */
 #cmakedefine CURL_DISABLE_LDAPS 1
 
-/* to disable MQTT */
+/* disables --libcurl option from the curl tool */
+#cmakedefine CURL_DISABLE_LIBCURL_OPTION 1
+
+/* disables MIME support */
+#cmakedefine CURL_DISABLE_MIME 1
+
+/* disables MQTT */
 #cmakedefine CURL_DISABLE_MQTT 1
 
-/* to disable POP3 */
+/* disables netrc parser */
+#cmakedefine CURL_DISABLE_NETRC 1
+
+/* disables NTLM support */
+#cmakedefine CURL_DISABLE_NTLM 1
+
+/* disables date parsing */
+#cmakedefine CURL_DISABLE_PARSEDATE 1
+
+/* disables POP3 */
 #cmakedefine CURL_DISABLE_POP3 1
 
-/* to disable proxies */
+/* disables built-in progress meter */
+#cmakedefine CURL_DISABLE_PROGRESS_METER 1
+
+/* disables proxies */
 #cmakedefine CURL_DISABLE_PROXY 1
 
-/* to disable RTSP */
+/* disables RTSP */
 #cmakedefine CURL_DISABLE_RTSP 1
 
-/* to disable SMB */
+/* disables SMB */
 #cmakedefine CURL_DISABLE_SMB 1
 
-/* to disable SMTP */
+/* disables SMTP */
 #cmakedefine CURL_DISABLE_SMTP 1
 
-/* to disable TELNET */
+/* disables use of socketpair for curl_multi_poll */
+#cmakedefine CURL_DISABLE_SOCKETPAIR 1
+
+/* disables TELNET */
 #cmakedefine CURL_DISABLE_TELNET 1
 
-/* to disable TFTP */
+/* disables TFTP */
 #cmakedefine CURL_DISABLE_TFTP 1
 
-/* to disable verbose strings */
+/* disables verbose strings */
 #cmakedefine CURL_DISABLE_VERBOSE_STRINGS 1
 
 /* to make a symbol visible */
@@ -112,12 +142,6 @@
 /* Define if you want to enable IPv6 support */
 #cmakedefine ENABLE_IPV6 1
 
-/* Specifies the number of arguments to getservbyport_r */
-#cmakedefine GETSERVBYPORT_R_ARGS ${GETSERVBYPORT_R_ARGS}
-
-/* Specifies the size of the buffer to pass to getservbyport_r */
-#cmakedefine GETSERVBYPORT_R_BUFSIZE ${GETSERVBYPORT_R_BUFSIZE}
-
 /* Define to 1 if you have the alarm function. */
 #cmakedefine HAVE_ALARM 1
 
@@ -151,18 +175,12 @@
 /* Define to 1 if you have the `CRYPTO_cleanup_all_ex_data' function. */
 #cmakedefine HAVE_CRYPTO_CLEANUP_ALL_EX_DATA 1
 
-/* Define to 1 if you have the <crypto.h> header file. */
-#cmakedefine HAVE_CRYPTO_H 1
-
 /* Define to 1 if you have the <dlfcn.h> header file. */
 #cmakedefine HAVE_DLFCN_H 1
 
 /* Define to 1 if you have the <errno.h> header file. */
 #cmakedefine HAVE_ERRNO_H 1
 
-/* Define to 1 if you have the <err.h> header file. */
-#cmakedefine HAVE_ERR_H 1
-
 /* Define to 1 if you have the fcntl function. */
 #cmakedefine HAVE_FCNTL 1
 
@@ -172,18 +190,9 @@
 /* Define to 1 if you have a working fcntl O_NONBLOCK function. */
 #cmakedefine HAVE_FCNTL_O_NONBLOCK 1
 
-/* Define to 1 if you have the fdopen function. */
-#cmakedefine HAVE_FDOPEN 1
-
-/* Define to 1 if you have the `fork' function. */
-#cmakedefine HAVE_FORK 1
-
 /* Define to 1 if you have the freeaddrinfo function. */
 #cmakedefine HAVE_FREEADDRINFO 1
 
-/* Define to 1 if you have the freeifaddrs function. */
-#cmakedefine HAVE_FREEIFADDRS 1
-
 /* Define to 1 if you have the ftruncate function. */
 #cmakedefine HAVE_FTRUNCATE 1
 
@@ -196,21 +205,6 @@
 /* Define to 1 if you have the `getppid' function. */
 #cmakedefine HAVE_GETPPID 1
 
-/* Define to 1 if you have the gethostbyaddr function. */
-#cmakedefine HAVE_GETHOSTBYADDR 1
-
-/* Define to 1 if you have the gethostbyaddr_r function. */
-#cmakedefine HAVE_GETHOSTBYADDR_R 1
-
-/* gethostbyaddr_r() takes 5 args */
-#cmakedefine HAVE_GETHOSTBYADDR_R_5 1
-
-/* gethostbyaddr_r() takes 7 args */
-#cmakedefine HAVE_GETHOSTBYADDR_R_7 1
-
-/* gethostbyaddr_r() takes 8 args */
-#cmakedefine HAVE_GETHOSTBYADDR_R_8 1
-
 /* Define to 1 if you have the gethostbyname function. */
 #cmakedefine HAVE_GETHOSTBYNAME 1
 
@@ -259,9 +253,6 @@
 /* Define to 1 if you have the `getrlimit' function. */
 #cmakedefine HAVE_GETRLIMIT 1
 
-/* Define to 1 if you have the getservbyport_r function. */
-#cmakedefine HAVE_GETSERVBYPORT_R 1
-
 /* Define to 1 if you have the `gettimeofday' function. */
 #cmakedefine HAVE_GETTIMEOFDAY 1
 
@@ -395,21 +386,6 @@
 /* Define to 1 if you have the `ssh2' library (-lssh2). */
 #cmakedefine HAVE_LIBSSH2 1
 
-/* Define to 1 if libssh2 provides `libssh2_version'. */
-#cmakedefine HAVE_LIBSSH2_VERSION 1
-
-/* Define to 1 if libssh2 provides `libssh2_init'. */
-#cmakedefine HAVE_LIBSSH2_INIT 1
-
-/* Define to 1 if libssh2 provides `libssh2_exit'. */
-#cmakedefine HAVE_LIBSSH2_EXIT 1
-
-/* Define to 1 if libssh2 provides `libssh2_scp_send64'. */
-#cmakedefine HAVE_LIBSSH2_SCP_SEND64 1
-
-/* Define to 1 if libssh2 provides `libssh2_session_handshake'. */
-#cmakedefine HAVE_LIBSSH2_SESSION_HANDSHAKE 1
-
 /* Define to 1 if you have the <libssh2.h> header file. */
 #cmakedefine HAVE_LIBSSH2_H 1
 
@@ -527,9 +503,6 @@
 /* Define to 1 if you have the recvfrom function. */
 #cmakedefine HAVE_RECVFROM 1
 
-/* Define to 1 if you have the <rsa.h> header file. */
-#cmakedefine HAVE_RSA_H 1
-
 /* Define to 1 if you have the select function. */
 #cmakedefine HAVE_SELECT 1
 
@@ -563,9 +536,6 @@
 /* Define to 1 if you have a working setsockopt SO_NONBLOCK function. */
 #cmakedefine HAVE_SETSOCKOPT_SO_NONBLOCK 1
 
-/* Define to 1 if you have the <sgtty.h> header file. */
-#cmakedefine HAVE_SGTTY_H 1
-
 /* Define to 1 if you have the sigaction function. */
 #cmakedefine HAVE_SIGACTION 1
 
@@ -581,12 +551,6 @@
 /* Define to 1 if you have the sigsetjmp function or macro. */
 #cmakedefine HAVE_SIGSETJMP 1
 
-/* Define to 1 if sig_atomic_t is an available typedef. */
-#cmakedefine HAVE_SIG_ATOMIC_T 1
-
-/* Define to 1 if sig_atomic_t is already defined as volatile. */
-#cmakedefine HAVE_SIG_ATOMIC_T_VOLATILE 1
-
 /* Define to 1 if struct sockaddr_in6 has the sin6_scope_id member */
 #cmakedefine HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1
 
@@ -991,9 +955,6 @@ ${SIZEOF_TIME_T_CODE}
 /* if Unix domain sockets are enabled  */
 #cmakedefine USE_UNIX_SOCKETS
 
-/* to disable alt-svc */
-#cmakedefine CURL_DISABLE_ALTSVC 1
-
 /* Define to 1 if you are building a Windows target with large file support. */
 #cmakedefine USE_WIN32_LARGE_FILES 1
 

+ 0 - 42
lib/curl_endian.c

@@ -80,45 +80,3 @@ unsigned short Curl_read16_be(const unsigned char *buf)
   return (unsigned short)(((unsigned short)buf[0] << 8) |
                           ((unsigned short)buf[1]));
 }
-
-#if (SIZEOF_CURL_OFF_T > 4)
-/*
- * write32_le()
- *
- * This function converts a 32-bit integer from the native endian format,
- * to little endian format ready for sending down the wire.
- *
- * Parameters:
- *
- * value    [in]     - The 32-bit integer value.
- * buffer   [in]     - A pointer to the output buffer.
- */
-static void write32_le(const int value, unsigned char *buffer)
-{
-  buffer[0] = (char)(value & 0x000000FF);
-  buffer[1] = (char)((value & 0x0000FF00) >> 8);
-  buffer[2] = (char)((value & 0x00FF0000) >> 16);
-  buffer[3] = (char)((value & 0xFF000000) >> 24);
-}
-
-/*
- * Curl_write64_le()
- *
- * This function converts a 64-bit integer from the native endian format,
- * to little endian format ready for sending down the wire.
- *
- * Parameters:
- *
- * value    [in]     - The 64-bit integer value.
- * buffer   [in]     - A pointer to the output buffer.
- */
-#if defined(HAVE_LONGLONG)
-void Curl_write64_le(const long long value, unsigned char *buffer)
-#else
-void Curl_write64_le(const __int64 value, unsigned char *buffer)
-#endif
-{
-  write32_le((int)value, buffer);
-  write32_le((int)(value >> 32), buffer + 4);
-}
-#endif /* SIZEOF_CURL_OFF_T > 4 */

+ 2 - 2
lib/curl_gssapi.c

@@ -59,7 +59,7 @@ OM_uint32 Curl_gss_init_sec_context(
     req_flags |= GSS_C_DELEG_POLICY_FLAG;
 #else
     infof(data, "warning: support for CURLGSSAPI_DELEGATION_POLICY_FLAG not "
-        "compiled in\n");
+        "compiled in");
 #endif
   }
 
@@ -130,7 +130,7 @@ void Curl_gss_log_error(struct Curl_easy *data, const char *prefix,
 
   display_gss_error(minor, GSS_C_MECH_CODE, buf, len);
 
-  infof(data, "%s%s\n", prefix, buf);
+  infof(data, "%s%s", prefix, buf);
 #ifdef CURL_DISABLE_VERBOSE_STRINGS
   (void)data;
   (void)prefix;

+ 28 - 22
lib/curl_multibyte.c

@@ -102,14 +102,16 @@ int curlx_win32_open(const char *filename, int oflag, ...)
   va_end(param);
 
 #ifdef _UNICODE
-  if(filename_w)
+  if(filename_w) {
     result = _wopen(filename_w, oflag, pmode);
-  free(filename_w);
-  if(result != -1)
-    return result;
-#endif
-
+    free(filename_w);
+  }
+  else
+    errno = EINVAL;
+  return result;
+#else
   return (_open)(filename, oflag, pmode);
+#endif
 }
 
 FILE *curlx_win32_fopen(const char *filename, const char *mode)
@@ -120,19 +122,20 @@ FILE *curlx_win32_fopen(const char *filename, const char *mode)
   wchar_t *mode_w = curlx_convert_UTF8_to_wchar(mode);
   if(filename_w && mode_w)
     result = _wfopen(filename_w, mode_w);
+  else
+    errno = EINVAL;
   free(filename_w);
   free(mode_w);
-  if(result)
-    return result;
-#endif
-
+  return result;
+#else
   return (fopen)(filename, mode);
+#endif
 }
 
 int curlx_win32_stat(const char *path, struct_stat *buffer)
 {
-  int result = -1;
 #ifdef _UNICODE
+  int result = -1;
   wchar_t *path_w = curlx_convert_UTF8_to_wchar(path);
   if(path_w) {
 #if defined(USE_WIN32_SMALL_FILES)
@@ -141,31 +144,34 @@ int curlx_win32_stat(const char *path, struct_stat *buffer)
     result = _wstati64(path_w, buffer);
 #endif
     free(path_w);
-    if(result != -1)
-      return result;
   }
-#endif /* _UNICODE */
-
+  else
+    errno = EINVAL;
+  return result;
+#else
 #if defined(USE_WIN32_SMALL_FILES)
-  result = _stat(path, buffer);
+  return _stat(path, buffer);
 #else
-  result = _stati64(path, buffer);
+  return _stati64(path, buffer);
+#endif
 #endif
-  return result;
 }
 
 int curlx_win32_access(const char *path, int mode)
 {
 #if defined(_UNICODE)
+  int result = -1;
   wchar_t *path_w = curlx_convert_UTF8_to_wchar(path);
   if(path_w) {
-    int result = _waccess(path_w, mode);
+    result = _waccess(path_w, mode);
     free(path_w);
-    if(result != -1)
-      return result;
   }
-#endif /* _UNICODE */
+  else
+    errno = EINVAL;
+  return result;
+#else
   return _access(path, mode);
+#endif
 }
 
 #endif /* USE_WIN32_LARGE_FILES || USE_WIN32_SMALL_FILES */

+ 6 - 10
lib/curl_ntlm_core.c

@@ -86,7 +86,6 @@
 #elif defined(USE_MBEDTLS)
 
 #  include <mbedtls/des.h>
-#  include "curl_md4.h"
 
 #elif defined(USE_SECTRANSP)
 
@@ -498,17 +497,14 @@ CURLcode Curl_ntlm_core_mk_nt_hash(struct Curl_easy *data,
    * network encoding not the host encoding.
    */
   result = Curl_convert_to_network(data, (char *)pw, len * 2);
-  if(result)
-    return result;
-
-  /* Create NT hashed password. */
-  Curl_md4it(ntbuffer, pw, 2 * len);
-
-  memset(ntbuffer + 16, 0, 21 - 16);
-
+  if(!result) {
+    /* Create NT hashed password. */
+    Curl_md4it(ntbuffer, pw, 2 * len);
+    memset(ntbuffer + 16, 0, 21 - 16);
+  }
   free(pw);
 
-  return CURLE_OK;
+  return result;
 }
 
 #if defined(USE_NTLM_V2) && !defined(USE_WINDOWS_SSPI)

+ 5 - 4
lib/curl_ntlm_wb.c

@@ -355,17 +355,17 @@ CURLcode Curl_input_ntlm_wb(struct Curl_easy *data,
   }
   else {
     if(*state == NTLMSTATE_LAST) {
-      infof(data, "NTLM auth restarted\n");
+      infof(data, "NTLM auth restarted");
       Curl_http_auth_cleanup_ntlm_wb(conn);
     }
     else if(*state == NTLMSTATE_TYPE3) {
-      infof(data, "NTLM handshake rejected\n");
+      infof(data, "NTLM handshake rejected");
       Curl_http_auth_cleanup_ntlm_wb(conn);
       *state = NTLMSTATE_NONE;
       return CURLE_REMOTE_ACCESS_DENIED;
     }
     else if(*state >= NTLMSTATE_TYPE1) {
-      infof(data, "NTLM handshake failure (internal error)\n");
+      infof(data, "NTLM handshake failure (internal error)");
       return CURLE_REMOTE_ACCESS_DENIED;
     }
 
@@ -426,7 +426,8 @@ CURLcode Curl_output_ntlm_wb(struct Curl_easy *data, struct connectdata *conn,
     /* Use Samba's 'winbind' daemon to support NTLM authentication,
      * by delegating the NTLM challenge/response protocol to a helper
      * in ntlm_auth.
-     * http://devel.squid-cache.org/ntlm/squid_helper_protocol.html
+     * https://web.archive.org/web/20190925164737
+     * /devel.squid-cache.org/ntlm/squid_helper_protocol.html
      * https://www.samba.org/samba/docs/man/manpages-3/winbindd.8.html
      * https://www.samba.org/samba/docs/man/manpages-3/ntlm_auth.1.html
      * Preprocessor symbol 'NTLM_WB_ENABLED' is defined when this

+ 4 - 4
lib/curl_range.c

@@ -53,14 +53,14 @@ CURLcode Curl_range(struct Curl_easy *data)
     if((to_t == CURL_OFFT_INVAL) && !from_t) {
       /* X - */
       data->state.resume_from = from;
-      DEBUGF(infof(data, "RANGE %" CURL_FORMAT_CURL_OFF_T " to end of file\n",
+      DEBUGF(infof(data, "RANGE %" CURL_FORMAT_CURL_OFF_T " to end of file",
                    from));
     }
     else if((from_t == CURL_OFFT_INVAL) && !to_t) {
       /* -Y */
       data->req.maxdownload = to;
       data->state.resume_from = -to;
-      DEBUGF(infof(data, "RANGE the last %" CURL_FORMAT_CURL_OFF_T " bytes\n",
+      DEBUGF(infof(data, "RANGE the last %" CURL_FORMAT_CURL_OFF_T " bytes",
                    to));
     }
     else {
@@ -78,12 +78,12 @@ CURLcode Curl_range(struct Curl_easy *data)
       data->req.maxdownload = totalsize + 1; /* include last byte */
       data->state.resume_from = from;
       DEBUGF(infof(data, "RANGE from %" CURL_FORMAT_CURL_OFF_T
-                   " getting %" CURL_FORMAT_CURL_OFF_T " bytes\n",
+                   " getting %" CURL_FORMAT_CURL_OFF_T " bytes",
                    from, data->req.maxdownload));
     }
     DEBUGF(infof(data, "range-download from %" CURL_FORMAT_CURL_OFF_T
                  " to %" CURL_FORMAT_CURL_OFF_T ", totally %"
-                 CURL_FORMAT_CURL_OFF_T " bytes\n",
+                 CURL_FORMAT_CURL_OFF_T " bytes",
                  from, to, data->req.maxdownload));
   }
   else

+ 7 - 3
lib/curl_sasl.c

@@ -234,7 +234,7 @@ static void state(struct SASL *sasl, struct Curl_easy *data,
   };
 
   if(sasl->state != newstate)
-    infof(data, "SASL %p state change from %s to %s\n",
+    infof(data, "SASL %p state change from %s to %s",
           (void *)sasl, names[sasl->state], names[newstate]);
 #else
   (void) data;
@@ -630,7 +630,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
       }
       else
         /* Decode the security challenge and create the response message */
-        result = Curl_auth_create_gssapi_security_message(data, &serverdata,
+        result = Curl_auth_create_gssapi_security_message(data,
+                                                          conn->sasl_authzid,
+                                                          &serverdata,
                                                           &conn->krb5,
                                                           &resp);
     }
@@ -639,7 +641,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
     /* Decode the security challenge and create the response message */
     result = get_server_message(sasl, data, &serverdata);
     if(!result)
-      result = Curl_auth_create_gssapi_security_message(data, &serverdata,
+      result = Curl_auth_create_gssapi_security_message(data,
+                                                        conn->sasl_authzid,
+                                                        &serverdata,
                                                         &conn->krb5,
                                                         &resp);
     break;

+ 57 - 34
lib/curl_setup.h

@@ -166,41 +166,47 @@
  */
 
 #ifdef HTTP_ONLY
-#  ifndef CURL_DISABLE_TFTP
-#    define CURL_DISABLE_TFTP
+#  ifndef CURL_DISABLE_DICT
+#    define CURL_DISABLE_DICT
+#  endif
+#  ifndef CURL_DISABLE_FILE
+#    define CURL_DISABLE_FILE
 #  endif
 #  ifndef CURL_DISABLE_FTP
 #    define CURL_DISABLE_FTP
 #  endif
+#  ifndef CURL_DISABLE_GOPHER
+#    define CURL_DISABLE_GOPHER
+#  endif
+#  ifndef CURL_DISABLE_IMAP
+#    define CURL_DISABLE_IMAP
+#  endif
 #  ifndef CURL_DISABLE_LDAP
 #    define CURL_DISABLE_LDAP
 #  endif
-#  ifndef CURL_DISABLE_TELNET
-#    define CURL_DISABLE_TELNET
+#  ifndef CURL_DISABLE_LDAPS
+#    define CURL_DISABLE_LDAPS
 #  endif
-#  ifndef CURL_DISABLE_DICT
-#    define CURL_DISABLE_DICT
+#  ifndef CURL_DISABLE_MQTT
+#    define CURL_DISABLE_MQTT
 #  endif
-#  ifndef CURL_DISABLE_FILE
-#    define CURL_DISABLE_FILE
+#  ifndef CURL_DISABLE_POP3
+#    define CURL_DISABLE_POP3
 #  endif
 #  ifndef CURL_DISABLE_RTSP
 #    define CURL_DISABLE_RTSP
 #  endif
-#  ifndef CURL_DISABLE_POP3
-#    define CURL_DISABLE_POP3
-#  endif
-#  ifndef CURL_DISABLE_IMAP
-#    define CURL_DISABLE_IMAP
+#  ifndef CURL_DISABLE_SMB
+#    define CURL_DISABLE_SMB
 #  endif
 #  ifndef CURL_DISABLE_SMTP
 #    define CURL_DISABLE_SMTP
 #  endif
-#  ifndef CURL_DISABLE_GOPHER
-#    define CURL_DISABLE_GOPHER
+#  ifndef CURL_DISABLE_TELNET
+#    define CURL_DISABLE_TELNET
 #  endif
-#  ifndef CURL_DISABLE_SMB
-#    define CURL_DISABLE_SMB
+#  ifndef CURL_DISABLE_TFTP
+#    define CURL_DISABLE_TFTP
 #  endif
 #endif
 
@@ -456,6 +462,15 @@
 #endif
 #endif
 
+#ifndef SSIZE_T_MAX
+/* some limits.h headers have this defined, some don't */
+#if defined(SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > 4)
+#define SSIZE_T_MAX 9223372036854775807
+#else
+#define SSIZE_T_MAX 2147483647
+#endif
+#endif
+
 /*
  * Arg 2 type for gethostname in case it hasn't been defined in config file.
  */
@@ -644,24 +659,16 @@ int netware_init(void);
 #endif
 
 /* Single point where USE_NTLM definition might be defined */
-#ifndef CURL_DISABLE_CRYPTO_AUTH
-#if defined(USE_OPENSSL) || defined(USE_MBEDTLS) ||                     \
-  defined(USE_GNUTLS) || defined(USE_NSS) || defined(USE_SECTRANSP) ||  \
-  defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) ||              \
-  (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT))
-
-#define USE_CURL_NTLM_CORE
-
-#  if defined(USE_MBEDTLS)
-/* Get definition of MBEDTLS_MD4_C */
-#  include <mbedtls/md4.h>
+#if !defined(CURL_DISABLE_CRYPTO_AUTH) && !defined(CURL_DISABLE_NTLM)
+#  if defined(USE_OPENSSL) || defined(USE_MBEDTLS) ||                       \
+      defined(USE_GNUTLS) || defined(USE_NSS) || defined(USE_SECTRANSP) ||  \
+      defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) ||              \
+      (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT))
+#    define USE_CURL_NTLM_CORE
+#  endif
+#  if defined(USE_CURL_NTLM_CORE) || defined(USE_WINDOWS_SSPI)
+#    define USE_NTLM
 #  endif
-
-#endif
-
-#if defined(USE_CURL_NTLM_CORE) || defined(USE_WINDOWS_SSPI)
-#define USE_NTLM
-#endif
 #endif
 
 #ifdef CURL_WANTS_CA_BUNDLE_ENV
@@ -819,4 +826,20 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
 #define ENABLE_QUIC
 #endif
 
+#if defined(USE_UNIX_SOCKETS) && defined(WIN32)
+#  if defined(__MINGW32__) && !defined(LUP_SECURE)
+     typedef u_short ADDRESS_FAMILY; /* Classic mingw, 11y+ old mingw-w64 */
+#  endif
+#  if !defined(UNIX_PATH_MAX)
+     /* Replicating logic present in afunix.h
+        (distributed with newer Windows 10 SDK versions only) */
+#    define UNIX_PATH_MAX 108
+     /* !checksrc! disable TYPEDEFSTRUCT 1 */
+     typedef struct sockaddr_un {
+       ADDRESS_FAMILY sun_family;
+       char sun_path[UNIX_PATH_MAX];
+     } SOCKADDR_UN, *PSOCKADDR_UN;
+#  endif
+#endif
+
 #endif /* HEADER_CURL_SETUP_H */

+ 0 - 20
lib/curl_setup_once.h

@@ -323,26 +323,6 @@ struct timeval {
 
 #include "curl_ctype.h"
 
-/*
- * Typedef to 'int' if sig_atomic_t is not an available 'typedefed' type.
- */
-
-#ifndef HAVE_SIG_ATOMIC_T
-typedef int sig_atomic_t;
-#define HAVE_SIG_ATOMIC_T
-#endif
-
-
-/*
- * Convenience SIG_ATOMIC_T definition
- */
-
-#ifdef HAVE_SIG_ATOMIC_T_VOLATILE
-#define SIG_ATOMIC_T static sig_atomic_t
-#else
-#define SIG_ATOMIC_T static volatile sig_atomic_t
-#endif
-
 
 /*
  * Macro used to include code only in debug builds.

+ 2 - 2
lib/dict.c

@@ -216,7 +216,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
     }
 
     if(!word || (*word == (char)0)) {
-      infof(data, "lookup word is missing\n");
+      infof(data, "lookup word is missing");
       word = (char *)"default";
     }
     if(!database || (*database == (char)0)) {
@@ -267,7 +267,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
     }
 
     if(!word || (*word == (char)0)) {
-      infof(data, "lookup word is missing\n");
+      infof(data, "lookup word is missing");
       word = (char *)"default";
     }
     if(!database || (*database == (char)0)) {

+ 25 - 25
lib/doh.c

@@ -186,19 +186,19 @@ doh_write_cb(const void *contents, size_t size, size_t nmemb, void *userp)
   return realsize;
 }
 
-/* called from multi.c when this DOH transfer is complete */
+/* called from multi.c when this DoH transfer is complete */
 static int doh_done(struct Curl_easy *doh, CURLcode result)
 {
   struct Curl_easy *data = doh->set.dohfor;
   struct dohdata *dohp = data->req.doh;
-  /* so one of the DOH request done for the 'data' transfer is now complete! */
+  /* so one of the DoH request done for the 'data' transfer is now complete! */
   dohp->pending--;
-  infof(data, "a DOH request is completed, %u to go\n", dohp->pending);
+  infof(data, "a DoH request is completed, %u to go", dohp->pending);
   if(result)
-    infof(data, "DOH request %s\n", curl_easy_strerror(result));
+    infof(data, "DoH request %s", curl_easy_strerror(result));
 
   if(!dohp->pending) {
-    /* DOH completed */
+    /* DoH completed */
     curl_slist_free_all(dohp->headers);
     dohp->headers = NULL;
     Curl_expire(data, 0, EXPIRE_RUN_NOW);
@@ -228,7 +228,7 @@ static CURLcode dohprobe(struct Curl_easy *data,
   DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer),
                          &p->dohlen);
   if(d) {
-    failf(data, "Failed to encode DOH packet [%d]", d);
+    failf(data, "Failed to encode DoH packet [%d]", d);
     return CURLE_OUT_OF_MEMORY;
   }
 
@@ -302,7 +302,7 @@ static CURLcode dohprobe(struct Curl_easy *data,
     /* Inherit *some* SSL options from the user's transfer. This is a
        best-guess as to which options are needed for compatibility. #3661
 
-       Note DOH does not inherit the user's proxy server so proxy SSL settings
+       Note DoH does not inherit the user's proxy server so proxy SSL settings
        have no effect and are not inherited. If that changes then two new
        options should be added to check doh proxy insecure separately,
        CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER.
@@ -359,17 +359,17 @@ static CURLcode dohprobe(struct Curl_easy *data,
         (data->set.ssl.auto_client_cert ?
          CURLSSLOPT_AUTO_CLIENT_CERT : 0);
 
-      curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask);
+      (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask);
     }
 
     doh->set.fmultidone = doh_done;
     doh->set.dohfor = data; /* identify for which transfer this is done */
     p->easy = doh;
 
-    /* DOH private_data must be null because the user must have a way to
-       distinguish their transfer's handle from DOH handles in user
+    /* DoH private_data must be null because the user must have a way to
+       distinguish their transfer's handle from DoH handles in user
        callbacks (ie SSL CTX callback). */
-    DEBUGASSERT(!data->set.private_data);
+    DEBUGASSERT(!doh->set.private_data);
 
     if(curl_multi_add_handle(multi, doh))
       goto error;
@@ -386,7 +386,7 @@ static CURLcode dohprobe(struct Curl_easy *data,
 }
 
 /*
- * Curl_doh() resolves a name using DOH. It resolves a name and returns a
+ * Curl_doh() resolves a name using DoH. It resolves a name and returns a
  * 'Curl_addrinfo *' with the address information.
  */
 
@@ -420,7 +420,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
   if(!dohp->headers)
     goto error;
 
-  /* create IPv4 DOH request */
+  /* create IPv4 DoH request */
   result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V4],
                     DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
                     data->multi, dohp->headers);
@@ -429,7 +429,7 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
   dohp->pending++;
 
   if(Curl_ipv6works(data)) {
-    /* create IPv6 DOH request */
+    /* create IPv6 DoH request */
     result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_IPADDR_V6],
                       DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
                       data->multi, dohp->headers);
@@ -764,11 +764,11 @@ static void showdoh(struct Curl_easy *data,
                     const struct dohentry *d)
 {
   int i;
-  infof(data, "TTL: %u seconds\n", d->ttl);
+  infof(data, "TTL: %u seconds", d->ttl);
   for(i = 0; i < d->numaddr; i++) {
     const struct dohaddr *a = &d->addr[i];
     if(a->type == DNS_TYPE_A) {
-      infof(data, "DOH A: %u.%u.%u.%u\n",
+      infof(data, "DoH A: %u.%u.%u.%u",
             a->ip.v4[0], a->ip.v4[1],
             a->ip.v4[2], a->ip.v4[3]);
     }
@@ -777,7 +777,7 @@ static void showdoh(struct Curl_easy *data,
       char buffer[128];
       char *ptr;
       size_t len;
-      msnprintf(buffer, 128, "DOH AAAA: ");
+      msnprintf(buffer, 128, "DoH AAAA: ");
       ptr = &buffer[10];
       len = 118;
       for(j = 0; j < 16; j += 2) {
@@ -788,11 +788,11 @@ static void showdoh(struct Curl_easy *data,
         len -= l;
         ptr += l;
       }
-      infof(data, "%s\n", buffer);
+      infof(data, "%s", buffer);
     }
   }
   for(i = 0; i < d->numcname; i++) {
-    infof(data, "CNAME: %s\n", Curl_dyn_ptr(&d->cname[i]));
+    infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i]));
   }
 }
 #else
@@ -803,7 +803,7 @@ static void showdoh(struct Curl_easy *data,
  * doh2ai()
  *
  * This function returns a pointer to the first element of a newly allocated
- * Curl_addrinfo struct linked list filled with the data from a set of DOH
+ * Curl_addrinfo struct linked list filled with the data from a set of DoH
  * lookups.  Curl_addrinfo is meant to work like the addrinfo struct does for
  * a IPv6 stack, but usable also for IPv4, all hosts and environments.
  *
@@ -931,7 +931,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
 
   if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
      !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
-    failf(data, "Could not DOH-resolve: %s", data->state.async.hostname);
+    failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
     return data->conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
       CURLE_COULDNT_RESOLVE_HOST;
   }
@@ -941,7 +941,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
     };
     struct dohentry de;
     int slot;
-    /* remove DOH handles from multi handle and close them */
+    /* remove DoH handles from multi handle and close them */
     for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
       curl_multi_remove_handle(data->multi, dohp->probe[slot].easy);
       Curl_close(&dohp->probe[slot].easy);
@@ -958,7 +958,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
                             &de);
       Curl_dyn_free(&p->serverdoh);
       if(rc[slot]) {
-        infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc[slot]),
+        infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
               type2name(p->dnstype), dohp->host);
       }
     } /* next slot */
@@ -969,7 +969,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
       struct Curl_dns_entry *dns;
       struct Curl_addrinfo *ai;
 
-      infof(data, "DOH Host name: %s\n", dohp->host);
+      infof(data, "DoH Host name: %s", dohp->host);
       showdoh(data, &de);
 
       ai = doh2ai(&de, dohp->host, dohp->port);
@@ -1007,7 +1007,7 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
 
   } /* !dohp->pending */
 
-  /* else wait for pending DOH transactions to complete */
+  /* else wait for pending DoH transactions to complete */
   return CURLE_OK;
 }
 

+ 1 - 1
lib/doh.h

@@ -101,7 +101,7 @@ void de_init(struct dohentry *d);
 void de_cleanup(struct dohentry *d);
 #endif
 
-#else /* if DOH is disabled */
+#else /* if DoH is disabled */
 #define Curl_doh(a,b,c,d) NULL
 #define Curl_doh_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
 #endif

+ 14 - 7
lib/easy.c

@@ -117,7 +117,7 @@ curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc;
 curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup;
 curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc;
 #if defined(WIN32) && defined(UNICODE)
-curl_wcsdup_callback Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup;
+curl_wcsdup_callback Curl_cwcsdup = Curl_wcsdup;
 #endif
 
 #if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__)
@@ -417,13 +417,13 @@ static int events_socket(struct Curl_easy *easy,      /* easy handle */
           ev->list = nxt;
         free(m);
         m = nxt;
-        infof(easy, "socket cb: socket %d REMOVED\n", s);
+        infof(easy, "socket cb: socket %d REMOVED", s);
       }
       else {
         /* The socket 's' is already being monitored, update the activity
            mask. Convert from libcurl bitmask to the poll one. */
         m->socket.events = socketcb2poll(what);
-        infof(easy, "socket cb: socket %d UPDATED as %s%s\n", s,
+        infof(easy, "socket cb: socket %d UPDATED as %s%s", s,
               (what&CURL_POLL_IN)?"IN":"",
               (what&CURL_POLL_OUT)?"OUT":"");
       }
@@ -447,7 +447,7 @@ static int events_socket(struct Curl_easy *easy,      /* easy handle */
         m->socket.events = socketcb2poll(what);
         m->socket.revents = 0;
         ev->list = m;
-        infof(easy, "socket cb: socket %d ADDED as %s%s\n", s,
+        infof(easy, "socket cb: socket %d ADDED as %s%s", s,
               (what&CURL_POLL_IN)?"IN":"",
               (what&CURL_POLL_OUT)?"OUT":"");
       }
@@ -532,7 +532,7 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
         if(fds[i].revents) {
           /* socket activity, tell libcurl */
           int act = poll2cselect(fds[i].revents); /* convert */
-          infof(multi->easyp, "call curl_multi_socket_action(socket %d)\n",
+          infof(multi->easyp, "call curl_multi_socket_action(socket %d)",
                 fds[i].fd);
           mcode = curl_multi_socket_action(multi, fds[i].fd, act,
                                            &ev->running_handles);
@@ -1027,7 +1027,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
 
   if((newstate & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) == oldstate) {
     /* Not changing any pause state, return */
-    DEBUGF(infof(data, "pause: no change, early return\n"));
+    DEBUGF(infof(data, "pause: no change, early return"));
     return CURLE_OK;
   }
 
@@ -1213,9 +1213,16 @@ static int conn_upkeep(struct Curl_easy *data,
   /* Param is unused. */
   (void)param;
 
-  if(conn->handler->connection_check)
+  if(conn->handler->connection_check) {
+    /* briefly attach the connection to this transfer for the purpose of
+       checking it */
+    Curl_attach_connnection(data, conn);
+
     /* Do a protocol-specific keepalive check on the connection. */
     conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE);
+    /* detach the connection again */
+    Curl_detach_connnection(data);
+  }
 
   return 0; /* continue iteration */
 }

+ 11 - 5
lib/formdata.c

@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2021, 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
@@ -865,8 +865,6 @@ CURLcode Curl_getformdata(struct Curl_easy *data,
 
         if(post->flags & CURL_HTTPPOST_LARGE)
           clen = post->contentlen;
-        if(!clen)
-          clen = -1;
 
         if(post->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE)) {
           if(!strcmp(file->contents, "-")) {
@@ -888,13 +886,21 @@ CURLcode Curl_getformdata(struct Curl_easy *data,
         else if(post->flags & HTTPPOST_BUFFER)
           result = curl_mime_data(part, post->buffer,
                                   post->bufferlength? post->bufferlength: -1);
-        else if(post->flags & HTTPPOST_CALLBACK)
+        else if(post->flags & HTTPPOST_CALLBACK) {
           /* the contents should be read with the callback and the size is set
              with the contentslength */
+          if(!clen)
+            clen = -1;
           result = curl_mime_data_cb(part, clen,
                                      fread_func, NULL, NULL, post->userp);
+        }
         else {
-          result = curl_mime_data(part, post->contents, (ssize_t) clen);
+          size_t uclen;
+          if(!clen)
+            uclen = CURL_ZERO_TERMINATED;
+          else
+            uclen = (size_t)clen;
+          result = curl_mime_data(part, post->contents, uclen);
 #ifdef CURL_DOES_CONVERSIONS
           /* Convert textual contents now. */
           if(!result && data && part->datasize)

+ 66 - 55
lib/ftp.c

@@ -289,7 +289,7 @@ static CURLcode AcceptServerConnect(struct Curl_easy *data)
     failf(data, "Error accept()ing server connect");
     return CURLE_FTP_PORT_FAILED;
   }
-  infof(data, "Connection accepted from server\n");
+  infof(data, "Connection accepted from server");
   /* when this happens within the DO state it is important that we mark us as
      not needing DO_MORE anymore */
   conn->bits.do_more = FALSE;
@@ -380,7 +380,7 @@ static CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received)
   *received = FALSE;
 
   timeout_ms = ftp_timeleft_accept(data);
-  infof(data, "Checking for server connect\n");
+  infof(data, "Checking for server connect");
   if(timeout_ms < 0) {
     /* if a timeout was already reached, bail out */
     failf(data, "Accept timeout occurred while waiting server connect");
@@ -390,7 +390,7 @@ static CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received)
   /* First check whether there is a cached response from server */
   if(pp->cache_size && pp->cache && pp->cache[0] > '3') {
     /* Data connection could not be established, let's return */
-    infof(data, "There is negative response in cache while serv connect\n");
+    infof(data, "There is negative response in cache while serv connect");
     (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
     return CURLE_FTP_ACCEPT_FAILED;
   }
@@ -408,11 +408,11 @@ static CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received)
   default:
 
     if(result & CURL_CSELECT_IN2) {
-      infof(data, "Ready to accept data connection from server\n");
+      infof(data, "Ready to accept data connection from server");
       *received = TRUE;
     }
     else if(result & CURL_CSELECT_IN) {
-      infof(data, "Ctrl conn has data while waiting for data conn\n");
+      infof(data, "Ctrl conn has data while waiting for data conn");
       (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
 
       if(ftpcode/100 > 3)
@@ -444,7 +444,7 @@ static CURLcode InitiateTransfer(struct Curl_easy *data)
   if(conn->bits.ftp_use_data_ssl) {
     /* since we only have a plaintext TCP connection here, we must now
      * do the TLS stuff */
-    infof(data, "Doing the SSL/TLS handshake on the data stream\n");
+    infof(data, "Doing the SSL/TLS handshake on the data stream");
     result = Curl_ssl_connect(data, conn, SECONDARYSOCKET);
     if(result)
       return result;
@@ -487,7 +487,7 @@ static CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected)
   CURLcode result = CURLE_OK;
 
   *connected = FALSE;
-  infof(data, "Preparing for accepting server on data port\n");
+  infof(data, "Preparing for accepting server on data port");
 
   /* Save the time we start accepting server connect */
   Curl_pgrsTime(data, TIMER_STARTACCEPT);
@@ -592,7 +592,7 @@ static CURLcode ftp_readresp(struct Curl_easy *data,
      * This response code can come at any point so having it treated
      * generically is a good idea.
      */
-    infof(data, "We got a 421 - timeout!\n");
+    infof(data, "We got a 421 - timeout!");
     state(data, FTP_STOP);
     return CURLE_OPERATION_TIMEDOUT;
   }
@@ -768,7 +768,7 @@ static void _state(struct Curl_easy *data,
   (void) lineno;
 #else
   if(ftpc->state != newstate)
-    infof(data, "FTP %p (line %d) state change from %s to %s\n",
+    infof(data, "FTP %p (line %d) state change from %s to %s",
           (void *)ftpc, lineno, ftp_state_names[ftpc->state],
           ftp_state_names[newstate]);
 #endif
@@ -1139,7 +1139,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
         /* The requested bind address is not local.  Use the address used for
          * the control connection instead and restart the port loop
          */
-        infof(data, "bind(port=%hu) on non-local address failed: %s\n", port,
+        infof(data, "bind(port=%hu) on non-local address failed: %s", port,
               Curl_strerror(error, buffer, sizeof(buffer)));
 
         sslen = sizeof(ss);
@@ -1341,7 +1341,7 @@ static CURLcode ftp_state_use_pasv(struct Curl_easy *data,
   if(!result) {
     ftpc->count1 = modeoff;
     state(data, FTP_PASV);
-    infof(data, "Connect data stream passively\n");
+    infof(data, "Connect data stream passively");
   }
   return result;
 }
@@ -1648,7 +1648,7 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
       data->state.infilesize -= data->state.resume_from;
 
       if(data->state.infilesize <= 0) {
-        infof(data, "File already completely uploaded\n");
+        infof(data, "File already completely uploaded");
 
         /* no data to transfer */
         Curl_setup_transfer(data, -1, -1, FALSE, -1);
@@ -1802,7 +1802,7 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data,
     return CURLE_WEIRD_SERVER_REPLY;
   }
 
-  infof(data, "Failed EPSV attempt. Disabling EPSV\n");
+  infof(data, "Failed EPSV attempt. Disabling EPSV");
   /* disable it for next transfer */
   conn->bits.ftp_use_epsv = FALSE;
   data->state.errorbuf = FALSE; /* allow error message to get
@@ -1921,7 +1921,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
     if(data->set.ftp_skip_ip) {
       /* told to ignore the remotely given IP but instead use the host we used
          for the control connection */
-      infof(data, "Skip %u.%u.%u.%u for data connection, re-use %s instead\n",
+      infof(data, "Skip %u.%u.%u.%u for data connection, re-use %s instead",
             ip[0], ip[1], ip[2], ip[3],
             conn->host.name);
       ftpc->newhost = strdup(control_address(conn));
@@ -2044,7 +2044,7 @@ static CURLcode ftp_state_port_resp(struct Curl_easy *data,
     /* the command failed */
 
     if(EPRT == fcmd) {
-      infof(data, "disabling EPRT usage\n");
+      infof(data, "disabling EPRT usage");
       conn->bits.ftp_use_eprt = FALSE;
     }
     fcmd++;
@@ -2058,7 +2058,7 @@ static CURLcode ftp_state_port_resp(struct Curl_easy *data,
       result = ftp_state_use_port(data, fcmd);
   }
   else {
-    infof(data, "Connect data stream actively\n");
+    infof(data, "Connect data stream actively");
     state(data, FTP_STOP); /* end of DO phase */
     result = ftp_dophase_done(data, FALSE);
   }
@@ -2128,7 +2128,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
     }
     break;
   default:
-    infof(data, "unsupported MDTM reply format\n");
+    infof(data, "unsupported MDTM reply format");
     break;
   case 550: /* "No such file or directory" */
     failf(data, "Given file does not exist");
@@ -2142,7 +2142,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
       case CURL_TIMECOND_IFMODSINCE:
       default:
         if(data->info.filetime <= data->set.timevalue) {
-          infof(data, "The requested document is not new enough\n");
+          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);
@@ -2151,7 +2151,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
         break;
       case CURL_TIMECOND_IFUNMODSINCE:
         if(data->info.filetime > data->set.timevalue) {
-          infof(data, "The requested document is not old enough\n");
+          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);
@@ -2161,7 +2161,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
       } /* switch */
     }
     else {
-      infof(data, "Skipping time comparison\n");
+      infof(data, "Skipping time comparison");
     }
   }
 
@@ -2186,7 +2186,7 @@ static CURLcode ftp_state_type_resp(struct Curl_easy *data,
     return CURLE_FTP_COULDNT_SET_TYPE;
   }
   if(ftpcode != 200)
-    infof(data, "Got a %03d response code instead of the assumed 200\n",
+    infof(data, "Got a %03d response code instead of the assumed 200",
           ftpcode);
 
   if(instate == FTP_TYPE)
@@ -2219,7 +2219,7 @@ static CURLcode ftp_state_retr(struct Curl_easy *data,
     /* We always (attempt to) get the size of downloads, so it is done before
        this even when not doing resumes. */
     if(filesize == -1) {
-      infof(data, "ftp server doesn't support SIZE\n");
+      infof(data, "ftp server doesn't support SIZE");
       /* We couldn't get the size and therefore we can't know if there really
          is a part of the file left to get, although the server will just
          close the connection when we start the connection so it won't cause
@@ -2256,7 +2256,7 @@ static CURLcode ftp_state_retr(struct Curl_easy *data,
     if(ftp->downloadsize == 0) {
       /* no data to transfer */
       Curl_setup_transfer(data, -1, -1, FALSE, -1);
-      infof(data, "File already completely downloaded\n");
+      infof(data, "File already completely downloaded");
 
       /* Set ->transfer so that we won't get any error in ftp_done()
        * because we didn't transfer the any file */
@@ -2267,7 +2267,7 @@ static CURLcode ftp_state_retr(struct Curl_easy *data,
 
     /* Set resume file transfer offset */
     infof(data, "Instructs server to resume from offset %"
-          CURL_FORMAT_CURL_OFF_T "\n", data->state.resume_from);
+          CURL_FORMAT_CURL_OFF_T, data->state.resume_from);
 
     result = Curl_pp_sendf(data, &ftpc->pp, "REST %" CURL_FORMAT_CURL_OFF_T,
                            data->state.resume_from);
@@ -2413,7 +2413,7 @@ static CURLcode ftp_state_stor_resp(struct Curl_easy *data,
 
     if(!connected) {
       struct ftp_conn *ftpc = &conn->proto.ftpc;
-      infof(data, "Data conn was not available immediately\n");
+      infof(data, "Data conn was not available immediately");
       ftpc->wait_data_conn = TRUE;
     }
 
@@ -2506,11 +2506,11 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
     else if((instate != FTP_LIST) && (data->state.prefer_ascii))
       size = -1; /* kludge for servers that understate ASCII mode file size */
 
-    infof(data, "Maxdownload = %" CURL_FORMAT_CURL_OFF_T "\n",
+    infof(data, "Maxdownload = %" CURL_FORMAT_CURL_OFF_T,
           data->req.maxdownload);
 
     if(instate != FTP_LIST)
-      infof(data, "Getting file with size: %" CURL_FORMAT_CURL_OFF_T "\n",
+      infof(data, "Getting file with size: %" CURL_FORMAT_CURL_OFF_T,
             size);
 
     /* FTP download: */
@@ -2526,7 +2526,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\n");
+        infof(data, "Data conn was not available immediately");
         state(data, FTP_STOP);
         ftpc->wait_data_conn = TRUE;
       }
@@ -2681,9 +2681,12 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
     /* we have now received a full FTP server response */
     switch(ftpc->state) {
     case FTP_WAIT220:
-      if(ftpcode == 230)
-        /* 230 User logged in - already! */
-        return ftp_state_user_resp(data, ftpcode, ftpc->state);
+      if(ftpcode == 230) {
+        /* 230 User logged in - already! Take as 220 if TLS required. */
+        if(data->set.use_ssl <= CURLUSESSL_TRY ||
+           conn->bits.ftp_use_control_ssl)
+          return ftp_state_user_resp(data, ftpcode, ftpc->state);
+      }
       else if(ftpcode != 220) {
         failf(data, "Got a %03d ftp-server response when 220 was expected",
               ftpcode);
@@ -2702,9 +2705,9 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
         Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
 
         if(Curl_sec_login(data, conn))
-          infof(data, "Logging in with password in cleartext!\n");
+          infof(data, "Logging in with password in cleartext!");
         else
-          infof(data, "Authentication successful\n");
+          infof(data, "Authentication successful");
       }
 #endif
 
@@ -2740,6 +2743,9 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
     case FTP_AUTH:
       /* we have gotten the response to a previous AUTH command */
 
+      if(pp->cache_size)
+        return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */
+
       /* RFC2228 (page 5) says:
        *
        * If the server is willing to accept the named security mechanism,
@@ -2895,7 +2901,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
             }
             Curl_safefree(ftpc->entrypath);
             ftpc->entrypath = dir; /* remember this */
-            infof(data, "Entry path is '%s'\n", ftpc->entrypath);
+            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);
@@ -2904,18 +2910,18 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
 
           Curl_safefree(ftpc->entrypath);
           ftpc->entrypath = dir; /* remember this */
-          infof(data, "Entry path is '%s'\n", ftpc->entrypath);
+          infof(data, "Entry path is '%s'", ftpc->entrypath);
           /* also save it where getinfo can access it: */
           data->state.most_recent_ftp_entrypath = ftpc->entrypath;
         }
         else {
           /* couldn't get the path */
           free(dir);
-          infof(data, "Failed to figure out path\n");
+          infof(data, "Failed to figure out path");
         }
       }
       state(data, FTP_STOP); /* we are done with the CONNECT phase! */
-      DEBUGF(infof(data, "protocol connect phase DONE\n"));
+      DEBUGF(infof(data, "protocol connect phase DONE"));
       break;
 
     case FTP_SYST:
@@ -2962,7 +2968,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
       }
 
       state(data, FTP_STOP); /* we are done with the CONNECT phase! */
-      DEBUGF(infof(data, "protocol connect phase DONE\n"));
+      DEBUGF(infof(data, "protocol connect phase DONE"));
       break;
 
     case FTP_NAMEFMT:
@@ -2973,7 +2979,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
       }
 
       state(data, FTP_STOP); /* we are done with the CONNECT phase! */
-      DEBUGF(infof(data, "protocol connect phase DONE\n"));
+      DEBUGF(infof(data, "protocol connect phase DONE"));
       break;
 
     case FTP_QUOTE:
@@ -3272,7 +3278,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
     }
 
     if(ftpc->prevpath)
-      infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath);
+      infof(data, "Remembering we are in dir \"%s\"", ftpc->prevpath);
   }
 
   /* free the dir tree and file parts */
@@ -3338,7 +3344,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
     if(ftpc->dont_check && data->req.maxdownload > 0) {
       /* we have just sent ABOR and there is no reliable way to check if it was
        * successful or not; we have to close the connection now */
-      infof(data, "partial download completed, closing connection\n");
+      infof(data, "partial download completed, closing connection");
       connclose(conn, "Partial download with no ability to check");
       return result;
     }
@@ -3529,7 +3535,7 @@ ftp_pasv_verbose(struct Curl_easy *data,
 {
   char buf[256];
   Curl_printable_address(ai, buf, sizeof(buf));
-  infof(data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
+  infof(data, "Connecting to %s (%s) port %d", newhost, buf, port);
 }
 #endif
 
@@ -3569,7 +3575,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
 
     /* Ready to do more? */
     if(connected) {
-      DEBUGF(infof(data, "DO-MORE connected phase starts\n"));
+      DEBUGF(infof(data, "DO-MORE connected phase starts"));
     }
     else {
       if(result && (ftpc->count1 == 0)) {
@@ -3644,8 +3650,13 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
         return result;
 
       result = ftp_multi_statemach(data, &complete);
-      /* ftpc->wait_data_conn is always false here */
-      *completep = (int)complete;
+      if(ftpc->wait_data_conn)
+        /* if we reach the end of the FTP state machine here, *complete will be
+           TRUE but so is ftpc->wait_data_conn, which says we need to wait for
+           the data connection and therefore we're not actually complete */
+        *completep = 0;
+      else
+        *completep = (int)complete;
     }
     else {
       /* download */
@@ -3692,7 +3703,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
   if(!ftpc->wait_data_conn) {
     /* no waiting for the data connection so this is now complete */
     *completep = 1;
-    DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result));
+    DEBUGF(infof(data, "DO-MORE phase ends with %d", (int)result));
   }
 
   return result;
@@ -3717,7 +3728,7 @@ CURLcode ftp_perform(struct Curl_easy *data,
   CURLcode result = CURLE_OK;
   struct connectdata *conn = data->conn;
 
-  DEBUGF(infof(data, "DO phase starts\n"));
+  DEBUGF(infof(data, "DO phase starts"));
 
   if(data->set.opt_no_body) {
     /* requested no body means no transfer... */
@@ -3737,10 +3748,10 @@ CURLcode ftp_perform(struct Curl_easy *data,
 
   *connected = conn->bits.tcpconnect[SECONDARYSOCKET];
 
-  infof(data, "ftp_perform ends with SECONDARY: %d\n", *connected);
+  infof(data, "ftp_perform ends with SECONDARY: %d", *connected);
 
   if(*dophase_done)
-    DEBUGF(infof(data, "DO phase is complete1\n"));
+    DEBUGF(infof(data, "DO phase is complete1"));
 
   return result;
 }
@@ -3834,7 +3845,7 @@ static CURLcode init_wc_data(struct Curl_easy *data)
   /* let the writefunc callback know the transfer */
   data->set.out = data;
 
-  infof(data, "Wildcard - Parsing started\n");
+  infof(data, "Wildcard - Parsing started");
   return CURLE_OK;
 
   fail:
@@ -3901,7 +3912,7 @@ static CURLcode wc_statemach(struct Curl_easy *data)
       free(ftp->pathalloc);
       ftp->pathalloc = ftp->path = tmp_path;
 
-      infof(data, "Wildcard - START of \"%s\"\n", finfo->filename);
+      infof(data, "Wildcard - START of \"%s\"", finfo->filename);
       if(data->set.chunk_bgn) {
         long userresponse;
         Curl_set_in_callback(data, true);
@@ -3910,7 +3921,7 @@ static CURLcode wc_statemach(struct Curl_easy *data)
         Curl_set_in_callback(data, false);
         switch(userresponse) {
         case CURL_CHUNK_BGN_FUNC_SKIP:
-          infof(data, "Wildcard - \"%s\" skipped by user\n",
+          infof(data, "Wildcard - \"%s\" skipped by user",
                 finfo->filename);
           wildcard->state = CURLWC_SKIP;
           continue;
@@ -4233,7 +4244,7 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data)
         n -= ftpc->file?strlen(ftpc->file):0;
 
       if((strlen(oldPath) == n) && !strncmp(rawPath, oldPath, n)) {
-        infof(data, "Request has same path as previous transfer\n");
+        infof(data, "Request has same path as previous transfer");
         ftpc->cwddone = TRUE;
       }
     }
@@ -4279,11 +4290,11 @@ static CURLcode ftp_doing(struct Curl_easy *data,
   CURLcode result = ftp_multi_statemach(data, dophase_done);
 
   if(result)
-    DEBUGF(infof(data, "DO phase failed\n"));
+    DEBUGF(infof(data, "DO phase failed"));
   else if(*dophase_done) {
     result = ftp_dophase_done(data, FALSE /* not connected */);
 
-    DEBUGF(infof(data, "DO phase is complete2\n"));
+    DEBUGF(infof(data, "DO phase is complete2"));
   }
   return result;
 }

+ 0 - 1
lib/hostasyn.c

@@ -50,7 +50,6 @@
 #include "hostip.h"
 #include "hash.h"
 #include "share.h"
-#include "strerror.h"
 #include "url.h"
 #include "curl_memory.h"
 /* The last #include file should be: */

+ 2 - 10
lib/hostcheck.c

@@ -36,7 +36,7 @@
 
 #include "hostcheck.h"
 #include "strcase.h"
-#include "inet_pton.h"
+#include "hostip.h"
 
 #include "curl_memory.h"
 /* The last #include file should be: */
@@ -67,10 +67,6 @@ static int hostmatch(char *hostname, char *pattern)
   const char *pattern_label_end, *pattern_wildcard, *hostname_label_end;
   int wildcard_enabled;
   size_t prefixlen, suffixlen;
-  struct in_addr ignored;
-#ifdef ENABLE_IPV6
-  struct sockaddr_in6 si6;
-#endif
 
   /* normalize pattern and hostname by stripping off trailing dots */
   size_t len = strlen(hostname);
@@ -86,12 +82,8 @@ static int hostmatch(char *hostname, char *pattern)
       CURL_HOST_MATCH : CURL_HOST_NOMATCH;
 
   /* detect IP address as hostname and fail the match if so */
-  if(Curl_inet_pton(AF_INET, hostname, &ignored) > 0)
-    return CURL_HOST_NOMATCH;
-#ifdef ENABLE_IPV6
-  if(Curl_inet_pton(AF_INET6, hostname, &si6.sin6_addr) > 0)
+  if(Curl_host_is_ipnum(hostname))
     return CURL_HOST_NOMATCH;
-#endif
 
   /* We require at least 2 dots in pattern to avoid too wide wildcard
      match. */

+ 156 - 29
lib/hostip.c

@@ -56,13 +56,13 @@
 #include "hash.h"
 #include "rand.h"
 #include "share.h"
-#include "strerror.h"
 #include "url.h"
 #include "inet_ntop.h"
 #include "inet_pton.h"
 #include "multiif.h"
 #include "doh.h"
 #include "warnless.h"
+#include "strcase.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -289,7 +289,7 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
     user.cache_timeout = data->set.dns_cache_timeout;
 
     if(hostcache_timestamp_remove(&user, dns)) {
-      infof(data, "Hostname in DNS cache was stale, zapped\n");
+      infof(data, "Hostname in DNS cache was stale, zapped");
       dns = NULL; /* the memory deallocation is being handled by the hash */
       Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
     }
@@ -460,6 +460,127 @@ Curl_cache_addr(struct Curl_easy *data,
   return dns;
 }
 
+#ifdef ENABLE_IPV6
+/* return a static IPv6 resolve for 'localhost' */
+static struct Curl_addrinfo *get_localhost6(int port)
+{
+  struct Curl_addrinfo *ca;
+  const size_t ss_size = sizeof(struct sockaddr_in6);
+  const size_t hostlen = strlen("localhost");
+  struct sockaddr_in6 sa6;
+  unsigned char ipv6[16];
+  unsigned short port16 = (unsigned short)(port & 0xffff);
+  ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1);
+  if(!ca)
+    return NULL;
+
+  sa6.sin6_family = AF_INET6;
+  sa6.sin6_port = htons(port16);
+  sa6.sin6_flowinfo = 0;
+  sa6.sin6_scope_id = 0;
+  if(Curl_inet_pton(AF_INET6, "::1", ipv6) < 1)
+    return NULL;
+  memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6));
+
+  ca->ai_flags     = 0;
+  ca->ai_family    = AF_INET6;
+  ca->ai_socktype  = SOCK_STREAM;
+  ca->ai_protocol  = IPPROTO_TCP;
+  ca->ai_addrlen   = (curl_socklen_t)ss_size;
+  ca->ai_next      = NULL;
+  ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
+  memcpy(ca->ai_addr, &sa6, ss_size);
+  ca->ai_canonname = (char *)ca->ai_addr + ss_size;
+  strcpy(ca->ai_canonname, "localhost");
+  return ca;
+}
+#else
+#define get_localhost6(x) NULL
+#endif
+
+/* return a static IPv4 resolve for 'localhost' */
+static struct Curl_addrinfo *get_localhost(int port)
+{
+  struct Curl_addrinfo *ca;
+  const size_t ss_size = sizeof(struct sockaddr_in);
+  const size_t hostlen = strlen("localhost");
+  struct sockaddr_in sa;
+  unsigned int ipv4;
+  unsigned short port16 = (unsigned short)(port & 0xffff);
+  ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1);
+  if(!ca)
+    return NULL;
+
+  /* memset to clear the sa.sin_zero field */
+  memset(&sa, 0, sizeof(sa));
+  sa.sin_family = AF_INET;
+  sa.sin_port = htons(port16);
+  if(Curl_inet_pton(AF_INET, "127.0.0.1", (char *)&ipv4) < 1)
+    return NULL;
+  memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4));
+
+  ca->ai_flags     = 0;
+  ca->ai_family    = AF_INET;
+  ca->ai_socktype  = SOCK_STREAM;
+  ca->ai_protocol  = IPPROTO_TCP;
+  ca->ai_addrlen   = (curl_socklen_t)ss_size;
+  ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
+  memcpy(ca->ai_addr, &sa, ss_size);
+  ca->ai_canonname = (char *)ca->ai_addr + ss_size;
+  strcpy(ca->ai_canonname, "localhost");
+  ca->ai_next = get_localhost6(port);
+  return ca;
+}
+
+#ifdef ENABLE_IPV6
+/*
+ * Curl_ipv6works() returns TRUE if IPv6 seems to work.
+ */
+bool Curl_ipv6works(struct Curl_easy *data)
+{
+  if(data) {
+    /* the nature of most system is that IPv6 status doesn't come and go
+       during a program's lifetime so we only probe the first time and then we
+       have the info kept for fast re-use */
+    DEBUGASSERT(data);
+    DEBUGASSERT(data->multi);
+    return data->multi->ipv6_works;
+  }
+  else {
+    int ipv6_works = -1;
+    /* probe to see if we have a working IPv6 stack */
+    curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
+    if(s == CURL_SOCKET_BAD)
+      /* an IPv6 address was requested but we can't get/use one */
+      ipv6_works = 0;
+    else {
+      ipv6_works = 1;
+      sclose(s);
+    }
+    return (ipv6_works>0)?TRUE:FALSE;
+  }
+}
+#endif /* ENABLE_IPV6 */
+
+/*
+ * Curl_host_is_ipnum() returns TRUE if the given string is a numerical IPv4
+ * (or IPv6 if supported) address.
+ */
+bool Curl_host_is_ipnum(const char *hostname)
+{
+  struct in_addr in;
+#ifdef ENABLE_IPV6
+  struct in6_addr in6;
+#endif
+  if(Curl_inet_pton(AF_INET, hostname, &in) > 0
+#ifdef ENABLE_IPV6
+     || Curl_inet_pton(AF_INET6, hostname, &in6) > 0
+#endif
+    )
+    return TRUE;
+  return FALSE;
+}
+
 /*
  * Curl_resolv() is the main name resolve function within libcurl. It resolves
  * a name and returns a pointer to the entry in the 'entry' argument (if one
@@ -487,7 +608,6 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
   CURLcode result;
   enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */
   struct connectdata *conn = data->conn;
-
   *entry = NULL;
   conn->bits.doh = FALSE; /* default is not */
 
@@ -497,7 +617,7 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
   dns = fetch_addr(data, hostname, port);
 
   if(dns) {
-    infof(data, "Hostname %s was found in DNS cache\n", hostname);
+    infof(data, "Hostname %s was found in DNS cache", hostname);
     dns->inuse++; /* we use it! */
     rc = CURLRESOLV_RESOLVED;
   }
@@ -534,16 +654,20 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
     }
 
 #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.
-     */
-    SCDynamicStoreCopyProxies(NULL);
+    {
+      /*
+       * 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
@@ -579,15 +703,18 @@ enum resolve_t Curl_resolv(struct Curl_easy *data,
 #endif /* !USE_RESOLVE_ON_IPS */
 
     if(!addr) {
-      /* Check what IP specifics the app has requested and if we can provide
-       * it. If not, bail out. */
-      if(!Curl_ipvalid(data, conn))
+      if(conn->ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
         return CURLRESOLV_ERROR;
 
-      if(allowDOH && data->set.doh && !ipnum) {
+      if(strcasecompare(hostname, "localhost"))
+        addr = get_localhost(port);
+      else if(allowDOH && data->set.doh && !ipnum)
         addr = Curl_doh(data, hostname, port, &respwait);
-      }
       else {
+        /* Check what IP specifics the app has requested and if we can provide
+         * it. If not, bail out. */
+        if(!Curl_ipvalid(data, conn))
+          return CURLRESOLV_ERROR;
         /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
            non-zero value indicating that we need to wait for the response to
            the resolve call */
@@ -757,7 +884,7 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
 #else
 #ifndef CURLRES_ASYNCH
   if(timeoutms)
-    infof(data, "timeout on name lookup is not supported\n");
+    infof(data, "timeout on name lookup is not supported");
 #else
   (void)timeoutms; /* timeoutms not used with an async resolver */
 #endif
@@ -895,7 +1022,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
       size_t entry_len;
 
       if(2 != sscanf(hostp->data + 1, "%255[^:]:%d", hostname, &port)) {
-        infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'!\n",
+        infof(data, "Couldn't parse CURLOPT_RESOLVE removal entry '%s'",
               hostp->data);
         continue;
       }
@@ -984,7 +1111,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
 
 #ifndef ENABLE_IPV6
         if(strchr(address, ':')) {
-          infof(data, "Ignoring resolve address '%s', missing IPv6 support.\n",
+          infof(data, "Ignoring resolve address '%s', missing IPv6 support.",
                 address);
           continue;
         }
@@ -992,7 +1119,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
 
         ai = Curl_str2addr(address, port);
         if(!ai) {
-          infof(data, "Resolve address '%s' found illegal!\n", address);
+          infof(data, "Resolve address '%s' found illegal!", address);
           goto err;
         }
 
@@ -1011,10 +1138,10 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
       error = false;
    err:
       if(error) {
-        infof(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!\n",
+        failf(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'!",
               hostp->data);
         Curl_freeaddrinfo(head);
-        continue;
+        return CURLE_SETOPT_OPTION_SYNTAX;
       }
 
       /* Create an entry id, based upon the hostname and port */
@@ -1028,7 +1155,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
       dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
 
       if(dns) {
-        infof(data, "RESOLVE %s:%d is - old addresses discarded!\n",
+        infof(data, "RESOLVE %s:%d is - old addresses discarded!",
                 hostname, port);
         /* delete old entry, there are two reasons for this
          1. old entry may have different addresses.
@@ -1061,12 +1188,12 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
         Curl_freeaddrinfo(head);
         return CURLE_OUT_OF_MEMORY;
       }
-      infof(data, "Added %s:%d:%s to DNS cache%s\n",
+      infof(data, "Added %s:%d:%s to DNS cache%s",
             hostname, port, addresses, permanent ? "" : " (non-permanent)");
 
       /* Wildcard hostname */
       if(hostname[0] == '*' && hostname[1] == '\0') {
-        infof(data, "RESOLVE %s:%d is wildcard, enabling wildcard checks\n",
+        infof(data, "RESOLVE %s:%d is wildcard, enabling wildcard checks",
               hostname, port);
         data->state.wildcard_resolve = true;
       }
@@ -1094,7 +1221,7 @@ int Curl_resolv_getsock(struct Curl_easy *data,
 {
 #ifdef CURLRES_ASYNCH
   if(data->conn->bits.doh)
-    /* nothing to wait for during DOH resolve, those handles have their own
+    /* nothing to wait for during DoH resolve, those handles have their own
        sockets */
     return GETSOCK_BLANK;
   return Curl_resolver_getsock(data, socks);

+ 3 - 1
lib/hostip.h

@@ -71,6 +71,8 @@ struct Curl_dns_entry {
   long inuse;
 };
 
+bool Curl_host_is_ipnum(const char *hostname);
+
 /*
  * Curl_resolv() returns an entry with the info for the specified host
  * and port.
@@ -95,7 +97,7 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
                                    struct Curl_dns_entry **dnsentry,
                                    timediff_t timeoutms);
 
-#ifdef CURLRES_IPV6
+#ifdef ENABLE_IPV6
 /*
  * Curl_ipv6works() returns TRUE if IPv6 seems to work.
  */

+ 1 - 2
lib/hostip4.c

@@ -50,7 +50,6 @@
 #include "hostip.h"
 #include "hash.h"
 #include "share.h"
-#include "strerror.h"
 #include "url.h"
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
@@ -104,7 +103,7 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
 
   ai = Curl_ipv4_resolve_r(hostname, port);
   if(!ai)
-    infof(data, "Curl_ipv4_resolve_r failed for %s\n", hostname);
+    infof(data, "Curl_ipv4_resolve_r failed for %s", hostname);
 
   return ai;
 }

+ 1 - 30
lib/hostip6.c

@@ -50,7 +50,6 @@
 #include "hostip.h"
 #include "hash.h"
 #include "share.h"
-#include "strerror.h"
 #include "url.h"
 #include "inet_pton.h"
 #include "connect.h"
@@ -59,34 +58,6 @@
 #include "curl_memory.h"
 #include "memdebug.h"
 
-/*
- * Curl_ipv6works() returns TRUE if IPv6 seems to work.
- */
-bool Curl_ipv6works(struct Curl_easy *data)
-{
-  if(data) {
-    /* the nature of most system is that IPv6 status doesn't come and go
-       during a program's lifetime so we only probe the first time and then we
-       have the info kept for fast re-use */
-    DEBUGASSERT(data);
-    DEBUGASSERT(data->multi);
-    return data->multi->ipv6_works;
-  }
-  else {
-    int ipv6_works = -1;
-    /* probe to see if we have a working IPv6 stack */
-    curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
-    if(s == CURL_SOCKET_BAD)
-      /* an IPv6 address was requested but we can't get/use one */
-      ipv6_works = 0;
-    else {
-      ipv6_works = 1;
-      sclose(s);
-    }
-    return (ipv6_works>0)?TRUE:FALSE;
-  }
-}
-
 /*
  * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
  * been set and returns TRUE if they are OK.
@@ -172,7 +143,7 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
 
   error = Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &res);
   if(error) {
-    infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port);
+    infof(data, "getaddrinfo(3) failed for %s:%d", hostname, port);
     return NULL;
   }
 

+ 1 - 2
lib/hostsyn.c

@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2021, 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
@@ -50,7 +50,6 @@
 #include "hostip.h"
 #include "hash.h"
 #include "share.h"
-#include "strerror.h"
 #include "url.h"
 #include "curl_memory.h"
 /* The last #include file should be: */

+ 8 - 1
lib/hsts.c

@@ -138,6 +138,11 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
   struct stsentry *sts;
   time_t now = time(NULL);
 
+  if(Curl_host_is_ipnum(hostname))
+    /* "explicit IP address identification of all forms is excluded."
+       / RFC 6797 */
+    return CURLE_OK;
+
   do {
     while(*p && ISSPACE(*p))
       p++;
@@ -521,7 +526,9 @@ CURLcode Curl_hsts_loadfile(struct Curl_easy *data,
  */
 CURLcode Curl_hsts_loadcb(struct Curl_easy *data, struct hsts *h)
 {
-  return hsts_pull(data, h);
+  if(h)
+    return hsts_pull(data, h);
+  return CURLE_OK;
 }
 
 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */

+ 157 - 122
lib/http.c

@@ -504,7 +504,7 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data,
         /* rewind data when completely done sending! */
         if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) {
           conn->bits.rewindaftersend = TRUE;
-          infof(data, "Rewind stream after send\n");
+          infof(data, "Rewind stream after send");
         }
 
         return CURLE_OK;
@@ -515,7 +515,7 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data,
         return CURLE_OK;
 
       infof(data, "NTLM send, close instead of sending %"
-            CURL_FORMAT_CURL_OFF_T " bytes\n",
+            CURL_FORMAT_CURL_OFF_T " bytes",
             (curl_off_t)(expectsend - bytessent));
     }
 #endif
@@ -532,7 +532,7 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data,
         /* rewind data when completely done sending! */
         if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) {
           conn->bits.rewindaftersend = TRUE;
-          infof(data, "Rewind stream after send\n");
+          infof(data, "Rewind stream after send");
         }
 
         return CURLE_OK;
@@ -543,7 +543,7 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data,
         return CURLE_OK;
 
       infof(data, "NEGOTIATE send, close instead of sending %"
-        CURL_FORMAT_CURL_OFF_T " bytes\n",
+        CURL_FORMAT_CURL_OFF_T " bytes",
         (curl_off_t)(expectsend - bytessent));
     }
 #endif
@@ -756,14 +756,14 @@ output_auth_headers(struct Curl_easy *data,
 
   if(auth) {
 #ifndef CURL_DISABLE_PROXY
-    infof(data, "%s auth using %s with user '%s'\n",
+    infof(data, "%s auth using %s with user '%s'",
           proxy ? "Proxy" : "Server", auth,
           proxy ? (data->state.aptr.proxyuser ?
                    data->state.aptr.proxyuser : "") :
           (data->state.aptr.user ?
            data->state.aptr.user : ""));
 #else
-    infof(data, "Server auth using %s with user '%s'\n",
+    infof(data, "Server auth using %s with user '%s'",
           auth, data->state.aptr.user ?
           data->state.aptr.user : "");
 #endif
@@ -850,7 +850,9 @@ Curl_http_output_auth(struct Curl_easy *data,
   /* To prevent the user+password to get sent to other than the original
      host due to a location-follow, we do some weirdo checks here */
   if(!data->state.this_is_a_follow ||
+#ifndef CURL_DISABLE_NETRC
      conn->bits.netrc ||
+#endif
      !data->state.first_host ||
      data->set.allow_auth_to_other_hosts ||
      strcasecompare(data->state.first_host, conn->host.name)) {
@@ -995,14 +997,14 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
 
                 result = Curl_input_ntlm_wb(data, conn, proxy, auth);
                 if(result) {
-                  infof(data, "Authentication problem. Ignoring this.\n");
+                  infof(data, "Authentication problem. Ignoring this.");
                   data->state.authproblem = TRUE;
                 }
               }
 #endif
             }
             else {
-              infof(data, "Authentication problem. Ignoring this.\n");
+              infof(data, "Authentication problem. Ignoring this.");
               data->state.authproblem = TRUE;
             }
           }
@@ -1013,7 +1015,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
 #ifndef CURL_DISABLE_CRYPTO_AUTH
         if(checkprefix("Digest", auth) && is_valid_auth_separator(auth[6])) {
           if((authp->avail & CURLAUTH_DIGEST) != 0)
-            infof(data, "Ignoring duplicate digest auth header.\n");
+            infof(data, "Ignoring duplicate digest auth header.");
           else if(Curl_auth_is_digest_supported()) {
             CURLcode result;
 
@@ -1026,7 +1028,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
              * Digest */
             result = Curl_input_digest(data, proxy, auth);
             if(result) {
-              infof(data, "Authentication problem. Ignoring this.\n");
+              infof(data, "Authentication problem. Ignoring this.");
               data->state.authproblem = TRUE;
             }
           }
@@ -1042,7 +1044,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
                  anyway, which basically means our name+password isn't
                  valid. */
               authp->avail = CURLAUTH_NONE;
-              infof(data, "Authentication problem. Ignoring this.\n");
+              infof(data, "Authentication problem. Ignoring this.");
               data->state.authproblem = TRUE;
             }
           }
@@ -1055,7 +1057,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
                 /* We asked for Bearer authentication but got a 40X back
                   anyway, which basically means our token isn't valid. */
                 authp->avail = CURLAUTH_NONE;
-                infof(data, "Authentication problem. Ignoring this.\n");
+                infof(data, "Authentication problem. Ignoring this.");
                 data->state.authproblem = TRUE;
               }
             }
@@ -1177,6 +1179,7 @@ static size_t readmoredata(char *buffer,
   data->req.forbidchunk = (http->sending == HTTPSEND_REQUEST)?TRUE:FALSE;
 
   if(data->set.max_send_speed &&
+     (data->set.max_send_speed < (curl_off_t)fullsize) &&
      (data->set.max_send_speed < http->postsize))
     /* speed limit */
     fullsize = (size_t)data->set.max_send_speed;
@@ -1537,38 +1540,35 @@ static int http_getsock_do(struct Curl_easy *data,
 #ifndef CURL_DISABLE_PROXY
 static CURLcode add_haproxy_protocol_header(struct Curl_easy *data)
 {
-  char proxy_header[128];
   struct dynbuf req;
   CURLcode result;
-  char tcp_version[5];
+  const char *tcp_version;
   DEBUGASSERT(data->conn);
+  Curl_dyn_init(&req, DYN_HAXPROXY);
 
-  /* Emit the correct prefix for IPv6 */
-  if(data->conn->bits.ipv6) {
-    strcpy(tcp_version, "TCP6");
-  }
+#ifdef USE_UNIX_SOCKETS
+  if(data->conn->unix_domain_socket)
+    /* the buffer is large enough to hold this! */
+    result = Curl_dyn_add(&req, "PROXY UNKNOWN\r\n");
   else {
-    strcpy(tcp_version, "TCP4");
-  }
-
-  msnprintf(proxy_header,
-            sizeof(proxy_header),
-            "PROXY %s %s %s %i %i\r\n",
-            tcp_version,
-            data->info.conn_local_ip,
-            data->info.conn_primary_ip,
-            data->info.conn_local_port,
-            data->info.conn_primary_port);
-
-  Curl_dyn_init(&req, DYN_HAXPROXY);
+#endif
+  /* Emit the correct prefix for IPv6 */
+  tcp_version = data->conn->bits.ipv6 ? "TCP6" : "TCP4";
 
-  result = Curl_dyn_add(&req, proxy_header);
-  if(result)
-    return result;
+  result = Curl_dyn_addf(&req, "PROXY %s %s %s %i %i\r\n",
+                         tcp_version,
+                         data->info.conn_local_ip,
+                         data->info.conn_primary_ip,
+                         data->info.conn_local_port,
+                         data->info.conn_primary_port);
 
-  result = Curl_buffer_send(&req, data, &data->info.request_size,
-                            0, FIRSTSOCKET);
+#ifdef USE_UNIX_SOCKETS
+  }
+#endif
 
+  if(!result)
+    result = Curl_buffer_send(&req, data, &data->info.request_size,
+                              0, FIRSTSOCKET);
   return result;
 }
 #endif
@@ -1588,7 +1588,7 @@ static CURLcode https_connecting(struct Curl_easy *data, bool *done)
 #endif
 
   /* perform SSL initialization for this socket */
-  result = Curl_ssl_connect_nonblocking(data, conn, FIRSTSOCKET, done);
+  result = Curl_ssl_connect_nonblocking(data, conn, FALSE, FIRSTSOCKET, done);
   if(result)
     connclose(conn, "Failed HTTPS connection");
 
@@ -1669,8 +1669,8 @@ CURLcode Curl_http_done(struct Curl_easy *data,
  * - if any server previously contacted to handle this request only supports
  * 1.0.
  */
-static bool use_http_1_1plus(const struct Curl_easy *data,
-                             const struct connectdata *conn)
+bool Curl_use_http_1_1plus(const struct Curl_easy *data,
+                           const struct connectdata *conn)
 {
   if((data->state.httpversion == 10) || (conn->httpversion == 10))
     return FALSE;
@@ -1696,7 +1696,7 @@ static const char *get_http_string(const struct Curl_easy *data,
     return "2";
 #endif
 
-  if(use_http_1_1plus(data, conn))
+  if(Curl_use_http_1_1plus(data, conn))
     return "1.1";
 
   return "1.0";
@@ -1711,7 +1711,7 @@ static CURLcode expect100(struct Curl_easy *data,
   CURLcode result = CURLE_OK;
   data->state.expect100header = FALSE; /* default to false unless it is set
                                           to TRUE below */
-  if(!data->state.disableexpect && use_http_1_1plus(data, conn) &&
+  if(!data->state.disableexpect && Curl_use_http_1_1plus(data, conn) &&
      (conn->httpversion < 20)) {
     /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an
        Expect: 100-continue to the headers which actually speeds up post
@@ -2348,7 +2348,7 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn,
       if(conn->bits.authneg)
         /* don't enable chunked during auth neg */
         ;
-      else if(use_http_1_1plus(data, conn)) {
+      else if(Curl_use_http_1_1plus(data, conn)) {
         if(conn->httpversion < 20)
           /* HTTP, upload, unknown file size and not HTTP 1.0 */
           data->req.upload_chunky = TRUE;
@@ -2711,14 +2711,16 @@ CURLcode Curl_http_cookies(struct Curl_easy *data,
     int count = 0;
 
     if(data->cookies && data->state.cookie_engine) {
+      const char *host = data->state.aptr.cookiehost ?
+        data->state.aptr.cookiehost : conn->host.name;
+      const bool secure_context =
+        conn->handler->protocol&CURLPROTO_HTTPS ||
+        strcasecompare("localhost", host) ||
+        !strcmp(host, "127.0.0.1") ||
+        !strcmp(host, "[::1]") ? TRUE : FALSE;
       Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
-      co = Curl_cookie_getlist(data->cookies,
-                               data->state.aptr.cookiehost?
-                               data->state.aptr.cookiehost:
-                               conn->host.name,
-                               data->state.up.path,
-                               (conn->handler->protocol&CURLPROTO_HTTPS)?
-                               TRUE:FALSE);
+      co = Curl_cookie_getlist(data->cookies, host, data->state.up.path,
+                               secure_context);
       Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
     }
     if(co) {
@@ -2901,6 +2903,20 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data,
 {
   struct SingleRequest *k = &data->req;
   DEBUGASSERT(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP));
+  if(data->req.ignore_cl) {
+    k->size = k->maxdownload = -1;
+  }
+  else if(k->size != -1) {
+    /* We wait until after all headers have been received to set this so that
+       we know for sure Content-Length is valid. */
+    if(data->set.max_filesize &&
+       k->size > data->set.max_filesize) {
+      failf(data, "Maximum file size exceeded");
+      return CURLE_FILESIZE_EXCEEDED;
+    }
+    Curl_pgrsSetDownloadSize(data, k->size);
+  }
+
   if(data->req.newurl) {
     if(conn->bits.close) {
       /* Abort after the headers if "follow Location" is set
@@ -2912,7 +2928,7 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data,
     /* We have a new url to load, but since we want to be able to re-use this
        connection properly, we read the full response in "ignore more" */
     k->ignorebody = TRUE;
-    infof(data, "Ignoring the response-body\n");
+    infof(data, "Ignoring the response-body");
   }
   if(data->state.resume_from && !k->content_range &&
      (data->state.httpreq == HTTPREQ_GET) &&
@@ -2947,7 +2963,7 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data,
       /* We're simulating a http 304 from server so we return
          what should have been returned from the server */
       data->info.httpcode = 304;
-      infof(data, "Simulate a HTTP 304 response!\n");
+      infof(data, "Simulate a HTTP 304 response!");
       /* we abort the transfer before it is completed == we ruin the
          re-use ability. Close the connection */
       connclose(conn, "Simulated 304 handling");
@@ -2958,6 +2974,39 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data,
   return CURLE_OK;
 }
 
+#ifdef HAVE_LIBZ
+CURLcode Curl_transferencode(struct Curl_easy *data)
+{
+  if(!Curl_checkheaders(data, "TE") &&
+     data->set.http_transfer_encoding) {
+    /* When we are to insert a TE: header in the request, we must also insert
+       TE in a Connection: header, so we need to merge the custom provided
+       Connection: header and prevent the original to get sent. Note that if
+       the user has inserted his/her own TE: header we don't do this magic
+       but then assume that the user will handle it all! */
+    char *cptr = Curl_checkheaders(data, "Connection");
+#define TE_HEADER "TE: gzip\r\n"
+
+    Curl_safefree(data->state.aptr.te);
+
+    if(cptr) {
+      cptr = Curl_copy_header_value(cptr);
+      if(!cptr)
+        return CURLE_OUT_OF_MEMORY;
+    }
+
+    /* Create the (updated) Connection: header */
+    data->state.aptr.te = aprintf("Connection: %s%sTE\r\n" TE_HEADER,
+                                cptr ? cptr : "", (cptr && *cptr) ? ", ":"");
+
+    free(cptr);
+    if(!data->state.aptr.te)
+      return CURLE_OUT_OF_MEMORY;
+  }
+  return CURLE_OK;
+}
+#endif
+
 #ifndef USE_HYPER
 /*
  * Curl_http() gets called from the generic multi_do() function when a HTTP
@@ -3004,11 +3053,11 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
           if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
             /* We don't support HTTP/2 proxies yet. Also it's debatable
                whether or not this setting should apply to HTTP/2 proxies. */
-            infof(data, "Ignoring HTTP/2 prior knowledge due to proxy\n");
+            infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
             break;
           }
 #endif
-          DEBUGF(infof(data, "HTTP/2 over clean TCP\n"));
+          DEBUGF(infof(data, "HTTP/2 over clean TCP"));
           conn->httpversion = 20;
 
           result = Curl_http2_switched(data, NULL, 0);
@@ -3074,33 +3123,9 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
 
 #ifdef HAVE_LIBZ
   /* we only consider transfer-encoding magic if libz support is built-in */
-
-  if(!Curl_checkheaders(data, "TE") &&
-     data->set.http_transfer_encoding) {
-    /* When we are to insert a TE: header in the request, we must also insert
-       TE in a Connection: header, so we need to merge the custom provided
-       Connection: header and prevent the original to get sent. Note that if
-       the user has inserted his/her own TE: header we don't do this magic
-       but then assume that the user will handle it all! */
-    char *cptr = Curl_checkheaders(data, "Connection");
-#define TE_HEADER "TE: gzip\r\n"
-
-    Curl_safefree(data->state.aptr.te);
-
-    if(cptr) {
-      cptr = Curl_copy_header_value(cptr);
-      if(!cptr)
-        return CURLE_OUT_OF_MEMORY;
-    }
-
-    /* Create the (updated) Connection: header */
-    data->state.aptr.te = aprintf("Connection: %s%sTE\r\n" TE_HEADER,
-                                cptr ? cptr : "", (cptr && *cptr) ? ", ":"");
-
-    free(cptr);
-    if(!data->state.aptr.te)
-      return CURLE_OUT_OF_MEMORY;
-  }
+  result = Curl_transferencode(data);
+  if(result)
+    return result;
 #endif
 
   result = Curl_http_body(data, conn, httpreq, &te);
@@ -3253,7 +3278,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
       /* already sent the entire request body, mark the "upload" as
          complete */
       infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T
-            " out of %" CURL_FORMAT_CURL_OFF_T " bytes\n",
+            " out of %" CURL_FORMAT_CURL_OFF_T " bytes",
             data->req.writebytecount, http->postsize);
       data->req.upload_done = TRUE;
       data->req.keepon &= ~KEEP_SEND; /* we're done writing */
@@ -3392,17 +3417,8 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
                                     NULL, 10, &contentlength);
 
     if(offt == CURL_OFFT_OK) {
-      if(data->set.max_filesize &&
-         contentlength > data->set.max_filesize) {
-        failf(data, "Maximum file size exceeded");
-        return CURLE_FILESIZE_EXCEEDED;
-      }
       k->size = contentlength;
       k->maxdownload = k->size;
-      /* we set the progress download size already at this point
-         just to make it easier for apps/callbacks to extract this
-         info as soon as possible */
-      Curl_pgrsSetDownloadSize(data, k->size);
     }
     else if(offt == CURL_OFFT_FLOW) {
       /* out of range */
@@ -3411,7 +3427,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
         return CURLE_FILESIZE_EXCEEDED;
       }
       streamclose(conn, "overflow content-length");
-      infof(data, "Overflow Content-Length: value!\n");
+      infof(data, "Overflow Content-Length: value!");
     }
     else {
       /* negative or just rubbish - bad HTTP */
@@ -3443,7 +3459,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
      * Default action for 1.0 is to close.
      */
     connkeep(conn, "Proxy-Connection keep-alive"); /* don't close */
-    infof(data, "HTTP/1.0 proxy connection set to keep alive!\n");
+    infof(data, "HTTP/1.0 proxy connection set to keep alive!");
   }
   else if((conn->httpversion == 11) &&
           conn->bits.httpproxy &&
@@ -3453,7 +3469,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
      * close down after this transfer.
      */
     connclose(conn, "Proxy-Connection: asked to close after done");
-    infof(data, "HTTP/1.1 proxy connection set close!\n");
+    infof(data, "HTTP/1.1 proxy connection set close!");
   }
 #endif
   else if((conn->httpversion == 10) &&
@@ -3465,7 +3481,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
      *
      * [RFC2068, section 19.7.1] */
     connkeep(conn, "Connection keep-alive");
-    infof(data, "HTTP/1.0 connection set to keep alive!\n");
+    infof(data, "HTTP/1.0 connection set to keep alive!");
   }
   else if(Curl_compareheader(headp, "Connection:", "close")) {
     /*
@@ -3493,6 +3509,12 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
                                          TRUE);
     if(result)
       return result;
+    if(!k->chunk) {
+      /* if this isn't chunked, only close can signal the end of this transfer
+         as Content-Length is said not to be trusted for transfer-encoding! */
+      connclose(conn, "HTTP/1.1 transfer-encoding without chunks");
+      k->ignore_cl = TRUE;
+    }
   }
   else if(!k->http_bodyless && checkprefix("Content-Encoding:", headp) &&
           data->set.str[STRING_ENCODING]) {
@@ -3555,18 +3577,21 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
 #if !defined(CURL_DISABLE_COOKIES)
   else if(data->cookies && data->state.cookie_engine &&
           checkprefix("Set-Cookie:", headp)) {
+    /* If there is a custom-set Host: name, use it here, or else use real peer
+       host name. */
+    const char *host = data->state.aptr.cookiehost?
+      data->state.aptr.cookiehost:conn->host.name;
+    const bool secure_context =
+      conn->handler->protocol&CURLPROTO_HTTPS ||
+      strcasecompare("localhost", host) ||
+      !strcmp(host, "127.0.0.1") ||
+      !strcmp(host, "[::1]") ? TRUE : FALSE;
+
     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE,
                     CURL_LOCK_ACCESS_SINGLE);
-    Curl_cookie_add(data,
-                    data->cookies, TRUE, FALSE,
-                    headp + strlen("Set-Cookie:"),
-                    /* If there is a custom-set Host: name, use it
-                       here, or else use real peer host name. */
-                    data->state.aptr.cookiehost?
-                    data->state.aptr.cookiehost:conn->host.name,
-                    data->state.up.path,
-                    (conn->handler->protocol&CURLPROTO_HTTPS)?
-                    TRUE:FALSE);
+    Curl_cookie_add(data, data->cookies, TRUE, FALSE,
+                    headp + strlen("Set-Cookie:"), host,
+                    data->state.up.path, secure_context);
     Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
   }
 #endif
@@ -3646,10 +3671,10 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
       Curl_hsts_parse(data->hsts, data->state.up.hostname,
                       headp + strlen("Strict-Transport-Security:"));
     if(check)
-      infof(data, "Illegal STS header skipped\n");
+      infof(data, "Illegal STS header skipped");
 #ifdef DEBUGBUILD
     else
-      infof(data, "Parsed STS header fine (%zu entries)\n",
+      infof(data, "Parsed STS header fine (%zu entries)",
             data->hsts->list.size);
 #endif
   }
@@ -3719,12 +3744,12 @@ CURLcode Curl_http_statusline(struct Curl_easy *data,
     /* Default action for HTTP/1.0 must be to close, unless
        we get one of those fancy headers that tell us the
        server keeps it open for us! */
-    infof(data, "HTTP 1.0, assume close after body\n");
+    infof(data, "HTTP 1.0, assume close after body");
     connclose(conn, "HTTP/1.0 close after body");
   }
   else if(conn->httpversion == 20 ||
           (k->upgr101 == UPGR101_REQUESTED && k->httpcode == 101)) {
-    DEBUGF(infof(data, "HTTP/2 found, allow multiplexing\n"));
+    DEBUGF(infof(data, "HTTP/2 found, allow multiplexing"));
     /* HTTP/2 cannot avoid multiplexing since it is a core functionality
        of the protocol */
     conn->bundle->multiuse = BUNDLE_MULTIPLEX;
@@ -3733,7 +3758,7 @@ CURLcode Curl_http_statusline(struct Curl_easy *data,
           !conn->bits.close) {
     /* If HTTP version is >= 1.1 and connection is persistent */
     DEBUGF(infof(data,
-                 "HTTP 1.1 or later with persistent connection\n"));
+                 "HTTP 1.1 or later with persistent connection"));
   }
 
   k->http_bodyless = k->httpcode >= 100 && k->httpcode < 200;
@@ -3911,7 +3936,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
           /* Switching Protocols */
           if(k->upgr101 == UPGR101_REQUESTED) {
             /* Switching to HTTP/2 */
-            infof(data, "Received 101\n");
+            infof(data, "Received 101");
             k->upgr101 = UPGR101_RECEIVED;
 
             /* we'll get more headers (HTTP/2 response) */
@@ -3951,7 +3976,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
              assume that the server will close the connection to
              signal the end of the document. */
           infof(data, "no chunk, no close, no size. Assume close to "
-                "signal end\n");
+                "signal end");
           streamclose(conn, "HTTP: No end-of-message indicator");
         }
       }
@@ -3964,7 +3989,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
            (conn->http_ntlm_state == NTLMSTATE_TYPE2)) ||
           ((data->req.httpcode == 407) &&
            (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) {
-        infof(data, "Connection closure while negotiating auth (HTTP 1.0?)\n");
+        infof(data, "Connection closure while negotiating auth (HTTP 1.0?)");
         data->state.authproblem = TRUE;
       }
 #endif
@@ -3974,7 +3999,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
           (conn->http_negotiate_state == GSS_AUTHRECV)) ||
          ((data->req.httpcode == 407) &&
           (conn->proxy_negotiate_state == GSS_AUTHRECV)))) {
-        infof(data, "Connection closure while negotiating auth (HTTP 1.0?)\n");
+        infof(data, "Connection closure while negotiating auth (HTTP 1.0?)");
         data->state.authproblem = TRUE;
       }
       if((conn->http_negotiate_state == GSS_AUTHDONE) &&
@@ -4054,21 +4079,21 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
               if((k->httpcode == 417) && data->state.expect100header) {
                 /* 417 Expectation Failed - try again without the Expect
                    header */
-                infof(data, "Got 417 while waiting for a 100\n");
+                infof(data, "Got 417 while waiting for a 100");
                 data->state.disableexpect = TRUE;
                 DEBUGASSERT(!data->req.newurl);
                 data->req.newurl = strdup(data->state.url);
                 Curl_done_sending(data, k);
               }
               else if(data->set.http_keep_sending_on_error) {
-                infof(data, "HTTP error before end of send, keep sending\n");
+                infof(data, "HTTP error before end of send, keep sending");
                 if(k->exp100 > EXP100_SEND_DATA) {
                   k->exp100 = EXP100_SEND_DATA;
                   k->keepon |= KEEP_SEND;
                 }
               }
               else {
-                infof(data, "HTTP error before end of send, stop sending\n");
+                infof(data, "HTTP error before end of send, stop sending");
                 streamclose(conn, "Stop sending data before everything sent");
                 result = Curl_done_sending(data, k);
                 if(result)
@@ -4088,7 +4113,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
         if(conn->bits.rewindaftersend) {
           /* We rewind after a complete send, so thus we continue
              sending now */
-          infof(data, "Keep sending data to get tossed away!\n");
+          infof(data, "Keep sending data to get tossed away!");
           k->keepon |= KEEP_SEND;
         }
       }
@@ -4201,18 +4226,20 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
          * https://tools.ietf.org/html/rfc7230#section-3.1.2
          *
          * The response code is always a three-digit number in HTTP as the spec
-         * says. We try to allow any number here, but we cannot make
+         * says. We allow any three-digit number here, but we cannot make
          * guarantees on future behaviors since it isn't within the protocol.
          */
         char separator;
         char twoorthree[2];
         int httpversion = 0;
+        int digit4 = -1; /* should remain untouched to be good */
         nc = sscanf(HEADER1,
-                    " HTTP/%1d.%1d%c%3d",
+                    " HTTP/%1d.%1d%c%3d%1d",
                     &httpversion_major,
                     &httpversion,
                     &separator,
-                    &k->httpcode);
+                    &k->httpcode,
+                    &digit4);
 
         if(nc == 1 && httpversion_major >= 2 &&
            2 == sscanf(HEADER1, " HTTP/%1[23] %d", twoorthree, &k->httpcode)) {
@@ -4221,6 +4248,14 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
           separator = ' ';
         }
 
+        /* There can only be a 4th response code digit stored in 'digit4' if
+           all the other fields were parsed and stored first, so nc is 5 when
+           digit4 is not -1 */
+        else if(digit4 != -1) {
+          failf(data, "Unsupported response code in HTTP response");
+          return CURLE_UNSUPPORTED_PROTOCOL;
+        }
+
         if((nc == 4) && (' ' == separator)) {
           httpversion += 10 * httpversion_major;
           switch(httpversion) {
@@ -4243,11 +4278,11 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
           if(k->upgr101 == UPGR101_RECEIVED) {
             /* supposedly upgraded to http2 now */
             if(conn->httpversion != 20)
-              infof(data, "Lying server, not serving HTTP/2\n");
+              infof(data, "Lying server, not serving HTTP/2");
           }
           if(conn->httpversion < 20) {
             conn->bundle->multiuse = BUNDLE_NO_MULTIUSE;
-            infof(data, "Mark bundle as not supporting multiuse\n");
+            infof(data, "Mark bundle as not supporting multiuse");
           }
         }
         else if(!nc) {

+ 3 - 0
lib/http.h

@@ -93,11 +93,14 @@ CURLcode Curl_http_statusline(struct Curl_easy *data,
                               struct connectdata *conn);
 CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
                           char *headp);
+CURLcode Curl_transferencode(struct Curl_easy *data);
 CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn,
                         Curl_HttpReq httpreq,
                         const char **teep);
 CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
                             struct dynbuf *r, Curl_HttpReq httpreq);
+bool Curl_use_http_1_1plus(const struct Curl_easy *data,
+                           const struct connectdata *conn);
 #ifndef CURL_DISABLE_COOKIES
 CURLcode Curl_http_cookies(struct Curl_easy *data,
                            struct connectdata *conn,

+ 96 - 99
lib/http2.c

@@ -146,12 +146,12 @@ static CURLcode http2_disconnect(struct Curl_easy *data,
   (void)data;
 #endif
 
-  H2BUGF(infof(data, "HTTP/2 DISCONNECT starts now\n"));
+  H2BUGF(infof(data, "HTTP/2 DISCONNECT starts now"));
 
   nghttp2_session_del(c->h2);
   Curl_safefree(c->inbuf);
 
-  H2BUGF(infof(data, "HTTP/2 DISCONNECT done\n"));
+  H2BUGF(infof(data, "HTTP/2 DISCONNECT done"));
 
   return CURLE_OK;
 }
@@ -196,11 +196,13 @@ static bool http2_connisdead(struct Curl_easy *data, struct connectdata *conn)
           data, FIRSTSOCKET, httpc->inbuf, H2_BUFSIZE, &result);
       if(nread != -1) {
         infof(data,
-              "%d bytes stray data read before trying h2 connection\n",
+              "%d bytes stray data read before trying h2 connection",
               (int)nread);
         httpc->nread_inbuf = 0;
         httpc->inbuflen = nread;
-        (void)h2_process_pending_input(data, httpc, &result);
+        if(h2_process_pending_input(data, httpc, &result) < 0)
+          /* immediate error, considered dead */
+          dead = TRUE;
       }
       else
         /* the read failed so let's say this is dead anyway */
@@ -350,13 +352,12 @@ static const struct Curl_handler Curl_handler_http2_ssl = {
 };
 
 /*
- * Store nghttp2 version info in this buffer, Prefix with a space.  Return
- * total length written.
+ * Store nghttp2 version info in this buffer.
  */
-int Curl_http2_ver(char *p, size_t len)
+void Curl_http2_ver(char *p, size_t len)
 {
   nghttp2_info *h2 = nghttp2_version(0);
-  return msnprintf(p, len, "nghttp2/%s", h2->version_str);
+  (void)msnprintf(p, len, "nghttp2/%s", h2->version_str);
 }
 
 /*
@@ -551,7 +552,7 @@ static int push_promise(struct Curl_easy *data,
                         const nghttp2_push_promise *frame)
 {
   int rv; /* one of the CURL_PUSH_* defines */
-  H2BUGF(infof(data, "PUSH_PROMISE received, stream %u!\n",
+  H2BUGF(infof(data, "PUSH_PROMISE received, stream %u!",
                frame->promised_stream_id));
   if(data->multi->push_cb) {
     struct HTTP *stream;
@@ -563,7 +564,7 @@ static int push_promise(struct Curl_easy *data,
     /* clone the parent */
     struct Curl_easy *newhandle = duphandle(data);
     if(!newhandle) {
-      infof(data, "failed to duplicate handle\n");
+      infof(data, "failed to duplicate handle");
       rv = CURL_PUSH_DENY; /* FAIL HARD */
       goto fail;
     }
@@ -571,7 +572,7 @@ static int push_promise(struct Curl_easy *data,
     heads.data = data;
     heads.frame = frame;
     /* ask the application */
-    H2BUGF(infof(data, "Got PUSH_PROMISE, ask application!\n"));
+    H2BUGF(infof(data, "Got PUSH_PROMISE, ask application!"));
 
     stream = data->req.p.http;
     if(!stream) {
@@ -619,7 +620,7 @@ static int push_promise(struct Curl_easy *data,
        state with the given connection !*/
     rc = Curl_multi_add_perform(data->multi, newhandle, conn);
     if(rc) {
-      infof(data, "failed to add handle to multi\n");
+      infof(data, "failed to add handle to multi");
       http2_stream_free(newhandle->req.p.http);
       newhandle->req.p.http = NULL;
       Curl_close(&newhandle);
@@ -632,15 +633,17 @@ static int push_promise(struct Curl_easy *data,
                                               frame->promised_stream_id,
                                               newhandle);
     if(rv) {
-      infof(data, "failed to set user_data for stream %d\n",
+      infof(data, "failed to set user_data for stream %d",
             frame->promised_stream_id);
       DEBUGASSERT(0);
       rv = CURL_PUSH_DENY;
       goto fail;
     }
+    Curl_dyn_init(&newstream->header_recvbuf, DYN_H2_HEADERS);
+    Curl_dyn_init(&newstream->trailer_recvbuf, DYN_H2_TRAILERS);
   }
   else {
-    H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it!\n"));
+    H2BUGF(infof(data, "Got PUSH_PROMISE, ignore it!"));
     rv = CURL_PUSH_DENY;
   }
   fail:
@@ -676,21 +679,21 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
     /* stream ID zero is for connection-oriented stuff */
     if(frame->hd.type == NGHTTP2_SETTINGS) {
       uint32_t max_conn = httpc->settings.max_concurrent_streams;
-      H2BUGF(infof(data, "Got SETTINGS\n"));
+      H2BUGF(infof(data, "Got SETTINGS"));
       httpc->settings.max_concurrent_streams =
         nghttp2_session_get_remote_settings(
           session, NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS);
       httpc->settings.enable_push =
         nghttp2_session_get_remote_settings(
           session, NGHTTP2_SETTINGS_ENABLE_PUSH);
-      H2BUGF(infof(data, "MAX_CONCURRENT_STREAMS == %d\n",
+      H2BUGF(infof(data, "MAX_CONCURRENT_STREAMS == %d",
                    httpc->settings.max_concurrent_streams));
-      H2BUGF(infof(data, "ENABLE_PUSH == %s\n",
+      H2BUGF(infof(data, "ENABLE_PUSH == %s",
                    httpc->settings.enable_push?"TRUE":"false"));
       if(max_conn != httpc->settings.max_concurrent_streams) {
         /* only signal change if the value actually changed */
         infof(data,
-              "Connection state changed (MAX_CONCURRENT_STREAMS == %u)!\n",
+              "Connection state changed (MAX_CONCURRENT_STREAMS == %u)!",
               httpc->settings.max_concurrent_streams);
         multi_connchanged(data->multi);
       }
@@ -700,19 +703,19 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
   data_s = nghttp2_session_get_stream_user_data(session, stream_id);
   if(!data_s) {
     H2BUGF(infof(data,
-                 "No Curl_easy associated with stream: %x\n",
+                 "No Curl_easy associated with stream: %x",
                  stream_id));
     return 0;
   }
 
   stream = data_s->req.p.http;
   if(!stream) {
-    H2BUGF(infof(data_s, "No proto pointer for stream: %x\n",
+    H2BUGF(infof(data_s, "No proto pointer for stream: %x",
                  stream_id));
     return NGHTTP2_ERR_CALLBACK_FAILURE;
   }
 
-  H2BUGF(infof(data_s, "on_frame_recv() header %x stream %x\n",
+  H2BUGF(infof(data_s, "on_frame_recv() header %x stream %x",
                frame->hd.type, stream_id));
 
   switch(frame->hd.type) {
@@ -760,7 +763,8 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
            ncopy);
     stream->nread_header_recvbuf += ncopy;
 
-    H2BUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p\n",
+    DEBUGASSERT(stream->mem);
+    H2BUGF(infof(data_s, "Store %zu bytes headers from stream %u at %p",
                  ncopy, stream_id, stream->mem));
 
     stream->len -= ncopy;
@@ -782,13 +786,13 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
       if(nghttp2_is_fatal(h2))
         return NGHTTP2_ERR_CALLBACK_FAILURE;
       else if(rv == CURL_PUSH_ERROROUT) {
-        DEBUGF(infof(data_s, "Fail the parent stream (too)\n"));
+        DEBUGF(infof(data_s, "Fail the parent stream (too)"));
         return NGHTTP2_ERR_CALLBACK_FAILURE;
       }
     }
     break;
   default:
-    H2BUGF(infof(data_s, "Got frame type %x for stream %u!\n",
+    H2BUGF(infof(data_s, "Got frame type %x for stream %u!",
                  frame->hd.type, stream_id));
     break;
   }
@@ -833,7 +837,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
     Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
 
   H2BUGF(infof(data_s, "%zu data received for stream %u "
-               "(%zu left in buffer %p, total %zu)\n",
+               "(%zu left in buffer %p, total %zu)",
                nread, stream_id,
                stream->len, stream->mem,
                stream->memlen));
@@ -842,7 +846,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
     stream->pausedata = mem + nread;
     stream->pauselen = len - nread;
     H2BUGF(infof(data_s, "NGHTTP2_ERR_PAUSE - %zu bytes out of buffer"
-                 ", stream %u\n",
+                 ", stream %u",
                  len - nread, stream_id));
     data_s->conn->proto.httpc.pause_stream_id = stream_id;
 
@@ -880,7 +884,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
          decided to reject stream (e.g., PUSH_PROMISE). */
       return 0;
     }
-    H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u\n",
+    H2BUGF(infof(data_s, "on_stream_close(), %s (err %d), stream %u",
                  nghttp2_http2_strerror(error_code), error_code, stream_id));
     stream = data_s->req.p.http;
     if(!stream)
@@ -895,15 +899,15 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
     /* remove the entry from the hash as the stream is now gone */
     rv = nghttp2_session_set_stream_user_data(session, stream_id, 0);
     if(rv) {
-      infof(data_s, "http/2: failed to clear user_data for stream %d!\n",
+      infof(data_s, "http/2: failed to clear user_data for stream %d!",
             stream_id);
       DEBUGASSERT(0);
     }
     if(stream_id == httpc->pause_stream_id) {
-      H2BUGF(infof(data_s, "Stopped the pause stream!\n"));
+      H2BUGF(infof(data_s, "Stopped the pause stream!"));
       httpc->pause_stream_id = 0;
     }
-    H2BUGF(infof(data_s, "Removed stream %u hash!\n", stream_id));
+    H2BUGF(infof(data_s, "Removed stream %u hash!", stream_id));
     stream->stream_id = 0; /* cleared */
   }
   return 0;
@@ -921,7 +925,7 @@ static int on_begin_headers(nghttp2_session *session,
     return 0;
   }
 
-  H2BUGF(infof(data_s, "on_begin_headers() was called\n"));
+  H2BUGF(infof(data_s, "on_begin_headers() was called"));
 
   if(frame->hd.type != NGHTTP2_HEADERS) {
     return 0;
@@ -1049,7 +1053,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
 
   if(stream->bodystarted) {
     /* This is a trailer */
-    H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen,
+    H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s", namelen, name, valuelen,
                  value));
     result = Curl_dyn_addf(&stream->trailer_recvbuf,
                            "%.*s: %.*s\r\n", namelen, name,
@@ -1082,7 +1086,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
     if(get_transfer(httpc) != data_s)
       Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
 
-    H2BUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)\n",
+    H2BUGF(infof(data_s, "h2 status: HTTP/2 %03d (easy %p)",
                  stream->status_code, data_s));
     return 0;
   }
@@ -1106,7 +1110,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
   if(get_transfer(httpc) != data_s)
     Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
 
-  H2BUGF(infof(data_s, "h2 header: %.*s: %.*s\n", namelen, name, valuelen,
+  H2BUGF(infof(data_s, "h2 header: %.*s: %.*s", namelen, name, valuelen,
                value));
 
   return 0; /* 0 is successful */
@@ -1156,7 +1160,7 @@ static ssize_t data_source_read_callback(nghttp2_session *session,
     return NGHTTP2_ERR_DEFERRED;
 
   H2BUGF(infof(data_s, "data_source_read_callback: "
-               "returns %zu bytes stream %u\n",
+               "returns %zu bytes stream %u",
                nread, stream_id));
 
   return nread;
@@ -1223,7 +1227,7 @@ void Curl_http2_done(struct Curl_easy *data, bool premature)
       (void)nghttp2_session_send(httpc->h2);
 
     if(http->stream_id == httpc->pause_stream_id) {
-      infof(data, "stopped the pause stream!\n");
+      infof(data, "stopped the pause stream!");
       httpc->pause_stream_id = 0;
     }
   }
@@ -1236,7 +1240,7 @@ void Curl_http2_done(struct Curl_easy *data, bool premature)
     int rv = nghttp2_session_set_stream_user_data(httpc->h2,
                                                   http->stream_id, 0);
     if(rv) {
-      infof(data, "http/2: failed to clear user_data for stream %d!\n",
+      infof(data, "http/2: failed to clear user_data for stream %d!",
             http->stream_id);
       DEBUGASSERT(0);
     }
@@ -1383,7 +1387,7 @@ static int h2_process_pending_input(struct Curl_easy *data,
   if(nread == rv) {
     H2BUGF(infof(data,
                  "h2_process_pending_input: All data in connection buffer "
-                 "processed\n"));
+                 "processed"));
     httpc->inbuflen = 0;
     httpc->nread_inbuf = 0;
   }
@@ -1391,7 +1395,7 @@ static int h2_process_pending_input(struct Curl_easy *data,
     httpc->nread_inbuf += rv;
     H2BUGF(infof(data,
                  "h2_process_pending_input: %zu bytes left in connection "
-                 "buffer\n",
+                 "buffer",
                  httpc->inbuflen - httpc->nread_inbuf));
   }
 
@@ -1412,7 +1416,7 @@ static int h2_process_pending_input(struct Curl_easy *data,
   if(should_close_session(httpc)) {
     struct HTTP *stream = data->req.p.http;
     H2BUGF(infof(data,
-                 "h2_process_pending_input: nothing to do in this session\n"));
+                 "h2_process_pending_input: nothing to do in this session"));
     if(stream->error)
       *err = CURLE_HTTP2;
     else {
@@ -1456,7 +1460,7 @@ CURLcode Curl_http2_done_sending(struct Curl_easy *data,
       struct SingleRequest *k = &data->req;
       int rv;
 
-      H2BUGF(infof(data, "HTTP/2 still wants to send data (easy %p)\n", data));
+      H2BUGF(infof(data, "HTTP/2 still wants to send data (easy %p)", data));
 
       /* and attempt to send the pending frames */
       rv = h2_session_send(data, h2);
@@ -1495,7 +1499,7 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn,
   /* Reset to FALSE to prevent infinite loop in readwrite_data function. */
   stream->closed = FALSE;
   if(stream->error == NGHTTP2_REFUSED_STREAM) {
-    H2BUGF(infof(data, "REFUSED_STREAM (%d), try again on a new connection!\n",
+    H2BUGF(infof(data, "REFUSED_STREAM (%d), try again on a new connection!",
                  stream->stream_id));
     connclose(conn, "REFUSED_STREAM"); /* don't use this anymore */
     data->state.refused_stream = TRUE;
@@ -1544,7 +1548,7 @@ static ssize_t http2_handle_stream_close(struct connectdata *conn,
 
   stream->close_handled = TRUE;
 
-  H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close\n"));
+  H2BUGF(infof(data, "http2_recv returns 0, http2_handle_stream_close"));
   return 0;
 }
 
@@ -1587,7 +1591,7 @@ static int h2_session_send(struct Curl_easy *data,
 
     h2_pri_spec(data, &pri_spec);
 
-    H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)\n",
+    H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)",
                  stream->stream_id, data));
     DEBUGASSERT(stream->stream_id != -1);
     rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
@@ -1611,7 +1615,7 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
 
   if(should_close_session(httpc)) {
     H2BUGF(infof(data,
-                 "http2_recv: nothing to do in this session\n"));
+                 "http2_recv: nothing to do in this session"));
     if(conn->bits.close) {
       /* already marked for closure, return OK and we're done */
       *err = CURLE_OK;
@@ -1621,10 +1625,6 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
     return -1;
   }
 
-  if(stream->closed)
-    /* closed overrides paused */
-    return http2_handle_stream_close(conn, data, stream, err);
-
   /* Nullify here because we call nghttp2_session_send() and they
      might refer to the old buffer. */
   stream->upload_mem = NULL;
@@ -1645,12 +1645,12 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
            stream->nread_header_recvbuf, ncopy);
     stream->nread_header_recvbuf += ncopy;
 
-    H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf\n",
+    H2BUGF(infof(data, "http2_recv: Got %d bytes from header_recvbuf",
                  (int)ncopy));
     return ncopy;
   }
 
-  H2BUGF(infof(data, "http2_recv: easy %p (stream %u) win %u/%u\n",
+  H2BUGF(infof(data, "http2_recv: easy %p (stream %u) win %u/%u",
                data, stream->stream_id,
                nghttp2_session_get_local_window_size(httpc->h2),
                nghttp2_session_get_stream_local_window_size(httpc->h2,
@@ -1658,7 +1658,7 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
            ));
 
   if((data->state.drain) && stream->memlen) {
-    H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)\n",
+    H2BUGF(infof(data, "http2_recv: DRAIN %zu bytes stream %u!! (%p => %p)",
                  stream->memlen, stream->stream_id,
                  stream->mem, mem));
     if(mem != stream->mem) {
@@ -1686,7 +1686,7 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
     stream->pauselen -= nread;
 
     if(stream->pauselen == 0) {
-      H2BUGF(infof(data, "Unpaused by stream %u\n", stream->stream_id));
+      H2BUGF(infof(data, "Unpaused by stream %u", stream->stream_id));
       DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
       httpc->pause_stream_id = 0;
 
@@ -1704,7 +1704,7 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
         return -1;
       }
     }
-    H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u\n",
+    H2BUGF(infof(data, "http2_recv: returns unpaused %zd bytes on stream %u",
                  nread, stream->stream_id));
     return nread;
   }
@@ -1720,7 +1720,7 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
     if(stream->closed)
       /* closed overrides paused */
       return 0;
-    H2BUGF(infof(data, "stream %x is paused, pause id: %x\n",
+    H2BUGF(infof(data, "stream %x is paused, pause id: %x",
                  stream->stream_id, httpc->pause_stream_id));
     *err = CURLE_AGAIN;
     return -1;
@@ -1758,12 +1758,12 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
           return -1;
         }
 
-        H2BUGF(infof(data, "end of stream\n"));
+        H2BUGF(infof(data, "end of stream"));
         *err = CURLE_OK;
         return 0;
       }
 
-      H2BUGF(infof(data, "nread=%zd\n", nread));
+      H2BUGF(infof(data, "nread=%zd", nread));
 
       httpc->inbuflen = nread;
 
@@ -1772,7 +1772,7 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
     else {
       nread = httpc->inbuflen - httpc->nread_inbuf;
       (void)nread;  /* silence warning, used in debug */
-      H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd\n",
+      H2BUGF(infof(data, "Use data left in connection buffer, nread=%zd",
                    nread));
     }
 
@@ -1781,14 +1781,14 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
   }
   if(stream->memlen) {
     ssize_t retlen = stream->memlen;
-    H2BUGF(infof(data, "http2_recv: returns %zd for stream %u\n",
+    H2BUGF(infof(data, "http2_recv: returns %zd for stream %u",
                  retlen, stream->stream_id));
     stream->memlen = 0;
 
     if(httpc->pause_stream_id == stream->stream_id) {
       /* data for this stream is returned now, but this stream caused a pause
          already so we need it called again asap */
-      H2BUGF(infof(data, "Data returned for PAUSED stream %u\n",
+      H2BUGF(infof(data, "Data returned for PAUSED stream %u",
                    stream->stream_id));
     }
     else if(!stream->closed) {
@@ -1803,7 +1803,7 @@ static ssize_t http2_recv(struct Curl_easy *data, int sockindex,
   if(stream->closed)
     return http2_handle_stream_close(conn, data, stream, err);
   *err = CURLE_AGAIN;
-  H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u\n",
+  H2BUGF(infof(data, "http2_recv returns AGAIN for stream %u",
                stream->stream_id));
   return -1;
 }
@@ -1907,11 +1907,11 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
 
   (void)sockindex;
 
-  H2BUGF(infof(data, "http2_send len=%zu\n", len));
+  H2BUGF(infof(data, "http2_send len=%zu", len));
 
   if(stream->stream_id != -1) {
     if(stream->close_handled) {
-      infof(data, "stream %d closed\n", stream->stream_id);
+      infof(data, "stream %d closed", stream->stream_id);
       *err = CURLE_HTTP2_STREAM;
       return -1;
     }
@@ -1940,7 +1940,7 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
     stream->upload_len = 0;
 
     if(should_close_session(httpc)) {
-      H2BUGF(infof(data, "http2_send: nothing to do in this session\n"));
+      H2BUGF(infof(data, "http2_send: nothing to do in this session"));
       *err = CURLE_HTTP2;
       return -1;
     }
@@ -1953,7 +1953,7 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
       nghttp2_session_resume_data(h2, stream->stream_id);
     }
 
-    H2BUGF(infof(data, "http2_send returns %zu for stream %u\n", len,
+    H2BUGF(infof(data, "http2_send returns %zu for stream %u", len,
                  stream->stream_id));
     return len;
   }
@@ -2116,7 +2116,7 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
     for(i = 0; i < nheader; ++i) {
       acc += nva[i].namelen + nva[i].valuelen;
 
-      H2BUGF(infof(data, "h2 header: %.*s:%.*s\n",
+      H2BUGF(infof(data, "h2 header: %.*s:%.*s",
                    nva[i].namelen, nva[i].name,
                    nva[i].valuelen, nva[i].value));
     }
@@ -2124,13 +2124,13 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
     if(acc > MAX_ACC) {
       infof(data, "http2_send: Warning: The cumulative length of all "
             "headers exceeds %d bytes and that could cause the "
-            "stream to be rejected.\n", MAX_ACC);
+            "stream to be rejected.", MAX_ACC);
     }
   }
 
   h2_pri_spec(data, &pri_spec);
 
-  H2BUGF(infof(data, "http2_send request allowed %d (easy handle %p)\n",
+  H2BUGF(infof(data, "http2_send request allowed %d (easy handle %p)",
                nghttp2_session_check_request_allowed(h2), (void *)data));
 
   switch(data->state.httpreq) {
@@ -2158,20 +2158,20 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
 
   if(stream_id < 0) {
     H2BUGF(infof(data,
-                 "http2_send() nghttp2_submit_request error (%s)%d\n",
+                 "http2_send() nghttp2_submit_request error (%s)%d",
                  nghttp2_strerror(stream_id), stream_id));
     *err = CURLE_SEND_ERROR;
     return -1;
   }
 
-  infof(data, "Using Stream ID: %x (easy handle %p)\n",
+  infof(data, "Using Stream ID: %x (easy handle %p)",
         stream_id, (void *)data);
   stream->stream_id = stream_id;
 
   rv = h2_session_send(data, h2);
   if(rv) {
     H2BUGF(infof(data,
-                 "http2_send() nghttp2_session_send error (%s)%d\n",
+                 "http2_send() nghttp2_session_send error (%s)%d",
                  nghttp2_strerror(rv), rv));
 
     *err = CURLE_SEND_ERROR;
@@ -2179,7 +2179,7 @@ static ssize_t http2_send(struct Curl_easy *data, int sockindex,
   }
 
   if(should_close_session(httpc)) {
-    H2BUGF(infof(data, "http2_send: nothing to do in this session\n"));
+    H2BUGF(infof(data, "http2_send: nothing to do in this session"));
     *err = CURLE_HTTP2;
     return -1;
   }
@@ -2215,6 +2215,22 @@ CURLcode Curl_http2_setup(struct Curl_easy *data,
   Curl_dyn_init(&stream->header_recvbuf, DYN_H2_HEADERS);
   Curl_dyn_init(&stream->trailer_recvbuf, DYN_H2_TRAILERS);
 
+  stream->upload_left = 0;
+  stream->upload_mem = NULL;
+  stream->upload_len = 0;
+  stream->mem = data->state.buffer;
+  stream->len = data->set.buffer_size;
+
+  httpc->inbuflen = 0;
+  httpc->nread_inbuf = 0;
+
+  httpc->pause_stream_id = 0;
+  httpc->drain_total = 0;
+
+  multi_connchanged(data->multi);
+  /* below this point only connection related inits are done, which only needs
+     to be done once per connection */
+
   if((conn->handler == &Curl_handler_http2_ssl) ||
      (conn->handler == &Curl_handler_http2))
     return CURLE_OK; /* already done */
@@ -2230,25 +2246,13 @@ CURLcode Curl_http2_setup(struct Curl_easy *data,
     return result;
   }
 
-  infof(data, "Using HTTP2, server supports multi-use\n");
-  stream->upload_left = 0;
-  stream->upload_mem = NULL;
-  stream->upload_len = 0;
-  stream->mem = data->state.buffer;
-  stream->len = data->set.buffer_size;
-
-  httpc->inbuflen = 0;
-  httpc->nread_inbuf = 0;
-
-  httpc->pause_stream_id = 0;
-  httpc->drain_total = 0;
+  infof(data, "Using HTTP2, server supports multiplexing");
 
   conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
   conn->httpversion = 20;
   conn->bundle->multiuse = BUNDLE_MULTIPLEX;
 
-  infof(data, "Connection state changed (HTTP/2 confirmed)\n");
-  multi_connchanged(data->multi);
+  infof(data, "Connection state changed (HTTP/2 confirmed)");
 
   return CURLE_OK;
 }
@@ -2287,7 +2291,7 @@ CURLcode Curl_http2_switched(struct Curl_easy *data,
                                               stream->stream_id,
                                               data);
     if(rv) {
-      infof(data, "http/2: failed to set user_data for stream %d!\n",
+      infof(data, "http/2: failed to set user_data for stream %d!",
             stream->stream_id);
       DEBUGASSERT(0);
     }
@@ -2327,7 +2331,7 @@ CURLcode Curl_http2_switched(struct Curl_easy *data,
   }
 
   infof(data, "Copying HTTP/2 data in stream buffer to connection buffer"
-        " after upgrade: len=%zu\n",
+        " after upgrade: len=%zu",
         nread);
 
   if(nread)
@@ -2337,15 +2341,8 @@ CURLcode Curl_http2_switched(struct Curl_easy *data,
 
   DEBUGASSERT(httpc->nread_inbuf == 0);
 
-  /* Good enough to call it an end once the remaining payload is copied to the
-   * connection buffer.
-   * Some servers (e.g. nghttpx v1.43.0) may fulfill stream 1 immediately
-   * following the protocol switch other than waiting for the client-side
-   * connection preface. If h2_process_pending_input is invoked here to parse
-   * the remaining payload, stream 1 would be marked as closed too early and
-   * thus ignored in http2_recv (following 252790c53).
-   * The logic in lib/http.c and lib/transfer.c guarantees a following
-   * http2_recv would be invoked very soon. */
+  if(-1 == h2_process_pending_input(data, httpc, &result))
+    return CURLE_HTTP2;
 
   return CURLE_OK;
 }
@@ -2378,7 +2375,7 @@ CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause)
     if(rv)
       return CURLE_SEND_ERROR;
 
-    DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u\n",
+    DEBUGF(infof(data, "Set HTTP/2 window size to %u for stream %u",
                  window, stream->stream_id));
 
 #ifdef DEBUGBUILD
@@ -2387,7 +2384,7 @@ CURLcode Curl_http2_stream_pause(struct Curl_easy *data, bool pause)
       uint32_t window2 =
         nghttp2_session_get_stream_local_window_size(httpc->h2,
                                                      stream->stream_id);
-      DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u\n",
+      DEBUGF(infof(data, "HTTP/2 window size is now %u for stream %u",
                    window2, stream->stream_id));
     }
 #endif

+ 2 - 3
lib/http2.h

@@ -32,10 +32,9 @@
 #define DEFAULT_MAX_CONCURRENT_STREAMS 100
 
 /*
- * Store nghttp2 version info in this buffer, Prefix with a space.  Return
- * total length written.
+ * Store nghttp2 version info in this buffer.
  */
-int Curl_http2_ver(char *p, size_t len);
+void Curl_http2_ver(char *p, size_t len);
 
 const char *Curl_http2_strerror(uint32_t err);
 

+ 6 - 6
lib/http_aws_sigv4.c

@@ -126,7 +126,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
   tmp1 = strchr(tmp0, ':');
   len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
   if(len < 1) {
-    infof(data, "first provider can't be empty\n");
+    infof(data, "first provider can't be empty");
     ret = CURLE_BAD_FUNCTION_ARGUMENT;
     goto fail;
   }
@@ -145,7 +145,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
     tmp1 = strchr(tmp0, ':');
     len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
     if(len < 1) {
-      infof(data, "second provider can't be empty\n");
+      infof(data, "second provider can't be empty");
       ret = CURLE_BAD_FUNCTION_ARGUMENT;
       goto fail;
     }
@@ -165,7 +165,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
       tmp1 = strchr(tmp0, ':');
       len = tmp1 ? (size_t)(tmp1 - tmp0) : strlen(tmp0);
       if(len < 1) {
-        infof(data, "region can't be empty\n");
+        infof(data, "region can't be empty");
         ret = CURLE_BAD_FUNCTION_ARGUMENT;
         goto fail;
       }
@@ -182,7 +182,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
           goto fail;
         }
         if(strlen(service) < 1) {
-          infof(data, "service can't be empty\n");
+          infof(data, "service can't be empty");
           ret = CURLE_BAD_FUNCTION_ARGUMENT;
           goto fail;
         }
@@ -203,7 +203,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
     tmp1 = strchr(tmp0, '.');
     len = tmp1 - tmp0;
     if(!tmp1 || len < 1) {
-      infof(data, "service missing in parameters or hostname\n");
+      infof(data, "service missing in parameters or hostname");
       ret = CURLE_URL_MALFORMAT;
       goto fail;
     }
@@ -218,7 +218,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
       tmp1 = strchr(tmp0, '.');
       len = tmp1 - tmp0;
       if(!tmp1 || len < 1) {
-        infof(data, "region missing in parameters or hostname\n");
+        infof(data, "region missing in parameters or hostname");
         ret = CURLE_URL_MALFORMAT;
         goto fail;
       }

+ 2 - 1
lib/http_digest.c

@@ -146,7 +146,8 @@ CURLcode Curl_output_digest(struct Curl_easy *data,
     tmp = strchr((char *)uripath, '?');
     if(tmp) {
       size_t urilen = tmp - (char *)uripath;
-      path = (unsigned char *) aprintf("%.*s", urilen, uripath);
+      /* typecast is fine here since the value is always less than 32 bits */
+      path = (unsigned char *) aprintf("%.*s", (int)urilen, uripath);
     }
   }
   if(!tmp)

+ 3 - 3
lib/http_negotiate.c

@@ -89,7 +89,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
   neg_ctx->havenegdata = len != 0;
   if(!len) {
     if(state == GSS_AUTHSUCC) {
-      infof(data, "Negotiate auth restarted\n");
+      infof(data, "Negotiate auth restarted");
       Curl_http_auth_cleanup_negotiate(conn);
     }
     else if(state != GSS_AUTHNONE) {
@@ -142,11 +142,11 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data,
   }
 
   if(neg_ctx->noauthpersist ||
-    (*state != GSS_AUTHDONE && *state != GSS_AUTHSUCC)) {
+     (*state != GSS_AUTHDONE && *state != GSS_AUTHSUCC)) {
 
     if(neg_ctx->noauthpersist && *state == GSS_AUTHSUCC) {
       infof(data, "Curl_output_negotiate, "
-       "no persistent authentication: cleanup existing context");
+            "no persistent authentication: cleanup existing context");
       Curl_http_auth_cleanup_negotiate(conn);
     }
     if(!neg_ctx->context) {

+ 3 - 3
lib/http_ntlm.c

@@ -100,17 +100,17 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data,
     }
     else {
       if(*state == NTLMSTATE_LAST) {
-        infof(data, "NTLM auth restarted\n");
+        infof(data, "NTLM auth restarted");
         Curl_http_auth_cleanup_ntlm(conn);
       }
       else if(*state == NTLMSTATE_TYPE3) {
-        infof(data, "NTLM handshake rejected\n");
+        infof(data, "NTLM handshake rejected");
         Curl_http_auth_cleanup_ntlm(conn);
         *state = NTLMSTATE_NONE;
         return CURLE_REMOTE_ACCESS_DENIED;
       }
       else if(*state >= NTLMSTATE_TYPE1) {
-        infof(data, "NTLM handshake failure (internal error)\n");
+        infof(data, "NTLM handshake failure (internal error)");
         return CURLE_REMOTE_ACCESS_DENIED;
       }
 

+ 85 - 45
lib/http_proxy.c

@@ -61,7 +61,7 @@ static CURLcode https_proxy_connect(struct Curl_easy *data, int sockindex)
   if(!conn->bits.proxy_ssl_connected[sockindex]) {
     /* perform SSL initialization for this socket */
     result =
-      Curl_ssl_connect_nonblocking(data, conn, sockindex,
+      Curl_ssl_connect_nonblocking(data, conn, TRUE, sockindex,
                                    &conn->bits.proxy_ssl_connected[sockindex]);
     if(result)
       /* a failed connection is marked for closure to prevent (bad) re-use or
@@ -129,13 +129,13 @@ CURLcode Curl_proxy_connect(struct Curl_easy *data, int sockindex)
 bool Curl_connect_complete(struct connectdata *conn)
 {
   return !conn->connect_state ||
-    (conn->connect_state->tunnel_state == TUNNEL_COMPLETE);
+    (conn->connect_state->tunnel_state >= TUNNEL_COMPLETE);
 }
 
 bool Curl_connect_ongoing(struct connectdata *conn)
 {
   return conn->connect_state &&
-    (conn->connect_state->tunnel_state != TUNNEL_COMPLETE);
+    (conn->connect_state->tunnel_state <= TUNNEL_COMPLETE);
 }
 
 /* when we've sent a CONNECT to a proxy, we should rather either wait for the
@@ -148,7 +148,7 @@ int Curl_connect_getsock(struct connectdata *conn)
   DEBUGASSERT(conn->connect_state);
   http = &conn->connect_state->http_proxy;
 
-  if(http->sending)
+  if(http->sending == HTTPSEND_REQUEST)
     return GETSOCK_WRITESOCK(0);
 
   return GETSOCK_READSOCK(0);
@@ -169,7 +169,7 @@ static CURLcode connect_init(struct Curl_easy *data, bool reinit)
     s = calloc(1, sizeof(struct http_connect_state));
     if(!s)
       return CURLE_OUT_OF_MEMORY;
-    infof(data, "allocate connect buffer!\n");
+    infof(data, "allocate connect buffer!");
     conn->connect_state = s;
     Curl_dyn_init(&s->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
 
@@ -202,13 +202,16 @@ static void connect_done(struct Curl_easy *data)
 {
   struct connectdata *conn = data->conn;
   struct http_connect_state *s = conn->connect_state;
-  s->tunnel_state = TUNNEL_COMPLETE;
-  Curl_dyn_free(&s->rcvbuf);
-  Curl_dyn_free(&s->req);
+  if(s->tunnel_state != TUNNEL_EXIT) {
+    s->tunnel_state = TUNNEL_EXIT;
+    Curl_dyn_free(&s->rcvbuf);
+    Curl_dyn_free(&s->req);
 
-  /* retore the protocol pointer */
-  data->req.p.http = s->prot_save;
-  infof(data, "CONNECT phase completed!\n");
+    /* retore the protocol pointer */
+    data->req.p.http = s->prot_save;
+    s->prot_save = NULL;
+    infof(data, "CONNECT phase completed!");
+  }
 }
 
 static CURLcode CONNECT_host(struct Curl_easy *data,
@@ -243,11 +246,11 @@ static CURLcode CONNECT_host(struct Curl_easy *data,
   return CURLE_OK;
 }
 
+#ifndef USE_HYPER
 static CURLcode CONNECT(struct Curl_easy *data,
                         int sockindex,
                         const char *hostname,
                         int remote_port)
-#ifndef USE_HYPER
 {
   int subversion = 0;
   struct SingleRequest *k = &data->req;
@@ -275,7 +278,7 @@ static CURLcode CONNECT(struct Curl_easy *data,
       char *hostheader = NULL;
       char *host = NULL;
 
-      infof(data, "Establish HTTP proxy tunnel to %s:%d\n",
+      infof(data, "Establish HTTP proxy tunnel to %s:%d",
             hostname, remote_port);
 
         /* This only happens if we've looped here due to authentication
@@ -297,32 +300,27 @@ static CURLcode CONNECT(struct Curl_easy *data,
                                      hostheader, TRUE);
 
       if(!result) {
-        const char *proxyconn = "";
-        const char *useragent = "";
         const char *httpv =
           (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? "1.0" : "1.1";
 
-        if(!Curl_checkProxyheaders(data, conn, "Proxy-Connection"))
-          proxyconn = "Proxy-Connection: Keep-Alive\r\n";
-
-        if(!Curl_checkProxyheaders(data, conn, "User-Agent") &&
-           data->set.str[STRING_USERAGENT])
-          useragent = data->state.aptr.uagent;
-
         result =
           Curl_dyn_addf(req,
                         "CONNECT %s HTTP/%s\r\n"
                         "%s"  /* Host: */
-                        "%s"  /* Proxy-Authorization */
-                        "%s"  /* User-Agent */
-                        "%s", /* Proxy-Connection */
+                        "%s", /* Proxy-Authorization */
                         hostheader,
                         httpv,
                         host?host:"",
                         data->state.aptr.proxyuserpwd?
-                        data->state.aptr.proxyuserpwd:"",
-                        useragent,
-                        proxyconn);
+                        data->state.aptr.proxyuserpwd:"");
+
+        if(!result && !Curl_checkProxyheaders(data, conn, "User-Agent") &&
+           data->set.str[STRING_USERAGENT])
+          result = Curl_dyn_addf(req, "User-Agent: %s\r\n",
+                                 data->set.str[STRING_USERAGENT]);
+
+        if(!result && !Curl_checkProxyheaders(data, conn, "Proxy-Connection"))
+          result = Curl_dyn_add(req, "Proxy-Connection: Keep-Alive\r\n");
 
         if(!result)
           result = Curl_add_custom_headers(data, TRUE, req);
@@ -387,6 +385,7 @@ static CURLcode CONNECT(struct Curl_easy *data,
         k->upload_fromhere += bytes_written;
         return result;
       }
+      http->sending = HTTPSEND_NADA;
       /* if nothing left to send, continue */
     }
     { /* READING RESPONSE PHASE */
@@ -416,7 +415,7 @@ static CURLcode CONNECT(struct Curl_easy *data,
             /* proxy auth was requested and there was proxy auth available,
                then deem this as "mere" proxy disconnect */
             conn->bits.proxy_connect_closed = TRUE;
-            infof(data, "Proxy CONNECT connection closed\n");
+            infof(data, "Proxy CONNECT connection closed");
           }
           else {
             error = SELECT_ERROR;
@@ -451,7 +450,7 @@ static CURLcode CONNECT(struct Curl_easy *data,
             r = Curl_httpchunk_read(data, &byte, 1, &tookcareof, &extra);
             if(r == CHUNKE_STOP) {
               /* we're done reading chunks! */
-              infof(data, "chunk reading DONE\n");
+              infof(data, "chunk reading DONE");
               s->keepon = KEEPON_DONE;
               /* we did the full CONNECT treatment, go COMPLETE */
               s->tunnel_state = TUNNEL_COMPLETE;
@@ -510,13 +509,13 @@ static CURLcode CONNECT(struct Curl_easy *data,
 
             if(s->cl) {
               infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
-                    " bytes of response-body\n", s->cl);
+                    " bytes of response-body", s->cl);
             }
             else if(s->chunked_encoding) {
               CHUNKcode r;
               CURLcode extra;
 
-              infof(data, "Ignore chunked response-body\n");
+              infof(data, "Ignore chunked response-body");
 
               /* We set ignorebody true here since the chunked decoder
                  function will acknowledge that. Pay attention so that this is
@@ -533,7 +532,7 @@ static CURLcode CONNECT(struct Curl_easy *data,
                                       &extra);
               if(r == CHUNKE_STOP) {
                 /* we're done reading chunks! */
-                infof(data, "chunk reading DONE\n");
+                infof(data, "chunk reading DONE");
                 s->keepon = KEEPON_DONE;
                 /* we did the full CONNECT treatment, go to COMPLETE */
                 s->tunnel_state = TUNNEL_COMPLETE;
@@ -579,7 +578,7 @@ static CURLcode CONNECT(struct Curl_easy *data,
             /* A client MUST ignore any Content-Length or Transfer-Encoding
                header fields received in a successful response to CONNECT.
                "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
-            infof(data, "Ignoring Content-Length in CONNECT %03d response\n",
+            infof(data, "Ignoring Content-Length in CONNECT %03d response",
                   k->httpcode);
           }
           else {
@@ -595,11 +594,11 @@ static CURLcode CONNECT(struct Curl_easy *data,
                header fields received in a successful response to CONNECT.
                "Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
             infof(data, "Ignoring Transfer-Encoding in "
-                  "CONNECT %03d response\n", k->httpcode);
+                  "CONNECT %03d response", k->httpcode);
           }
           else if(Curl_compareheader(linep,
                                      "Transfer-Encoding:", "chunked")) {
-            infof(data, "CONNECT responded chunked\n");
+            infof(data, "CONNECT responded chunked");
             s->chunked_encoding = TRUE;
             /* init our chunky engine */
             Curl_httpchunk_init(data);
@@ -657,7 +656,7 @@ static CURLcode CONNECT(struct Curl_easy *data,
   if(data->info.httpproxycode/100 != 2) {
     if(s->close_connection && data->req.newurl) {
       conn->bits.proxy_connect_closed = TRUE;
-      infof(data, "Connect me again please\n");
+      infof(data, "Connect me again please");
       connect_done(data);
     }
     else {
@@ -692,7 +691,7 @@ static CURLcode CONNECT(struct Curl_easy *data,
   data->state.authproxy.done = TRUE;
   data->state.authproxy.multipass = FALSE;
 
-  infof(data, "Proxy replied %d to CONNECT request\n",
+  infof(data, "Proxy replied %d to CONNECT request",
         data->info.httpproxycode);
   data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */
   conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the
@@ -702,6 +701,10 @@ static CURLcode CONNECT(struct Curl_easy *data,
 }
 #else
 /* The Hyper version of CONNECT */
+static CURLcode CONNECT(struct Curl_easy *data,
+                        int sockindex,
+                        const char *hostname,
+                        int remote_port)
 {
   struct connectdata *conn = data->conn;
   struct hyptransfer *h = &data->hyp;
@@ -740,6 +743,8 @@ static CURLcode CONNECT(struct Curl_easy *data,
       hyper_io_set_write(io, Curl_hyper_send);
       conn->sockfd = tunnelsocket;
 
+      data->state.hconnect = TRUE;
+
       /* create an executor to poll futures */
       if(!h->exec) {
         h->exec = hyper_executor_new();
@@ -830,16 +835,26 @@ static CURLcode CONNECT(struct Curl_easy *data,
          Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd))
         goto error;
 
-      if(data->set.str[STRING_USERAGENT] &&
-         *data->set.str[STRING_USERAGENT] &&
-         data->state.aptr.uagent &&
-         Curl_hyper_header(data, headers, data->state.aptr.uagent))
-        goto error;
+      if(!Curl_checkProxyheaders(data, conn, "User-Agent") &&
+         data->set.str[STRING_USERAGENT]) {
+        struct dynbuf ua;
+        Curl_dyn_init(&ua, DYN_HTTP_REQUEST);
+        result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n",
+                               data->set.str[STRING_USERAGENT]);
+        if(result)
+          goto error;
+        if(Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua)))
+          goto error;
+        Curl_dyn_free(&ua);
+      }
 
       if(!Curl_checkProxyheaders(data, conn, "Proxy-Connection") &&
          Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive"))
         goto error;
 
+      if(Curl_add_custom_headers(data, TRUE, headers))
+        goto error;
+
       sendtask = hyper_clientconn_send(client, req);
       if(!sendtask) {
         failf(data, "hyper_clientconn_send");
@@ -875,7 +890,6 @@ static CURLcode CONNECT(struct Curl_easy *data,
         goto error;
       if(!done)
         break;
-      fprintf(stderr, "done\n");
       s->tunnel_state = TUNNEL_COMPLETE;
       if(h->exec) {
         hyper_executor_free(h->exec);
@@ -897,6 +911,33 @@ static CURLcode CONNECT(struct Curl_easy *data,
   } while(data->req.newurl);
 
   result = CURLE_OK;
+  if(s->tunnel_state == TUNNEL_COMPLETE) {
+    data->info.httpproxycode = data->req.httpcode;
+    if(data->info.httpproxycode/100 != 2) {
+      if(conn->bits.close && data->req.newurl) {
+        conn->bits.proxy_connect_closed = TRUE;
+        infof(data, "Connect me again please");
+        connect_done(data);
+      }
+      else {
+        free(data->req.newurl);
+        data->req.newurl = NULL;
+        /* failure, close this connection to avoid re-use */
+        streamclose(conn, "proxy CONNECT failure");
+        Curl_closesocket(data, conn, conn->sock[sockindex]);
+        conn->sock[sockindex] = CURL_SOCKET_BAD;
+      }
+
+      /* to back to init state */
+      s->tunnel_state = TUNNEL_INIT;
+
+      if(!conn->bits.proxy_connect_closed) {
+        failf(data, "Received HTTP code %d from proxy after CONNECT",
+              data->req.httpcode);
+        result = CURLE_RECV_ERROR;
+      }
+    }
+  }
   error:
   free(host);
   free(hostheader);
@@ -917,7 +958,6 @@ static CURLcode CONNECT(struct Curl_easy *data,
   }
   return result;
 }
-
 #endif
 
 void Curl_connect_free(struct Curl_easy *data)

+ 4 - 3
lib/http_proxy.h

@@ -65,9 +65,10 @@ struct http_connect_state {
   } keepon;
   curl_off_t cl; /* size of content to read and ignore */
   enum {
-    TUNNEL_INIT,    /* init/default/no tunnel state */
-    TUNNEL_CONNECT, /* CONNECT has been sent off */
-    TUNNEL_COMPLETE /* CONNECT response received completely */
+    TUNNEL_INIT,     /* init/default/no tunnel state */
+    TUNNEL_CONNECT,  /* CONNECT has been sent off */
+    TUNNEL_COMPLETE, /* CONNECT response received completely */
+    TUNNEL_EXIT
   } tunnel_state;
   BIT(chunked_encoding);
   BIT(close_connection);

+ 27 - 28
lib/imap.c

@@ -74,7 +74,6 @@
 #include "strcase.h"
 #include "vtls/vtls.h"
 #include "connect.h"
-#include "strerror.h"
 #include "select.h"
 #include "multiif.h"
 #include "url.h"
@@ -414,7 +413,7 @@ static void state(struct Curl_easy *data, imapstate newstate)
   };
 
   if(imapc->state != newstate)
-    infof(data, "IMAP %p state change from %s to %s\n",
+    infof(data, "IMAP %p state change from %s to %s",
           (void *)imapc, names[imapc->state], names[newstate]);
 #endif
 
@@ -475,8 +474,8 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
 {
   /* Start the SSL connection */
   struct imap_conn *imapc = &conn->proto.imapc;
-  CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FIRSTSOCKET,
-                                                 &imapc->ssldone);
+  CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
+                                                 FIRSTSOCKET, &imapc->ssldone);
 
   if(!result) {
     if(imapc->state != IMAP_UPGRADETLS)
@@ -606,7 +605,7 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data,
       result = imap_perform_login(data, conn);
     else {
       /* Other mechanisms not supported */
-      infof(data, "No known authentication mechanisms supported!\n");
+      infof(data, "No known authentication mechanisms supported!");
       result = CURLE_LOGIN_DENIED;
     }
   }
@@ -861,7 +860,7 @@ static CURLcode imap_state_servergreet_resp(struct Curl_easy *data,
     /* PREAUTH */
     struct imap_conn *imapc = &conn->proto.imapc;
     imapc->preauth = TRUE;
-    infof(data, "PREAUTH connection, already authenticated!\n");
+    infof(data, "PREAUTH connection, already authenticated!");
   }
   else if(imapcode != IMAP_RESP_OK) {
     failf(data, "Got unexpected imap-server response");
@@ -935,22 +934,18 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data,
       line += wordlen;
     }
   }
-  else if(imapcode == IMAP_RESP_OK) {
-    if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
-      /* We don't have a SSL/TLS connection yet, but SSL is requested */
-      if(imapc->tls_supported)
-        /* Switch to TLS connection now */
-        result = imap_perform_starttls(data, conn);
-      else if(data->set.use_ssl == CURLUSESSL_TRY)
-        /* Fallback and carry on with authentication */
-        result = imap_perform_authentication(data, conn);
-      else {
-        failf(data, "STARTTLS not supported.");
-        result = CURLE_USE_SSL_FAILED;
-      }
+  else if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
+    /* PREAUTH is not compatible with STARTTLS. */
+    if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) {
+      /* Switch to TLS connection now */
+      result = imap_perform_starttls(data, conn);
     }
-    else
+    else if(data->set.use_ssl <= CURLUSESSL_TRY)
       result = imap_perform_authentication(data, conn);
+    else {
+      failf(data, "STARTTLS not available.");
+      result = CURLE_USE_SSL_FAILED;
+    }
   }
   else
     result = imap_perform_authentication(data, conn);
@@ -968,6 +963,10 @@ static CURLcode imap_state_starttls_resp(struct Curl_easy *data,
 
   (void)instate; /* no use for this yet */
 
+  /* Pipelining in response is forbidden. */
+  if(data->conn->proto.imapc.pp.cache_size)
+    return CURLE_WEIRD_SERVER_REPLY;
+
   if(imapcode != IMAP_RESP_OK) {
     if(data->set.use_ssl != CURLUSESSL_TRY) {
       failf(data, "STARTTLS denied");
@@ -1143,7 +1142,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
   }
 
   if(parsed) {
-    infof(data, "Found %" CURL_FORMAT_CURL_OFF_T " bytes to download\n",
+    infof(data, "Found %" CURL_FORMAT_CURL_OFF_T " bytes to download",
           size);
     Curl_pgrsSetDownloadSize(data, size);
 
@@ -1169,7 +1168,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
       data->req.bytecount += chunk;
 
       infof(data, "Written %zu bytes, %" CURL_FORMAT_CURL_OFF_TU
-            " bytes are left for transfer\n", chunk, size - chunk);
+            " bytes are left for transfer", chunk, size - chunk);
 
       /* Have we used the entire cache or just part of it?*/
       if(pp->cache_size > chunk) {
@@ -1369,7 +1368,7 @@ static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done)
   struct imap_conn *imapc = &conn->proto.imapc;
 
   if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
-    result = Curl_ssl_connect_nonblocking(data, conn,
+    result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
                                           FIRSTSOCKET, &imapc->ssldone);
     if(result || !imapc->ssldone)
       return result;
@@ -1543,7 +1542,7 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected,
   struct imap_conn *imapc = &conn->proto.imapc;
   bool selected = FALSE;
 
-  DEBUGF(infof(data, "DO phase starts\n"));
+  DEBUGF(infof(data, "DO phase starts"));
 
   if(data->set.opt_no_body) {
     /* Requested no body means no transfer */
@@ -1590,7 +1589,7 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected,
   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
 
   if(*dophase_done)
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
 
   return result;
 }
@@ -1682,11 +1681,11 @@ static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done)
   CURLcode result = imap_multi_statemach(data, dophase_done);
 
   if(result)
-    DEBUGF(infof(data, "DO phase failed\n"));
+    DEBUGF(infof(data, "DO phase failed"));
   else if(*dophase_done) {
     result = imap_dophase_done(data, FALSE /* not connected */);
 
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
 
   return result;
@@ -2017,7 +2016,7 @@ static CURLcode imap_parse_url_path(struct Curl_easy *data)
       return result;
     }
 
-    DEBUGF(infof(data, "IMAP URL parameter '%s' = '%s'\n", name, value));
+    DEBUGF(infof(data, "IMAP URL parameter '%s' = '%s'", name, value));
 
     /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
        PARTIAL) stripping of the trailing slash character if it is present.

+ 1 - 1
lib/inet_ntop.c

@@ -40,7 +40,7 @@
 #define INT16SZ          2
 
 /*
- * Format an IPv4 address, more or less like inet_ntoa().
+ * Format an IPv4 address, more or less like inet_ntop().
  *
  * Returns `dst' (as a const)
  * Note:

+ 11 - 12
lib/krb5.c

@@ -263,7 +263,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);
-    Curl_infof(data, "Trying against %s\n", output_buffer.value);
+    infof(data, "Trying against %s", output_buffer.value);
     gssresp = GSS_C_NO_BUFFER;
     *context = GSS_C_NO_CONTEXT;
 
@@ -290,7 +290,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
       }
 
       if(GSS_ERROR(maj)) {
-        Curl_infof(data, "Error creating security context\n");
+        infof(data, "Error creating security context");
         ret = AUTH_ERROR;
         break;
       }
@@ -301,8 +301,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
         result = Curl_base64_encode(data, (char *)output_buffer.value,
                                     output_buffer.length, &p, &base64_sz);
         if(result) {
-          Curl_infof(data, "base64-encoding: %s\n",
-                     curl_easy_strerror(result));
+          infof(data, "base64-encoding: %s", curl_easy_strerror(result));
           ret = AUTH_ERROR;
           break;
         }
@@ -327,7 +326,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
         }
 
         if(data->state.buffer[0] != '2' && data->state.buffer[0] != '3') {
-          Curl_infof(data, "Server didn't accept auth data\n");
+          infof(data, "Server didn't accept auth data");
           ret = AUTH_ERROR;
           break;
         }
@@ -629,7 +628,7 @@ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn,
 
       socket_write(data, fd, cmd_buffer, cmd_size);
       socket_write(data, fd, "\r\n", 2);
-      infof(data, "Send: %s%s\n", prot_level == PROT_PRIVATE?enc:mic,
+      infof(data, "Send: %s%s", prot_level == PROT_PRIVATE?enc:mic,
             cmd_buffer);
       free(cmd_buffer);
     }
@@ -738,7 +737,7 @@ static int sec_set_protection_level(struct Curl_easy *data)
 
   if(!conn->sec_complete) {
     infof(data, "Trying to change the protection level after the"
-                " completion of the data exchange.\n");
+                " completion of the data exchange.");
     return -1;
   }
 
@@ -815,13 +814,13 @@ static CURLcode choose_mech(struct Curl_easy *data, struct connectdata *conn)
   if(mech->init) {
     ret = mech->init(conn->app_data);
     if(ret) {
-      infof(data, "Failed initialization for %s. Skipping it.\n",
+      infof(data, "Failed initialization for %s. Skipping it.",
             mech->name);
       return CURLE_FAILED_INIT;
     }
   }
 
-  infof(data, "Trying mechanism %s...\n", mech->name);
+  infof(data, "Trying mechanism %s...", mech->name);
   ret = ftp_send_command(data, "AUTH %s", mech->name);
   if(ret < 0)
     return CURLE_COULDNT_CONNECT;
@@ -830,15 +829,15 @@ static CURLcode choose_mech(struct Curl_easy *data, struct connectdata *conn)
     switch(ret) {
     case 504:
       infof(data, "Mechanism %s is not supported by the server (server "
-            "returned ftp code: 504).\n", mech->name);
+            "returned ftp code: 504).", mech->name);
       break;
     case 534:
       infof(data, "Mechanism %s was rejected by the server (server returned "
-            "ftp code: 534).\n", mech->name);
+            "ftp code: 534).", mech->name);
       break;
     default:
       if(ret/100 == 5) {
-        infof(data, "server does not support the security extensions\n");
+        infof(data, "server does not support the security extensions");
         return CURLE_USE_SSL_FAILED;
       }
       break;

+ 10 - 10
lib/ldap.c

@@ -296,9 +296,9 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
   char *passwd = NULL;
 
   *done = TRUE; /* unconditionally */
-  infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d\n",
+  infof(data, "LDAP local: LDAP Vendor = %s ; LDAP Version = %d",
           LDAP_VENDOR_NAME, LDAP_VENDOR_VERSION);
-  infof(data, "LDAP local: %s\n", data->state.url);
+  infof(data, "LDAP local: %s", data->state.url);
 
 #ifdef HAVE_LDAP_URL_PARSE
   rc = ldap_url_parse(data->state.url, &ludp);
@@ -314,7 +314,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
   /* Get the URL scheme (either ldap or ldaps) */
   if(conn->given->flags & PROTOPT_SSL)
     ldap_ssl = 1;
-  infof(data, "LDAP local: trying to establish %s connection\n",
+  infof(data, "LDAP local: trying to establish %s connection",
           ldap_ssl ? "encrypted" : "cleartext");
 
 #if defined(USE_WIN32_LDAP)
@@ -366,14 +366,14 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
         result = CURLE_SSL_CERTPROBLEM;
         goto quit;
       }
-      infof(data, "LDAP local: using %s CA cert '%s'\n",
-              (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
-              ldap_ca);
+      infof(data, "LDAP local: using %s CA cert '%s'",
+            (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
+            ldap_ca);
       rc = ldapssl_add_trusted_cert(ldap_ca, cert_type);
       if(rc != LDAP_SUCCESS) {
         failf(data, "LDAP local: ERROR setting %s CA cert: %s",
-                (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
-                ldap_err2string(rc));
+              (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
+              ldap_err2string(rc));
         result = CURLE_SSL_CERTPROBLEM;
         goto quit;
       }
@@ -409,7 +409,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
         result = CURLE_SSL_CERTPROBLEM;
         goto quit;
       }
-      infof(data, "LDAP local: using PEM CA cert: %s\n", ldap_ca);
+      infof(data, "LDAP local: using PEM CA cert: %s", ldap_ca);
       rc = ldap_set_option(NULL, LDAP_OPT_X_TLS_CACERTFILE, ldap_ca);
       if(rc != LDAP_SUCCESS) {
         failf(data, "LDAP local: ERROR setting PEM CA cert: %s",
@@ -718,7 +718,7 @@ quit:
     LDAP_TRACE(("Received %d entries\n", num));
   }
   if(rc == LDAP_SIZELIMIT_EXCEEDED)
-    infof(data, "There are more than %d entries\n", num);
+    infof(data, "There are more than %d entries", num);
   if(ludp)
     ldap_free_urldesc(ludp);
   if(server)

+ 5 - 1
lib/md4.c

@@ -36,8 +36,12 @@
 #endif /* USE_OPENSSL */
 
 #ifdef USE_MBEDTLS
-#include <mbedtls/config.h>
 #include <mbedtls/version.h>
+#if MBEDTLS_VERSION_NUMBER >= 0x03000000
+#include <mbedtls/mbedtls_config.h>
+#else
+#include <mbedtls/config.h>
+#endif
 
 #if(MBEDTLS_VERSION_NUMBER >= 0x02070000)
   #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS

+ 5 - 4
lib/md5.c

@@ -33,7 +33,8 @@
 #ifdef USE_MBEDTLS
 #include <mbedtls/version.h>
 
-#if(MBEDTLS_VERSION_NUMBER >= 0x02070000)
+#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) && \
+   (MBEDTLS_VERSION_NUMBER < 0x03000000)
   #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS
 #endif
 #endif /* USE_MBEDTLS */
@@ -85,7 +86,7 @@ typedef mbedtls_md5_context MD5_CTX;
 static void MD5_Init(MD5_CTX *ctx)
 {
 #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
-  mbedtls_md5_starts(ctx);
+  (void) mbedtls_md5_starts(ctx);
 #else
   (void) mbedtls_md5_starts_ret(ctx);
 #endif
@@ -96,7 +97,7 @@ static void MD5_Update(MD5_CTX *ctx,
                        unsigned int length)
 {
 #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
-  mbedtls_md5_update(ctx, data, length);
+  (void) mbedtls_md5_update(ctx, data, length);
 #else
   (void) mbedtls_md5_update_ret(ctx, data, length);
 #endif
@@ -105,7 +106,7 @@ static void MD5_Update(MD5_CTX *ctx,
 static void MD5_Final(unsigned char *digest, MD5_CTX *ctx)
 {
 #if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
-  mbedtls_md5_finish(ctx, digest);
+  (void) mbedtls_md5_finish(ctx, digest);
 #else
   (void) mbedtls_md5_finish_ret(ctx, digest);
 #endif

+ 3 - 1
lib/mprintf.c

@@ -1017,9 +1017,11 @@ int curl_mvsnprintf(char *buffer, size_t maxlength, const char *format,
   retcode = dprintf_formatf(&info, addbyter, format, ap_save);
   if((retcode != -1) && info.max) {
     /* we terminate this with a zero byte */
-    if(info.max == info.length)
+    if(info.max == info.length) {
       /* we're at maximum, scrap the last letter */
       info.buffer[-1] = 0;
+      retcode--; /* don't count the nul byte */
+    }
     else
       info.buffer[0] = 0;
   }

+ 207 - 51
lib/mqtt.c

@@ -128,6 +128,10 @@ static CURLcode mqtt_send(struct Curl_easy *data,
     mq->sendleftovers = sendleftovers;
     mq->nsend = nsend;
   }
+  else {
+    mq->sendleftovers = NULL;
+    mq->nsend = 0;
+  }
   return result;
 }
 
@@ -143,32 +147,197 @@ static int mqtt_getsock(struct Curl_easy *data,
   return GETSOCK_READSOCK(FIRSTSOCKET);
 }
 
+static int mqtt_encode_len(char *buf, size_t len)
+{
+  unsigned char encoded;
+  int i;
+
+  for(i = 0; (len > 0) && (i<4); i++) {
+    encoded = len % 0x80;
+    len /= 0x80;
+    if(len)
+      encoded |= 0x80;
+    buf[i] = encoded;
+  }
+
+  return i;
+}
+
+/* add the passwd to the CONNECT packet */
+static int add_passwd(const char *passwd, const size_t plen,
+                       char *pkt, const size_t start, int remain_pos)
+{
+  /* magic number that need to be set properly */
+  const size_t conn_flags_pos = remain_pos + 8;
+  if(plen > 0xffff)
+    return 1;
+
+  /* set password flag */
+  pkt[conn_flags_pos] |= 0x40;
+
+  /* length of password provided */
+  pkt[start] = (char)((plen >> 8) & 0xFF);
+  pkt[start + 1] = (char)(plen & 0xFF);
+  memcpy(&pkt[start + 2], passwd, plen);
+  return 0;
+}
+
+/* add user to the CONN packet */
+static int add_user(const char *username, const size_t ulen,
+                    unsigned char *pkt, const size_t start, int remain_pos)
+{
+  /* magic number that need to be set properly */
+  const size_t conn_flags_pos = remain_pos + 8;
+  if(ulen > 0xffff)
+    return 1;
+
+  /* set username flag */
+  pkt[conn_flags_pos] |= 0x80;
+  /* length of username provided */
+  pkt[start] = (unsigned char)((ulen >> 8) & 0xFF);
+  pkt[start + 1] = (unsigned char)(ulen & 0xFF);
+  memcpy(&pkt[start + 2], username, ulen);
+  return 0;
+}
+
+/* add client ID to the CONN packet */
+static int add_client_id(const char *client_id, const size_t client_id_len,
+                         char *pkt, const size_t start)
+{
+  if(client_id_len != MQTT_CLIENTID_LEN)
+    return 1;
+  pkt[start] = 0x00;
+  pkt[start + 1] = MQTT_CLIENTID_LEN;
+  memcpy(&pkt[start + 2], client_id, MQTT_CLIENTID_LEN);
+  return 0;
+}
+
+/* Set initial values of CONN packet */
+static int init_connpack(char *packet, char *remain, int remain_pos)
+{
+  /* Fixed header starts */
+  /* packet type */
+  packet[0] = MQTT_MSG_CONNECT;
+  /* remaining length field */
+  memcpy(&packet[1], remain, remain_pos);
+  /* Fixed header ends */
+
+  /* Variable header starts */
+  /* protocol length */
+  packet[remain_pos + 1] = 0x00;
+  packet[remain_pos + 2] = 0x04;
+  /* protocol name */
+  packet[remain_pos + 3] = 'M';
+  packet[remain_pos + 4] = 'Q';
+  packet[remain_pos + 5] = 'T';
+  packet[remain_pos + 6] = 'T';
+  /* protocol level */
+  packet[remain_pos + 7] = 0x04;
+  /* CONNECT flag: CleanSession */
+  packet[remain_pos + 8] = 0x02;
+  /* keep-alive 0 = disabled */
+  packet[remain_pos + 9] = 0x00;
+  packet[remain_pos + 10] = 0x3c;
+  /*end of variable header*/
+  return remain_pos + 10;
+}
+
 static CURLcode mqtt_connect(struct Curl_easy *data)
 {
   CURLcode result = CURLE_OK;
-  const size_t client_id_offset = 14;
-  const size_t packetlen = client_id_offset + MQTT_CLIENTID_LEN;
+  int pos = 0;
+  int rc = 0;
+  /*remain length*/
+  int remain_pos = 0;
+  char remain[4] = {0};
+  size_t packetlen = 0;
+  size_t payloadlen = 0;
+  size_t start_user = 0;
+  size_t start_pwd = 0;
   char client_id[MQTT_CLIENTID_LEN + 1] = "curl";
   const size_t clen = strlen("curl");
-  char packet[32] = {
-    MQTT_MSG_CONNECT,  /* packet type */
-    0x00,              /* remaining length */
-    0x00, 0x04,        /* protocol length */
-    'M','Q','T','T',   /* protocol name */
-    0x04,              /* protocol level */
-    0x02,              /* CONNECT flag: CleanSession */
-    0x00, 0x3c,        /* keep-alive 0 = disabled */
-    0x00, 0x00         /* payload1 length */
-  };
-  packet[1] = (packetlen - 2) & 0x7f;
-  packet[client_id_offset - 1] = MQTT_CLIENTID_LEN;
+  char *packet = NULL;
+
+  /* extracting username from request */
+  const char *username = data->state.aptr.user ?
+    data->state.aptr.user : "";
+  const size_t ulen = strlen(username);
+  /* extracting password from request */
+  const char *passwd = data->state.aptr.passwd ?
+    data->state.aptr.passwd : "";
+  const size_t plen = strlen(passwd);
+
+  payloadlen = ulen + plen + MQTT_CLIENTID_LEN + 2;
+  /* The plus 2 are for the MSB and LSB describing the length of the string to
+   * be added on the payload. Refer to spec 1.5.2 and 1.5.4 */
+  if(ulen)
+    payloadlen += 2;
+  if(plen)
+    payloadlen += 2;
+
+  /* getting how much occupy the remain length */
+  remain_pos = mqtt_encode_len(remain, payloadlen + 10);
+
+  /* 10 length of variable header and 1 the first byte of the fixed header */
+  packetlen = payloadlen + 10 + remain_pos + 1;
+
+  /* allocating packet */
+  if(packetlen > 268435455)
+    return CURLE_WEIRD_SERVER_REPLY;
+  packet = malloc(packetlen);
+  if(!packet)
+    return CURLE_OUT_OF_MEMORY;
+  memset(packet, 0, packetlen);
+
+  /* set initial values for CONN pack */
+  pos = init_connpack(packet, remain, remain_pos);
 
   result = Curl_rand_hex(data, (unsigned char *)&client_id[clen],
                          MQTT_CLIENTID_LEN - clen + 1);
-  memcpy(&packet[client_id_offset], client_id, MQTT_CLIENTID_LEN);
-  infof(data, "Using client id '%s'\n", client_id);
+  /* add client id */
+  rc = add_client_id(client_id, strlen(client_id), packet, pos + 1);
+  if(rc) {
+    failf(data, "Client ID length mismatched: [%lu]", strlen(client_id));
+    result = CURLE_WEIRD_SERVER_REPLY;
+    goto end;
+  }
+  infof(data, "Using client id '%s'", client_id);
+
+  /* position where starts the user payload */
+  start_user = pos + 3 + MQTT_CLIENTID_LEN;
+  /* position where starts the password payload */
+  start_pwd = start_user + ulen;
+  /* if user name was provided, add it to the packet */
+  if(ulen) {
+    start_pwd += 2;
+
+    rc = add_user(username, ulen,
+                  (unsigned char *)packet, start_user, remain_pos);
+    if(rc) {
+      failf(data, "Username is too large: [%lu]", ulen);
+      result = CURLE_WEIRD_SERVER_REPLY;
+      goto end;
+    }
+  }
+
+  /* if passwd was provided, add it to the packet */
+  if(plen) {
+    rc = add_passwd(passwd, plen, packet, start_pwd, remain_pos);
+    if(rc) {
+      failf(data, "Password is too large: [%lu]", plen);
+      result = CURLE_WEIRD_SERVER_REPLY;
+      goto end;
+    }
+  }
+
   if(!result)
     result = mqtt_send(data, packet, packetlen);
+
+end:
+  if(packet)
+    free(packet);
+  Curl_safefree(data->state.aptr.user);
+  Curl_safefree(data->state.aptr.passwd);
   return result;
 }
 
@@ -213,35 +382,12 @@ fail:
 static CURLcode mqtt_get_topic(struct Curl_easy *data,
                                char **topic, size_t *topiclen)
 {
-  CURLcode result = CURLE_OK;
   char *path = data->state.up.path;
-
-  if(strlen(path) > 1) {
-    result = Curl_urldecode(data, path + 1, 0, topic, topiclen,
-                            REJECT_NADA);
-  }
-  else {
-    failf(data, "Error: No topic specified.");
-    result = CURLE_URL_MALFORMAT;
-  }
-  return result;
-}
-
-
-static int mqtt_encode_len(char *buf, size_t len)
-{
-  unsigned char encoded;
-  int i;
-
-  for(i = 0; (len > 0) && (i<4); i++) {
-    encoded = len % 0x80;
-    len /= 0x80;
-    if(len)
-      encoded |= 0x80;
-    buf[i] = encoded;
-  }
-
-  return i;
+  if(strlen(path) > 1)
+    return Curl_urldecode(data, path + 1, 0, topic, topiclen,
+                          REJECT_NADA);
+  failf(data, "No MQTT topic found. Forgot to URL encode it?");
+  return CURLE_URL_MALFORMAT;
 }
 
 static CURLcode mqtt_subscribe(struct Curl_easy *data)
@@ -418,7 +564,7 @@ static void mqstate(struct Curl_easy *data,
   struct connectdata *conn = data->conn;
   struct mqtt_conn *mqtt = &conn->proto.mqtt;
 #ifdef CURLDEBUG
-  infof(data, "%s (from %s) (next is %s)\n",
+  infof(data, "%s (from %s) (next is %s)",
         statenames[state],
         statenames[mqtt->state],
         (state == MQTT_FIRST)? statenames[nextstate] : "");
@@ -465,7 +611,7 @@ static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done)
       goto MQTT_SUBACK_COMING;
     }
     else if(packet == MQTT_MSG_DISCONNECT) {
-      infof(data, "Got DISCONNECT\n");
+      infof(data, "Got DISCONNECT");
       *done = TRUE;
       goto end;
     }
@@ -476,7 +622,13 @@ static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done)
 
     /* -- switched state -- */
     remlen = mq->remaining_length;
-    infof(data, "Remaining length: %zd bytes\n", remlen);
+    infof(data, "Remaining length: %zd bytes", remlen);
+    if(data->set.max_filesize &&
+       (curl_off_t)remlen > data->set.max_filesize) {
+      failf(data, "Maximum file size exceeded");
+      result = CURLE_FILESIZE_EXCEEDED;
+      goto end;
+    }
     Curl_pgrsSetDownloadSize(data, remlen);
     data->req.bytecount = 0;
     data->req.size = remlen;
@@ -491,12 +643,12 @@ static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done)
     result = Curl_read(data, sockfd, (char *)pkt, rest, &nread);
     if(result) {
       if(CURLE_AGAIN == result) {
-        infof(data, "EEEE AAAAGAIN\n");
+        infof(data, "EEEE AAAAGAIN");
       }
       goto end;
     }
     if(!nread) {
-      infof(data, "server disconnected\n");
+      infof(data, "server disconnected");
       result = CURLE_PARTIAL_FILE;
       goto end;
     }
@@ -562,7 +714,7 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
       return result;
   }
 
-  infof(data, "mqtt_doing: state [%d]\n", (int) mqtt->state);
+  infof(data, "mqtt_doing: state [%d]", (int) mqtt->state);
   switch(mqtt->state) {
   case MQTT_FIRST:
     /* Read the initial byte only */
@@ -582,6 +734,10 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
       Curl_debug(data, CURLINFO_HEADER_IN, (char *)&byte, 1);
       pkt[mq->npacket++] = byte;
     } while((byte & 0x80) && (mq->npacket < 4));
+    if(nread && (byte & 0x80))
+      /* MQTT supports up to 127 * 128^0 + 127 * 128^1 + 127 * 128^2 +
+         127 * 128^3 bytes. server tried to send more */
+      result = CURLE_WEIRD_SERVER_REPLY;
     if(result)
       break;
     mq->remaining_length = mqtt_decode_len(&pkt[0], mq->npacket, NULL);
@@ -593,7 +749,7 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
     mqstate(data, MQTT_FIRST, MQTT_FIRST);
 
     if(mq->firstbyte == MQTT_MSG_DISCONNECT) {
-      infof(data, "Got DISCONNECT\n");
+      infof(data, "Got DISCONNECT");
       *done = TRUE;
     }
     break;

+ 97 - 55
lib/multi.c

@@ -169,7 +169,7 @@ static void mstate(struct Curl_easy *data, CURLMstate state
       connection_id = data->conn->connection_id;
 
     infof(data,
-          "STATE: %s => %s handle %p; line %d (connection #%ld)\n",
+          "STATE: %s => %s handle %p; line %d (connection #%ld)",
           statename[oldstate], statename[data->mstate],
           (void *)data, lineno, connection_id);
   }
@@ -562,7 +562,7 @@ static CURLcode multi_done(struct Curl_easy *data,
   struct connectdata *conn = data->conn;
   unsigned int i;
 
-  DEBUGF(infof(data, "multi_done\n"));
+  DEBUGF(infof(data, "multi_done"));
 
   if(data->state.done)
     /* Stop if multi_done() has already been called */
@@ -610,7 +610,7 @@ static CURLcode multi_done(struct Curl_easy *data,
     /* Stop if still used. */
     CONNCACHE_UNLOCK(data);
     DEBUGF(infof(data, "Connection still in use %zu, "
-                 "no more multi_done now!\n",
+                 "no more multi_done now!",
                  conn->easyq.size));
     return CURLE_OK;
   }
@@ -687,7 +687,7 @@ static CURLcode multi_done(struct Curl_easy *data,
     if(Curl_conncache_return_conn(data, conn)) {
       /* remember the most recently used connection */
       data->state.lastconnect_id = conn->connection_id;
-      infof(data, "%s\n", buffer);
+      infof(data, "%s", buffer);
     }
     else
       data->state.lastconnect_id = -1;
@@ -709,7 +709,6 @@ static int close_connect_only(struct Curl_easy *data,
     return 1;
 
   connclose(conn, "Removing connect-only easy handle");
-  conn->bits.connect_only = FALSE;
 
   return 1;
 }
@@ -1043,7 +1042,12 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi,
 
   data = multi->easyp;
   while(data) {
-    int bitmap = multi_getsock(data, sockbunch);
+    int bitmap;
+#ifdef __clang_analyzer_
+    /* to prevent "The left operand of '>=' is a garbage value" warnings */
+    memset(sockbunch, 0, sizeof(sockbunch));
+#endif
+    bitmap = multi_getsock(data, sockbunch);
 
     for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
       curl_socket_t s = CURL_SOCKET_BAD;
@@ -1096,6 +1100,9 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
   WSANETWORKEVENTS wsa_events;
   DEBUGASSERT(multi->wsa_event != WSA_INVALID_EVENT);
 #endif
+#ifndef ENABLE_WAKEUP
+  (void)use_wakeup;
+#endif
 
   if(!GOOD_MULTI_HANDLE(multi))
     return CURLM_BAD_HANDLE;
@@ -1176,7 +1183,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
 #ifdef USE_WINSOCK
         long mask = 0;
 #endif
-        if(bitmap & GETSOCK_READSOCK(i)) {
+        if((bitmap & GETSOCK_READSOCK(i)) && VALID_SOCK((sockbunch[i]))) {
           s = sockbunch[i];
 #ifdef USE_WINSOCK
           mask |= FD_READ|FD_ACCEPT|FD_CLOSE;
@@ -1185,7 +1192,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
           ufds[nfds].events = POLLIN;
           ++nfds;
         }
-        if(bitmap & GETSOCK_WRITESOCK(i)) {
+        if((bitmap & GETSOCK_WRITESOCK(i)) && VALID_SOCK((sockbunch[i]))) {
           s = sockbunch[i];
 #ifdef USE_WINSOCK
           mask |= FD_WRITE|FD_CONNECT|FD_CLOSE;
@@ -1539,6 +1546,58 @@ static CURLcode multi_do_more(struct Curl_easy *data, int *complete)
   return result;
 }
 
+/*
+ * Check whether a timeout occurred, and handle it if it did
+ */
+static bool multi_handle_timeout(struct Curl_easy *data,
+                                 struct curltime *now,
+                                 bool *stream_error,
+                                 CURLcode *result,
+                                 bool connect_timeout)
+{
+  timediff_t timeout_ms;
+  timeout_ms = Curl_timeleft(data, now, connect_timeout);
+
+  if(timeout_ms < 0) {
+    /* Handle timed out */
+    if(data->mstate == MSTATE_RESOLVING)
+      failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T
+            " milliseconds",
+            Curl_timediff(*now, data->progress.t_startsingle));
+    else if(data->mstate == MSTATE_CONNECTING)
+      failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T
+            " milliseconds",
+            Curl_timediff(*now, data->progress.t_startsingle));
+    else {
+      struct SingleRequest *k = &data->req;
+      if(k->size != -1) {
+        failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
+              " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %"
+              CURL_FORMAT_CURL_OFF_T " bytes received",
+              Curl_timediff(*now, data->progress.t_startsingle),
+              k->bytecount, k->size);
+      }
+      else {
+        failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
+              " milliseconds with %" CURL_FORMAT_CURL_OFF_T
+              " bytes received",
+              Curl_timediff(*now, data->progress.t_startsingle),
+              k->bytecount);
+      }
+    }
+
+    /* Force connection closed if the connection has indeed been used */
+    if(data->mstate > MSTATE_DO) {
+      streamclose(data->conn, "Disconnected with pending data");
+      *stream_error = TRUE;
+    }
+    *result = CURLE_OPERATION_TIMEDOUT;
+    (void)multi_done(data, *result, TRUE);
+  }
+
+  return (timeout_ms < 0);
+}
+
 /*
  * We are doing protocol-specific connecting and this is being called over and
  * over from the multi interface until the connection phase is done on
@@ -1670,7 +1729,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
   bool done = FALSE;
   CURLMcode rc;
   CURLcode result = CURLE_OK;
-  timediff_t timeout_ms;
   timediff_t recv_timeout_ms;
   timediff_t send_timeout_ms;
   int control;
@@ -1685,7 +1743,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
     rc = CURLM_OK;
 
     if(multi_ischanged(multi, TRUE)) {
-      DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue!\n"));
+      DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue!"));
       process_pending_handles(multi); /* multiplexed */
     }
 
@@ -1700,47 +1758,16 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
     if(data->conn &&
        (data->mstate >= MSTATE_CONNECT) &&
        (data->mstate < MSTATE_COMPLETED)) {
+      /* Check for overall operation timeout here but defer handling the
+       * connection timeout to later, to allow for a connection to be set up
+       * in the window since we last checked timeout. This prevents us
+       * tearing down a completed connection in the case where we were slow
+       * to check the timeout (e.g. process descheduled during this loop).
+       * We set connect_timeout=FALSE to do this. */
+
       /* we need to wait for the connect state as only then is the start time
          stored, but we must not check already completed handles */
-      timeout_ms = Curl_timeleft(data, nowp,
-                                 (data->mstate <= MSTATE_DO)?
-                                 TRUE:FALSE);
-
-      if(timeout_ms < 0) {
-        /* Handle timed out */
-        if(data->mstate == MSTATE_RESOLVING)
-          failf(data, "Resolving timed out after %" CURL_FORMAT_TIMEDIFF_T
-                " milliseconds",
-                Curl_timediff(*nowp, data->progress.t_startsingle));
-        else if(data->mstate == MSTATE_CONNECTING)
-          failf(data, "Connection timed out after %" CURL_FORMAT_TIMEDIFF_T
-                " milliseconds",
-                Curl_timediff(*nowp, data->progress.t_startsingle));
-        else {
-          struct SingleRequest *k = &data->req;
-          if(k->size != -1) {
-            failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
-                  " milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %"
-                  CURL_FORMAT_CURL_OFF_T " bytes received",
-                  Curl_timediff(*nowp, data->progress.t_startsingle),
-                  k->bytecount, k->size);
-          }
-          else {
-            failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
-                  " milliseconds with %" CURL_FORMAT_CURL_OFF_T
-                  " bytes received",
-                  Curl_timediff(*nowp, data->progress.t_startsingle),
-                  k->bytecount);
-          }
-        }
-
-        /* Force connection closed if the connection has indeed been used */
-        if(data->mstate > MSTATE_DO) {
-          streamclose(data->conn, "Disconnected with pending data");
-          stream_error = TRUE;
-        }
-        result = CURLE_OPERATION_TIMEDOUT;
-        (void)multi_done(data, result, TRUE);
+      if(multi_handle_timeout(data, nowp, &stream_error, &result, FALSE)) {
         /* Skip the statemachine and go directly to error handling section. */
         goto statemachine_end;
       }
@@ -1792,7 +1819,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       }
       else if(data->state.previouslypending) {
         /* this transfer comes from the pending queue so try move another */
-        infof(data, "Transfer was pending, now try another\n");
+        infof(data, "Transfer was pending, now try another");
         process_pending_handles(data->multi);
       }
 
@@ -1847,7 +1874,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         data->state.async.done = TRUE;
 #endif
         result = CURLE_OK;
-        infof(data, "Hostname '%s' was found in DNS cache\n", hostname);
+        infof(data, "Hostname '%s' was found in DNS cache", hostname);
       }
 
       if(!dns)
@@ -2279,7 +2306,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
         CURLcode ret = Curl_retry_request(data, &newurl);
 
         if(!ret) {
-          infof(data, "Downgrades to HTTP/1.1!\n");
+          infof(data, "Downgrades to HTTP/1.1!");
           streamclose(data->conn, "Disconnect HTTP/2 for HTTP/1");
           data->state.httpwant = CURL_HTTP_VERSION_1_1;
           /* clear the error message bit too as we ignore the one we got */
@@ -2418,6 +2445,21 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
     default:
       return CURLM_INTERNAL_ERROR;
     }
+
+    if(data->conn &&
+       data->mstate >= MSTATE_CONNECT &&
+       data->mstate < MSTATE_DO &&
+       rc != CURLM_CALL_MULTI_PERFORM &&
+       !multi_ischanged(multi, false)) {
+      /* We now handle stream timeouts if and only if this will be the last
+       * loop iteration. We only check this on the last iteration to ensure
+       * that if we know we have additional work to do immediately
+       * (i.e. CURLM_CALL_MULTI_PERFORM == TRUE) then we should do that before
+       * declaring the connection timed out as we may almost have a completed
+       * connection. */
+      multi_handle_timeout(data, nowp, &stream_error, &result, TRUE);
+    }
+
     statemachine_end:
 
     if(data->mstate < MSTATE_COMPLETED) {
@@ -3339,7 +3381,7 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
     rc = Curl_splayremove(multi->timetree, &data->state.timenode,
                           &multi->timetree);
     if(rc)
-      infof(data, "Internal error removing splay node = %d\n", rc);
+      infof(data, "Internal error removing splay node = %d", rc);
   }
 
   /* Indicate that we are in the splay tree and insert the new timer expiry
@@ -3386,7 +3428,7 @@ void Curl_expire_clear(struct Curl_easy *data)
     rc = Curl_splayremove(multi->timetree, &data->state.timenode,
                           &multi->timetree);
     if(rc)
-      infof(data, "Internal error clearing splay node = %d\n", rc);
+      infof(data, "Internal error clearing splay node = %d", rc);
 
     /* flush the timeout list too */
     while(list->size > 0) {
@@ -3394,7 +3436,7 @@ void Curl_expire_clear(struct Curl_easy *data)
     }
 
 #ifdef DEBUGBUILD
-    infof(data, "Expire cleared (transfer %p)\n", data);
+    infof(data, "Expire cleared (transfer %p)", data);
 #endif
     nowp->tv_sec = 0;
     nowp->tv_usec = 0;

+ 3 - 0
lib/multihandle.h

@@ -153,6 +153,9 @@ struct Curl_multi {
   bool recheckstate; /* see Curl_multi_connchanged */
   bool in_callback;            /* true while executing a callback */
   bool ipv6_works;
+#ifdef USE_OPENSSL
+  bool ssl_seeded;
+#endif
 };
 
 #endif /* HEADER_CURL_MULTIHANDLE_H */

+ 21 - 4
lib/netrc.c

@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2021, 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
@@ -42,7 +42,8 @@
 enum host_lookup_state {
   NOTHING,
   HOSTFOUND,    /* the 'machine' keyword was found */
-  HOSTVALID     /* this is "our" machine! */
+  HOSTVALID,    /* this is "our" machine! */
+  MACDEF
 };
 
 #define NETRC_FILE_MISSING 1
@@ -84,12 +85,17 @@ static int parsenetrc(const char *host,
     int  netrcbuffsize = (int)sizeof(netrcbuffer);
 
     while(!done && fgets(netrcbuffer, netrcbuffsize, file)) {
+      if(state == MACDEF) {
+        if((netrcbuffer[0] == '\n') || (netrcbuffer[0] == '\r'))
+          state = NOTHING;
+        else
+          continue;
+      }
       tok = strtok_r(netrcbuffer, " \t\n", &tok_buf);
       if(tok && *tok == '#')
         /* treat an initial hash as a comment line */
         continue;
       while(tok) {
-
         if((login && *login) && (password && *password)) {
           done = TRUE;
           break;
@@ -97,7 +103,13 @@ static int parsenetrc(const char *host,
 
         switch(state) {
         case NOTHING:
-          if(strcasecompare("machine", tok)) {
+          if(strcasecompare("macdef", tok)) {
+            /* Define a macro. A macro is defined with the specified name; its
+               contents begin with the next .netrc line and continue until a
+               null line (consecutive new-line characters) is encountered. */
+            state = MACDEF;
+          }
+          else if(strcasecompare("machine", tok)) {
             /* the next tok is the machine name, this is in itself the
                delimiter that starts the stuff entered for this machine,
                after this we need to search for 'login' and
@@ -109,6 +121,11 @@ static int parsenetrc(const char *host,
             retcode = NETRC_SUCCESS; /* we did find our host */
           }
           break;
+        case MACDEF:
+          if(!strlen(tok)) {
+            state = NOTHING;
+          }
+          break;
         case HOSTFOUND:
           if(strcasecompare(host, tok)) {
             /* and yes, this is our host! */

+ 10 - 6
lib/non-ascii.c

@@ -31,6 +31,7 @@
 #include "sendf.h"
 #include "urldata.h"
 #include "multiif.h"
+#include "strerror.h"
 
 #include "curl_memory.h"
 /* The last #include file should be: */
@@ -104,6 +105,7 @@ CURLcode Curl_convert_to_network(struct Curl_easy *data,
     iconv_t *cd = &tmpcd;
     char *input_ptr, *output_ptr;
     size_t in_bytes, out_bytes, rc;
+    char ebuffer[STRERROR_LEN];
 
     /* open an iconv conversion descriptor if necessary */
     if(data)
@@ -116,7 +118,7 @@ CURLcode Curl_convert_to_network(struct Curl_easy *data,
               "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
               CURL_ICONV_CODESET_OF_NETWORK,
               CURL_ICONV_CODESET_OF_HOST,
-              errno, strerror(errno));
+              errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer)));
         return CURLE_CONV_FAILED;
       }
     }
@@ -130,7 +132,7 @@ CURLcode Curl_convert_to_network(struct Curl_easy *data,
     if((rc == ICONV_ERROR) || (in_bytes)) {
       failf(data,
             "The Curl_convert_to_network iconv call failed with errno %i: %s",
-            errno, strerror(errno));
+            errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer)));
       return CURLE_CONV_FAILED;
     }
 #else
@@ -170,6 +172,7 @@ CURLcode Curl_convert_from_network(struct Curl_easy *data,
     iconv_t *cd = &tmpcd;
     char *input_ptr, *output_ptr;
     size_t in_bytes, out_bytes, rc;
+    char ebuffer[STRERROR_LEN];
 
     /* open an iconv conversion descriptor if necessary */
     if(data)
@@ -182,7 +185,7 @@ CURLcode Curl_convert_from_network(struct Curl_easy *data,
               "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
               CURL_ICONV_CODESET_OF_HOST,
               CURL_ICONV_CODESET_OF_NETWORK,
-              errno, strerror(errno));
+              errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer)));
         return CURLE_CONV_FAILED;
       }
     }
@@ -196,7 +199,7 @@ CURLcode Curl_convert_from_network(struct Curl_easy *data,
     if((rc == ICONV_ERROR) || (in_bytes)) {
       failf(data,
             "Curl_convert_from_network iconv call failed with errno %i: %s",
-            errno, strerror(errno));
+            errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer)));
       return CURLE_CONV_FAILED;
     }
 #else
@@ -237,6 +240,7 @@ CURLcode Curl_convert_from_utf8(struct Curl_easy *data,
     char *input_ptr;
     char *output_ptr;
     size_t in_bytes, out_bytes, rc;
+    char ebuffer[STRERROR_LEN];
 
     /* open an iconv conversion descriptor if necessary */
     if(data)
@@ -249,7 +253,7 @@ CURLcode Curl_convert_from_utf8(struct Curl_easy *data,
               "The iconv_open(\"%s\", \"%s\") call failed with errno %i: %s",
               CURL_ICONV_CODESET_OF_HOST,
               CURL_ICONV_CODESET_FOR_UTF8,
-              errno, strerror(errno));
+              errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer)));
         return CURLE_CONV_FAILED;
       }
     }
@@ -263,7 +267,7 @@ CURLcode Curl_convert_from_utf8(struct Curl_easy *data,
     if((rc == ICONV_ERROR) || (in_bytes)) {
       failf(data,
             "The Curl_convert_from_utf8 iconv call failed with errno %i: %s",
-            errno, strerror(errno));
+            errno, Curl_strerror(errno, ebuffer, sizeof(ebuffer)));
       return CURLE_CONV_FAILED;
     }
     if(output_ptr < input_ptr) {

+ 5 - 4
lib/openldap.c

@@ -247,7 +247,7 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
 #ifdef USE_SSL
   if(conn->handler->flags & PROTOPT_SSL) {
     CURLcode result;
-    result = Curl_ssl_connect_nonblocking(data, conn,
+    result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
                                           FIRSTSOCKET, &li->ssldone);
     if(result)
       return result;
@@ -270,7 +270,8 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
   if(conn->handler->flags & PROTOPT_SSL) {
     /* Is the SSL handshake complete yet? */
     if(!li->ssldone) {
-      CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FIRSTSOCKET,
+      CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
+                                                     FIRSTSOCKET,
                                                      &li->ssldone);
       if(result || !li->ssldone)
         return result;
@@ -399,7 +400,7 @@ static CURLcode oldap_do(struct Curl_easy *data, bool *done)
 
   connkeep(conn, "OpenLDAP do");
 
-  infof(data, "LDAP local: %s\n", data->state.url);
+  infof(data, "LDAP local: %s", data->state.url);
 
   rc = ldap_url_parse(data->state.url, &ludp);
   if(rc != LDAP_URL_SUCCESS) {
@@ -509,7 +510,7 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
       else {
         /* successful */
         if(code == LDAP_SIZELIMIT_EXCEEDED)
-          infof(data, "There are more than %d entries\n", lr->nument);
+          infof(data, "There are more than %d entries", lr->nument);
         data->req.size = data->req.bytecount;
         *err = CURLE_OK;
         ret = 0;

+ 2 - 2
lib/pingpong.c

@@ -402,7 +402,7 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
         clipamount = gotbytes - i;
         restart = TRUE;
         DEBUGF(infof(data, "Curl_pp_readresp_ %d bytes of trailing "
-                     "server response left\n",
+                     "server response left",
                      (int)clipamount));
       }
       else if(keepon) {
@@ -412,7 +412,7 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
              with it. We keep the first bytes of the line then we throw
              away the rest. */
           infof(data, "Excessive server response line length received, "
-                "%zd bytes. Stripping\n", gotbytes);
+                "%zd bytes. Stripping", gotbytes);
           restart = TRUE;
 
           /* we keep 40 bytes since all our pingpong protocols are only

+ 28 - 29
lib/pop3.c

@@ -75,7 +75,6 @@
 #include "strcase.h"
 #include "vtls/vtls.h"
 #include "connect.h"
-#include "strerror.h"
 #include "select.h"
 #include "multiif.h"
 #include "url.h"
@@ -308,7 +307,7 @@ static void state(struct Curl_easy *data, pop3state newstate)
   };
 
   if(pop3c->state != newstate)
-    infof(data, "POP3 %p state change from %s to %s\n",
+    infof(data, "POP3 %p state change from %s to %s",
           (void *)pop3c, names[pop3c->state], names[newstate]);
 #endif
 
@@ -370,8 +369,9 @@ static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data,
 {
   /* Start the SSL connection */
   struct pop3_conn *pop3c = &conn->proto.pop3c;
-  CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FIRSTSOCKET,
-                                                 &pop3c->ssldone);
+  CURLcode result =
+    Curl_ssl_connect_nonblocking(data, conn, FALSE, FIRSTSOCKET,
+                                 &pop3c->ssldone);
 
   if(!result) {
     if(pop3c->state != POP3_UPGRADETLS)
@@ -551,7 +551,7 @@ static CURLcode pop3_perform_authentication(struct Curl_easy *data,
       result = pop3_perform_user(data, conn);
     else {
       /* Other mechanisms not supported */
-      infof(data, "No known authentication mechanisms supported!\n");
+      infof(data, "No known authentication mechanisms supported!");
       result = CURLE_LOGIN_DENIED;
     }
   }
@@ -740,28 +740,23 @@ static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code,
       }
     }
   }
-  else if(pop3code == '+') {
-    if(data->set.use_ssl && !conn->ssl[FIRSTSOCKET].use) {
-      /* We don't have a SSL/TLS connection yet, but SSL is requested */
-      if(pop3c->tls_supported)
-        /* Switch to TLS connection now */
-        result = pop3_perform_starttls(data, conn);
-      else if(data->set.use_ssl == CURLUSESSL_TRY)
-        /* Fallback and carry on with authentication */
-        result = pop3_perform_authentication(data, conn);
-      else {
-        failf(data, "STLS not supported.");
-        result = CURLE_USE_SSL_FAILED;
-      }
-    }
-    else
-      result = pop3_perform_authentication(data, conn);
-  }
   else {
     /* Clear text is supported when CAPA isn't recognised */
-    pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
+    if(pop3code != '+')
+      pop3c->authtypes |= POP3_TYPE_CLEARTEXT;
 
-    result = pop3_perform_authentication(data, conn);
+    if(!data->set.use_ssl || conn->ssl[FIRSTSOCKET].use)
+      result = pop3_perform_authentication(data, conn);
+    else if(pop3code == '+' && pop3c->tls_supported)
+      /* Switch to TLS connection now */
+      result = pop3_perform_starttls(data, conn);
+    else if(data->set.use_ssl <= CURLUSESSL_TRY)
+      /* Fallback and carry on with authentication */
+      result = pop3_perform_authentication(data, conn);
+    else {
+      failf(data, "STLS not supported.");
+      result = CURLE_USE_SSL_FAILED;
+    }
   }
 
   return result;
@@ -776,6 +771,10 @@ static CURLcode pop3_state_starttls_resp(struct Curl_easy *data,
   CURLcode result = CURLE_OK;
   (void)instate; /* no use for this yet */
 
+  /* Pipelining in response is forbidden. */
+  if(data->conn->proto.pop3c.pp.cache_size)
+    return CURLE_WEIRD_SERVER_REPLY;
+
   if(pop3code != '+') {
     if(data->set.use_ssl != CURLUSESSL_TRY) {
       failf(data, "STARTTLS denied");
@@ -1031,7 +1030,7 @@ static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done)
   struct pop3_conn *pop3c = &conn->proto.pop3c;
 
   if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
-    result = Curl_ssl_connect_nonblocking(data, conn,
+    result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
                                           FIRSTSOCKET, &pop3c->ssldone);
     if(result || !pop3c->ssldone)
       return result;
@@ -1172,7 +1171,7 @@ static CURLcode pop3_perform(struct Curl_easy *data, bool *connected,
   struct connectdata *conn = data->conn;
   struct POP3 *pop3 = data->req.p.pop3;
 
-  DEBUGF(infof(data, "DO phase starts\n"));
+  DEBUGF(infof(data, "DO phase starts"));
 
   if(data->set.opt_no_body) {
     /* Requested no body means no transfer */
@@ -1191,7 +1190,7 @@ static CURLcode pop3_perform(struct Curl_easy *data, bool *connected,
   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
 
   if(*dophase_done)
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
 
   return result;
 }
@@ -1274,11 +1273,11 @@ static CURLcode pop3_doing(struct Curl_easy *data, bool *dophase_done)
   CURLcode result = pop3_multi_statemach(data, dophase_done);
 
   if(result)
-    DEBUGF(infof(data, "DO phase failed\n"));
+    DEBUGF(infof(data, "DO phase failed"));
   else if(*dophase_done) {
     result = pop3_dophase_done(data, FALSE /* not connected */);
 
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
 
   return result;

+ 6 - 1
lib/progress.c

@@ -377,7 +377,12 @@ static curl_off_t trspeed(curl_off_t size, /* number of bytes */
 {
   if(us < 1)
     return size * 1000000;
-  return (curl_off_t)((long double)size/us * 1000000);
+  else if(size < CURL_OFF_T_MAX/1000000)
+    return (size * 1000000) / us;
+  else if(us >= 1000000)
+    return size / (us / 1000000);
+  else
+    return CURL_OFF_T_MAX;
 }
 
 /* returns TRUE if it's time to show the progress meter */

+ 1 - 1
lib/quic.h

@@ -45,7 +45,7 @@ CURLcode Curl_quic_is_connected(struct Curl_easy *data,
                                 struct connectdata *conn,
                                 int sockindex,
                                 bool *connected);
-int Curl_quic_ver(char *p, size_t len);
+void Curl_quic_ver(char *p, size_t len);
 CURLcode Curl_quic_done_sending(struct Curl_easy *data);
 void Curl_quic_done(struct Curl_easy *data, bool premature);
 bool Curl_quic_data_pending(const struct Curl_easy *data);

+ 2 - 2
lib/rand.c

@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2021, 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
@@ -87,7 +87,7 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd)
 
   if(!seeded) {
     struct curltime now = Curl_now();
-    infof(data, "WARNING: Using weak random seed\n");
+    infof(data, "WARNING: Using weak random seed");
     randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec;
     randseed = randseed * 1103515245 + 12345;
     randseed = randseed * 1103515245 + 12345;

+ 3 - 3
lib/rtsp.c

@@ -231,7 +231,7 @@ static CURLcode rtsp_done(struct Curl_easy *data,
     }
     if(data->set.rtspreq == RTSPREQ_RECEIVE &&
             (data->conn->proto.rtspc.rtp_channel == -1)) {
-      infof(data, "Got an RTP Receive with a CSeq of %ld\n", CSeq_recv);
+      infof(data, "Got an RTP Receive with a CSeq of %ld", CSeq_recv);
     }
   }
 
@@ -651,7 +651,7 @@ static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
       }
       /* We have the full RTP interleaved packet
        * Write out the header including the leading '$' */
-      DEBUGF(infof(data, "RTP write channel %d rtp_length %d\n",
+      DEBUGF(infof(data, "RTP write channel %d rtp_length %d",
              rtspc->rtp_channel, rtp_length));
       result = rtp_client_write(data, &rtp[0], rtp_length + 4);
       if(result) {
@@ -682,7 +682,7 @@ static CURLcode rtsp_rtp_readwrite(struct Curl_easy *data,
   }
 
   if(rtp_dataleft && rtp[0] == '$') {
-    DEBUGF(infof(data, "RTP Rewinding %zd %s\n", rtp_dataleft,
+    DEBUGF(infof(data, "RTP Rewinding %zd %s", rtp_dataleft,
           *readmore ? "(READMORE)" : ""));
 
     /* Store the incomplete RTP packet for a "rewind" */

+ 1 - 1
lib/rtsp.h

@@ -22,7 +22,7 @@
  *
  ***************************************************************************/
 #ifdef USE_HYPER
-#define CURL_DISABLE_RTSP
+#define CURL_DISABLE_RTSP 1
 #endif
 
 #ifndef CURL_DISABLE_RTSP

+ 4 - 0
lib/select.h

@@ -106,7 +106,11 @@ int tpf_select_libcurl(int maxfds, fd_set* reads, fd_set* writes,
   } \
 } while(0)
 #else
+#ifdef HAVE_POLL_FINE
+#define VALID_SOCK(s) ((s) >= 0)  /* FD_SETSIZE is irrelevant for poll */
+#else
 #define VALID_SOCK(s) (((s) >= 0) && ((s) < FD_SETSIZE))
+#endif
 #define VERIFY_SOCK(x) do { \
   if(!VALID_SOCK(x)) { \
     SET_SOCKERRNO(EINVAL); \

+ 9 - 17
lib/sendf.c

@@ -236,29 +236,21 @@ bool Curl_recv_has_postponed_data(struct connectdata *conn, int sockindex)
 #endif /* ! USE_RECV_BEFORE_SEND_WORKAROUND */
 
 /* Curl_infof() is for info message along the way */
+#define MAXINFO 2048
 
 void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
 {
+  DEBUGASSERT(!strchr(fmt, '\n'));
   if(data && data->set.verbose) {
     va_list ap;
     size_t len;
-    char print_buffer[2048 + 1];
+    char buffer[MAXINFO + 2];
     va_start(ap, fmt);
-    len = mvsnprintf(print_buffer, sizeof(print_buffer), fmt, ap);
-    /*
-     * Indicate truncation of the input by replacing the last 3 characters
-     * with "...", and transfer the newline over in case the format had one.
-     */
-    if(len >= sizeof(print_buffer)) {
-      len = strlen(fmt);
-      if(fmt[--len] == '\n')
-        msnprintf(print_buffer + (sizeof(print_buffer) - 5), 5, "...\n");
-      else
-        msnprintf(print_buffer + (sizeof(print_buffer) - 4), 4, "...");
-    }
+    len = mvsnprintf(buffer, MAXINFO, fmt, ap);
     va_end(ap);
-    len = strlen(print_buffer);
-    Curl_debug(data, CURLINFO_TEXT, print_buffer, len);
+    buffer[len++] = '\n';
+    buffer[len] = '\0';
+    Curl_debug(data, CURLINFO_TEXT, buffer, len);
   }
 }
 
@@ -274,14 +266,14 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
     size_t len;
     char error[CURL_ERROR_SIZE + 2];
     va_start(ap, fmt);
-    (void)mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
-    len = strlen(error);
+    len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
 
     if(data->set.errorbuffer && !data->state.errorbuf) {
       strcpy(data->set.errorbuffer, error);
       data->state.errorbuf = TRUE; /* wrote error string */
     }
     error[len++] = '\n';
+    error[len] = '\0';
     Curl_debug(data, CURLINFO_TEXT, error, len);
     va_end(ap);
   }

+ 7 - 7
lib/setopt.c

@@ -1689,7 +1689,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
   case CURLOPT_SSLCERT_BLOB:
     /*
-     * Blob that holds file name of the SSL certificate to use
+     * Blob that holds file content of the SSL certificate to use
      */
     result = Curl_setblobopt(&data->set.blobs[BLOB_CERT],
                              va_arg(param, struct curl_blob *));
@@ -1704,7 +1704,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
   case CURLOPT_PROXY_SSLCERT_BLOB:
     /*
-     * Blob that holds file name of the SSL certificate to use for proxy
+     * Blob that holds file content of the SSL certificate to use for proxy
      */
     result = Curl_setblobopt(&data->set.blobs[BLOB_CERT_PROXY],
                              va_arg(param, struct curl_blob *));
@@ -1735,7 +1735,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
   case CURLOPT_SSLKEY_BLOB:
     /*
-     * Blob that holds file name of the SSL key to use
+     * Blob that holds file content of the SSL key to use
      */
     result = Curl_setblobopt(&data->set.blobs[BLOB_KEY],
                              va_arg(param, struct curl_blob *));
@@ -1750,7 +1750,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
   case CURLOPT_PROXY_SSLKEY_BLOB:
     /*
-     * Blob that holds file name of the SSL key to use for proxy
+     * Blob that holds file content of the SSL key to use for proxy
      */
     result = Curl_setblobopt(&data->set.blobs[BLOB_KEY_PROXY],
                              va_arg(param, struct curl_blob *));
@@ -1872,7 +1872,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
   case CURLOPT_DOH_SSL_VERIFYPEER:
     /*
-     * Enable peer SSL verifying for DOH.
+     * Enable peer SSL verifying for DoH.
      */
     data->set.doh_verifypeer = (0 != va_arg(param, long)) ?
       TRUE : FALSE;
@@ -1911,7 +1911,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
   case CURLOPT_DOH_SSL_VERIFYHOST:
     /*
-     * Enable verification of the host name in the peer certificate for DOH
+     * Enable verification of the host name in the peer certificate for DoH
      */
     arg = va_arg(param, long);
 
@@ -1955,7 +1955,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
     break;
   case CURLOPT_DOH_SSL_VERIFYSTATUS:
     /*
-     * Enable certificate status verifying for DOH.
+     * Enable certificate status verifying for DoH.
      */
     if(!Curl_ssl_cert_status_request()) {
       result = CURLE_NOT_BUILT_IN;

+ 9 - 8
lib/sha256.c

@@ -42,8 +42,9 @@
 #ifdef USE_MBEDTLS
 #include <mbedtls/version.h>
 
-#if(MBEDTLS_VERSION_NUMBER >= 0x02070000)
-  #define HAS_RESULT_CODE_BASED_FUNCTIONS
+#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) && \
+   (MBEDTLS_VERSION_NUMBER < 0x03000000)
+  #define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS
 #endif
 #endif /* USE_MBEDTLS */
 
@@ -105,8 +106,8 @@ typedef mbedtls_sha256_context SHA256_CTX;
 
 static void SHA256_Init(SHA256_CTX *ctx)
 {
-#if !defined(HAS_RESULT_CODE_BASED_FUNCTIONS)
-  mbedtls_sha256_starts(ctx, 0);
+#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
+  (void) mbedtls_sha256_starts(ctx, 0);
 #else
   (void) mbedtls_sha256_starts_ret(ctx, 0);
 #endif
@@ -116,8 +117,8 @@ static void SHA256_Update(SHA256_CTX *ctx,
                           const unsigned char *data,
                           unsigned int length)
 {
-#if !defined(HAS_RESULT_CODE_BASED_FUNCTIONS)
-  mbedtls_sha256_update(ctx, data, length);
+#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
+  (void) mbedtls_sha256_update(ctx, data, length);
 #else
   (void) mbedtls_sha256_update_ret(ctx, data, length);
 #endif
@@ -125,8 +126,8 @@ static void SHA256_Update(SHA256_CTX *ctx,
 
 static void SHA256_Final(unsigned char *digest, SHA256_CTX *ctx)
 {
-#if !defined(HAS_RESULT_CODE_BASED_FUNCTIONS)
-  mbedtls_sha256_finish(ctx, digest);
+#if !defined(HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS)
+  (void) mbedtls_sha256_finish(ctx, digest);
 #else
   (void) mbedtls_sha256_finish_ret(ctx, digest);
 #endif

+ 3 - 3
lib/smb.c

@@ -204,7 +204,7 @@ static void conn_state(struct Curl_easy *data, enum smb_conn_state newstate)
   };
 
   if(smbc->state != newstate)
-    infof(data, "SMB conn %p state change from %s to %s\n",
+    infof(data, "SMB conn %p state change from %s to %s",
           (void *)smbc, names[smbc->state], names[newstate]);
 #endif
 
@@ -230,7 +230,7 @@ static void request_state(struct Curl_easy *data,
   };
 
   if(req->state != newstate)
-    infof(data, "SMB request %p state change from %s to %s\n",
+    infof(data, "SMB request %p state change from %s to %s",
           (void *)req, names[req->state], names[newstate]);
 #endif
 
@@ -670,7 +670,7 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done)
 #ifdef USE_SSL
     if((conn->handler->flags & PROTOPT_SSL)) {
       bool ssl_done = FALSE;
-      result = Curl_ssl_connect_nonblocking(data, conn,
+      result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
                                             FIRSTSOCKET, &ssl_done);
       if(result && result != CURLE_AGAIN)
         return result;

+ 13 - 9
lib/smtp.c

@@ -78,7 +78,6 @@
 #include "strcase.h"
 #include "vtls/vtls.h"
 #include "connect.h"
-#include "strerror.h"
 #include "select.h"
 #include "multiif.h"
 #include "url.h"
@@ -308,7 +307,7 @@ static void state(struct Curl_easy *data, smtpstate newstate)
   };
 
   if(smtpc->state != newstate)
-    infof(data, "SMTP %p state change from %s to %s\n",
+    infof(data, "SMTP %p state change from %s to %s",
           (void *)smtpc, names[smtpc->state], names[newstate]);
 #endif
 
@@ -397,7 +396,8 @@ static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
   /* Start the SSL connection */
   struct connectdata *conn = data->conn;
   struct smtp_conn *smtpc = &conn->proto.smtpc;
-  CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FIRSTSOCKET,
+  CURLcode result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
+                                                 FIRSTSOCKET,
                                                  &smtpc->ssldone);
 
   if(!result) {
@@ -484,7 +484,7 @@ static CURLcode smtp_perform_authentication(struct Curl_easy *data)
       state(data, SMTP_AUTH);
     else {
       /* Other mechanisms not supported */
-      infof(data, "No known authentication mechanisms supported!\n");
+      infof(data, "No known authentication mechanisms supported!");
       result = CURLE_LOGIN_DENIED;
     }
   }
@@ -834,6 +834,10 @@ static CURLcode smtp_state_starttls_resp(struct Curl_easy *data,
   CURLcode result = CURLE_OK;
   (void)instate; /* no use for this yet */
 
+  /* Pipelining in response is forbidden. */
+  if(data->conn->proto.smtpc.pp.cache_size)
+    return CURLE_WEIRD_SERVER_REPLY;
+
   if(smtpcode != 220) {
     if(data->set.use_ssl != CURLUSESSL_TRY) {
       failf(data, "STARTTLS denied, code %d", smtpcode);
@@ -1258,7 +1262,7 @@ static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done)
   struct smtp_conn *smtpc = &conn->proto.smtpc;
 
   if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
-    result = Curl_ssl_connect_nonblocking(data, conn,
+    result = Curl_ssl_connect_nonblocking(data, conn, FALSE,
                                           FIRSTSOCKET, &smtpc->ssldone);
     if(result || !smtpc->ssldone)
       return result;
@@ -1455,7 +1459,7 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
   struct connectdata *conn = data->conn;
   struct SMTP *smtp = data->req.p.smtp;
 
-  DEBUGF(infof(data, "DO phase starts\n"));
+  DEBUGF(infof(data, "DO phase starts"));
 
   if(data->set.opt_no_body) {
     /* Requested no body means no transfer */
@@ -1495,7 +1499,7 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
   *connected = conn->bits.tcpconnect[FIRSTSOCKET];
 
   if(*dophase_done)
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
 
   return result;
 }
@@ -1579,11 +1583,11 @@ static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
   CURLcode result = smtp_multi_statemach(data, dophase_done);
 
   if(result)
-    DEBUGF(infof(data, "DO phase failed\n"));
+    DEBUGF(infof(data, "DO phase failed"));
   else if(*dophase_done) {
     result = smtp_dophase_done(data, FALSE /* not connected */);
 
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
 
   return result;

+ 26 - 10
lib/socketpair.c

@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 2019 - 2020, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 2019 - 2021, 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
@@ -48,6 +48,10 @@
 #endif /* !INADDR_LOOPBACK */
 #endif /* !WIN32 */
 
+#include "nonblock.h" /* for curlx_nonblock */
+#include "timeval.h"  /* needed before select.h */
+#include "select.h"   /* for Curl_poll */
+
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_memory.h"
@@ -59,12 +63,11 @@ int Curl_socketpair(int domain, int type, int protocol,
   union {
     struct sockaddr_in inaddr;
     struct sockaddr addr;
-  } a;
+  } a, a2;
   curl_socket_t listener;
   curl_socklen_t addrlen = sizeof(a.inaddr);
   int reuse = 1;
-  char data[2][12];
-  ssize_t dlen;
+  struct pollfd pfd[1];
   (void)domain;
   (void)type;
   (void)protocol;
@@ -85,7 +88,8 @@ int Curl_socketpair(int domain, int type, int protocol,
     goto error;
   if(bind(listener, &a.addr, sizeof(a.inaddr)) == -1)
     goto error;
-  if(getsockname(listener, &a.addr, &addrlen) == -1)
+  if(getsockname(listener, &a.addr, &addrlen) == -1 ||
+     addrlen < (int)sizeof(a.inaddr))
     goto error;
   if(listen(listener, 1) == -1)
     goto error;
@@ -94,18 +98,30 @@ int Curl_socketpair(int domain, int type, int protocol,
     goto error;
   if(connect(socks[0], &a.addr, sizeof(a.inaddr)) == -1)
     goto error;
+
+  /* use non-blocking accept to make sure we don't block forever */
+  if(curlx_nonblock(listener, TRUE) < 0)
+    goto error;
+  pfd[0].fd = listener;
+  pfd[0].events = POLLIN;
+  pfd[0].revents = 0;
+  (void)Curl_poll(pfd, 1, 10*1000); /* 10 seconds */
   socks[1] = accept(listener, NULL, NULL);
   if(socks[1] == CURL_SOCKET_BAD)
     goto error;
 
   /* verify that nothing else connected */
-  msnprintf(data[0], sizeof(data[0]), "%p", socks);
-  dlen = strlen(data[0]);
-  if(swrite(socks[0], data[0], dlen) != dlen)
+  addrlen = sizeof(a.inaddr);
+  if(getsockname(socks[0], &a.addr, &addrlen) == -1 ||
+     addrlen < (int)sizeof(a.inaddr))
     goto error;
-  if(sread(socks[1], data[1], sizeof(data[1])) != dlen)
+  addrlen = sizeof(a2.inaddr);
+  if(getpeername(socks[1], &a2.addr, &addrlen) == -1 ||
+     addrlen < (int)sizeof(a2.inaddr))
     goto error;
-  if(memcmp(data[0], data[1], dlen))
+  if(a.inaddr.sin_family != a2.inaddr.sin_family ||
+     a.inaddr.sin_addr.s_addr != a2.inaddr.sin_addr.s_addr ||
+     a.inaddr.sin_port != a2.inaddr.sin_port)
     goto error;
 
   sclose(listener);

+ 37 - 35
lib/socks.c

@@ -99,24 +99,24 @@ int Curl_blockread_all(struct Curl_easy *data,   /* transfer */
 }
 #endif
 
-#ifndef DEBUGBUILD
-#define sxstate(x,y) socksstate(x,y)
-#else
+#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+#define DEBUG_AND_VERBOSE
 #define sxstate(x,y) socksstate(x,y, __LINE__)
+#else
+#define sxstate(x,y) socksstate(x,y)
 #endif
 
-
 /* always use this function to change state, to make debugging easier */
 static void socksstate(struct Curl_easy *data,
                        enum connect_t state
-#ifdef DEBUGBUILD
+#ifdef DEBUG_AND_VERBOSE
                        , int lineno
 #endif
 )
 {
   struct connectdata *conn = data->conn;
   enum connect_t oldstate = conn->cnnct.state;
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+#ifdef DEBUG_AND_VERBOSE
   /* synced with the state list in urldata.h */
   static const char * const statename[] = {
     "INIT",
@@ -146,9 +146,9 @@ static void socksstate(struct Curl_easy *data,
 
   conn->cnnct.state = state;
 
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
+#ifdef DEBUG_AND_VERBOSE
   infof(data,
-        "SXSTATE: %s => %s conn %p; line %d\n",
+        "SXSTATE: %s => %s conn %p; line %d",
         statename[oldstate], statename[conn->cnnct.state], conn,
         lineno);
 #endif
@@ -214,10 +214,10 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user,
     /* SOCKS4 can only do IPv4, insist! */
     conn->ip_version = CURL_IPRESOLVE_V4;
     if(conn->bits.httpproxy)
-      infof(data, "SOCKS4%s: connecting to HTTP proxy %s port %d\n",
+      infof(data, "SOCKS4%s: connecting to HTTP proxy %s port %d",
             protocol4a ? "a" : "", hostname, remote_port);
 
-    infof(data, "SOCKS4 communication to %s:%d\n", hostname, remote_port);
+    infof(data, "SOCKS4 communication to %s:%d", hostname, remote_port);
 
     /*
      * Compose socks4 request
@@ -244,7 +244,7 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user,
         return CURLPX_RESOLVE_HOST;
       else if(rc == CURLRESOLV_PENDING) {
         sxstate(data, CONNECT_RESOLVING);
-        infof(data, "SOCKS4 non-blocking resolve of %s\n", hostname);
+        infof(data, "SOCKS4 non-blocking resolve of %s", hostname);
         return CURLPX_OK;
       }
       sxstate(data, CONNECT_RESOLVED);
@@ -264,7 +264,7 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user,
       data->state.async.dns = dns;
       data->state.async.done = TRUE;
 #endif
-      infof(data, "Hostname '%s' was found\n", hostname);
+      infof(data, "Hostname '%s' was found", hostname);
       sxstate(data, CONNECT_RESOLVED);
     }
     else {
@@ -279,18 +279,21 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user,
   CONNECT_RESOLVED:
   case CONNECT_RESOLVED: {
     struct Curl_addrinfo *hp = NULL;
-    char buf[64];
     /*
      * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
      * returns a Curl_addrinfo pointer that may not always look the same.
      */
-    if(dns)
+    if(dns) {
       hp = dns->addr;
-    if(hp) {
-      Curl_printable_address(hp, buf, sizeof(buf));
 
-      if(hp->ai_family == AF_INET) {
+      /* scan for the first IPv4 address */
+      while(hp && (hp->ai_family != AF_INET))
+        hp = hp->ai_next;
+
+      if(hp) {
         struct sockaddr_in *saddr_in;
+        char buf[64];
+        Curl_printable_address(hp, buf, sizeof(buf));
 
         saddr_in = (struct sockaddr_in *)(void *)hp->ai_addr;
         socksreq[4] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[0];
@@ -298,20 +301,19 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user,
         socksreq[6] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[2];
         socksreq[7] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[3];
 
-        infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)\n", buf);
-      }
-      else {
-        hp = NULL; /* fail! */
-        failf(data, "SOCKS4 connection to %s not supported", buf);
-      }
+        infof(data, "SOCKS4 connect to IPv4 %s (locally resolved)", buf);
 
-      Curl_resolv_unlock(data, dns); /* not used anymore from now on */
+        Curl_resolv_unlock(data, dns); /* not used anymore from now on */
+      }
+      else
+        failf(data, "SOCKS4 connection to %s not supported", hostname);
     }
-    if(!hp) {
+    else
       failf(data, "Failed to resolve \"%s\" for SOCKS4 connect.",
             hostname);
+
+    if(!hp)
       return CURLPX_RESOLVE_HOST;
-    }
   }
     /* FALLTHROUGH */
   CONNECT_REQ_INIT:
@@ -435,7 +437,7 @@ CURLproxycode Curl_SOCKS4(const char *proxy_user,
   /* Result */
   switch(socksreq[1]) {
   case 90:
-    infof(data, "SOCKS4%s request granted.\n", protocol4a?"a":"");
+    infof(data, "SOCKS4%s request granted.", protocol4a?"a":"");
     break;
   case 91:
     failf(data,
@@ -528,19 +530,19 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
   switch(sx->state) {
   case CONNECT_SOCKS_INIT:
     if(conn->bits.httpproxy)
-      infof(data, "SOCKS5: connecting to HTTP proxy %s port %d\n",
+      infof(data, "SOCKS5: connecting to HTTP proxy %s port %d",
             hostname, remote_port);
 
     /* RFC1928 chapter 5 specifies max 255 chars for domain name in packet */
     if(!socks5_resolve_local && hostname_len > 255) {
       infof(data, "SOCKS5: server resolving disabled for hostnames of "
-            "length > 255 [actual len=%zu]\n", hostname_len);
+            "length > 255 [actual len=%zu]", hostname_len);
       socks5_resolve_local = TRUE;
     }
 
     if(auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
       infof(data,
-            "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu\n",
+            "warning: unsupported value passed to CURLOPT_SOCKS5_AUTH: %lu",
             auth);
     if(!(auth & CURLAUTH_BASIC))
       /* disable username/password auth */
@@ -778,7 +780,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
       data->state.async.dns = dns;
       data->state.async.done = TRUE;
 #endif
-      infof(data, "SOCKS5: hostname '%s' found\n", hostname);
+      infof(data, "SOCKS5: hostname '%s' found", hostname);
     }
 
     if(!dns) {
@@ -820,7 +822,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
         socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
       }
 
-      infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)\n", dest);
+      infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)", dest);
     }
 #ifdef ENABLE_IPV6
     else if(hp->ai_family == AF_INET6) {
@@ -834,7 +836,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
           ((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
       }
 
-      infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)\n", dest);
+      infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)", dest);
     }
 #endif
     else {
@@ -858,7 +860,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
       socksreq[len++] = (char) hostname_len; /* one byte address length */
       memcpy(&socksreq[len], hostname, hostname_len); /* address w/o NULL */
       len += hostname_len;
-      infof(data, "SOCKS5 connect to %s:%d (remotely resolved)\n",
+      infof(data, "SOCKS5 connect to %s:%d (remotely resolved)",
             hostname, remote_port);
     }
     /* FALLTHROUGH */
@@ -1022,7 +1024,7 @@ CURLproxycode Curl_SOCKS5(const char *proxy_user,
     }
     sxstate(data, CONNECT_DONE);
   }
-  infof(data, "SOCKS5 request granted.\n");
+  infof(data, "SOCKS5 request granted.");
 
   *done = TRUE;
   return CURLPX_OK; /* Proxy was successful! */

+ 3 - 3
lib/socks_gssapi.c

@@ -328,7 +328,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
   user[gss_send_token.length] = '\0';
   gss_release_name(&gss_status, &gss_client_name);
   gss_release_buffer(&gss_status, &gss_send_token);
-  infof(data, "SOCKS5 server authenticated user %s with GSS-API.\n",user);
+  infof(data, "SOCKS5 server authenticated user %s with GSS-API.",user);
   free(user);
   user = NULL;
 
@@ -344,7 +344,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
   else if(gss_ret_flags & GSS_C_INTEG_FLAG)
     gss_enc = 1;
 
-  infof(data, "SOCKS5 server supports GSS-API %s data protection.\n",
+  infof(data, "SOCKS5 server supports GSS-API %s data protection.",
         (gss_enc == 0)?"no":((gss_enc==1)?"integrity":"confidentiality"));
   /* force for the moment to no data protection */
   gss_enc = 0;
@@ -518,7 +518,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
 
   (void)curlx_nonblock(sock, TRUE);
 
-  infof(data, "SOCKS5 access with%s protection granted.\n",
+  infof(data, "SOCKS5 access with%s protection granted.",
         (socksreq[0] == 0)?"out GSS-API data":
         ((socksreq[0] == 1)?" GSS-API integrity":" GSS-API confidentiality"));
 

+ 3 - 3
lib/socks_sspi.c

@@ -327,7 +327,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
     failf(data, "Failed to determine user name.");
     return CURLE_COULDNT_CONNECT;
   }
-  infof(data, "SOCKS5 server authenticated user %s with GSS-API.\n",
+  infof(data, "SOCKS5 server authenticated user %s with GSS-API.",
         names.sUserName);
   s_pSecFn->FreeContextBuffer(names.sUserName);
 
@@ -343,7 +343,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
   else if(sspi_ret_flags & ISC_REQ_INTEGRITY)
     gss_enc = 1;
 
-  infof(data, "SOCKS5 server supports GSS-API %s data protection.\n",
+  infof(data, "SOCKS5 server supports GSS-API %s data protection.",
         (gss_enc == 0)?"no":((gss_enc == 1)?"integrity":"confidentiality") );
   /* force to no data protection, avoid encryption/decryption for now */
   gss_enc = 0;
@@ -591,7 +591,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
   }
   (void)curlx_nonblock(sock, TRUE);
 
-  infof(data, "SOCKS5 access with%s protection granted.\n",
+  infof(data, "SOCKS5 access with%s protection granted.",
         (socksreq[0] == 0)?"out GSS-API data":
         ((socksreq[0] == 1)?" GSS-API integrity":" GSS-API confidentiality"));
 

+ 27 - 1
lib/strdup.c

@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2021, 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
@@ -24,6 +24,10 @@
 
 #include <curl/curl.h>
 
+#ifdef WIN32
+#include <wchar.h>
+#endif
+
 #include "strdup.h"
 #include "curl_memory.h"
 
@@ -50,6 +54,28 @@ char *curlx_strdup(const char *str)
 }
 #endif
 
+#ifdef WIN32
+/***************************************************************************
+ *
+ * Curl_wcsdup(source)
+ *
+ * Copies the 'source' wchar string to a newly allocated buffer (that is
+ * returned).
+ *
+ * Returns the new pointer or NULL on failure.
+ *
+ ***************************************************************************/
+wchar_t *Curl_wcsdup(const wchar_t *src)
+{
+  size_t length = wcslen(src);
+
+  if(length > (SIZE_T_MAX / sizeof(wchar_t)) - 1)
+    return (wchar_t *)NULL; /* integer overflow */
+
+  return (wchar_t *)Curl_memdup(src, (length + 1) * sizeof(wchar_t));
+}
+#endif
+
 /***************************************************************************
  *
  * Curl_memdup(source, length)

+ 4 - 1
lib/strdup.h

@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2021, 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
@@ -26,6 +26,9 @@
 #ifndef HAVE_STRDUP
 extern char *curlx_strdup(const char *str);
 #endif
+#ifdef WIN32
+wchar_t* Curl_wcsdup(const wchar_t* src);
+#endif
 void *Curl_memdup(const void *src, size_t buffer_length);
 void *Curl_saferealloc(void *ptr, size_t size);
 

+ 3 - 2
lib/strerror.c

@@ -188,8 +188,8 @@ curl_easy_strerror(CURLcode error)
   case CURLE_UNKNOWN_OPTION:
     return "An unknown option was passed in to libcurl";
 
-  case CURLE_TELNET_OPTION_SYNTAX :
-    return "Malformed telnet option";
+  case CURLE_SETOPT_OPTION_SYNTAX :
+    return "Malformed option provided in a setopt";
 
   case CURLE_GOT_NOTHING:
     return "Server returned nothing (no headers, no data)";
@@ -731,6 +731,7 @@ const char *Curl_strerror(int err, char *buf, size_t buflen)
   max = buflen - 1;
   *buf = '\0';
 
+  /* !checksrc! disable STRERROR 2 */
 #if defined(WIN32) || defined(_WIN32_WCE)
 #if defined(WIN32)
   /* 'sys_nerr' is the maximum errno number, it is not widely portable */

+ 18 - 15
lib/telnet.c

@@ -268,9 +268,9 @@ static void printoption(struct Curl_easy *data,
   if(data->set.verbose) {
     if(cmd == CURL_IAC) {
       if(CURL_TELCMD_OK(option))
-        infof(data, "%s IAC %s\n", direction, CURL_TELCMD(option));
+        infof(data, "%s IAC %s", direction, CURL_TELCMD(option));
       else
-        infof(data, "%s IAC %d\n", direction, option);
+        infof(data, "%s IAC %d", direction, option);
     }
     else {
       const char *fmt = (cmd == CURL_WILL) ? "WILL" :
@@ -287,12 +287,12 @@ static void printoption(struct Curl_easy *data,
           opt = NULL;
 
         if(opt)
-          infof(data, "%s %s %s\n", direction, fmt, opt);
+          infof(data, "%s %s %s", direction, fmt, opt);
         else
-          infof(data, "%s %s %d\n", direction, fmt, option);
+          infof(data, "%s %s %d", direction, fmt, option);
       }
       else
-        infof(data, "%s %d %d\n", direction, cmd, option);
+        infof(data, "%s %d %d", direction, cmd, option);
     }
   }
 }
@@ -765,8 +765,6 @@ static void printsub(struct Curl_easy *data,
         break;
       }
     }
-    if(direction)
-      infof(data, "\n");
   }
 }
 
@@ -834,7 +832,7 @@ static CURLcode check_telnet_options(struct Curl_easy *data)
           tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
         else {
           failf(data, "Syntax error in telnet option: %s", head->data);
-          result = CURLE_TELNET_OPTION_SYNTAX;
+          result = CURLE_SETOPT_OPTION_SYNTAX;
           break;
         }
         continue;
@@ -855,7 +853,7 @@ static CURLcode check_telnet_options(struct Curl_easy *data)
       break;
     }
     failf(data, "Syntax error in telnet option: %s", head->data);
-    result = CURLE_TELNET_OPTION_SYNTAX;
+    result = CURLE_SETOPT_OPTION_SYNTAX;
     break;
   }
 
@@ -922,12 +920,17 @@ static void suboption(struct Curl_easy *data)
         size_t tmplen = (strlen(v->data) + 1);
         /* Add the variable only if it fits */
         if(len + tmplen < (int)sizeof(temp)-6) {
-          if(sscanf(v->data, "%127[^,],%127s", varname, varval) == 2) {
-            msnprintf((char *)&temp[len], sizeof(temp) - len,
-                      "%c%s%c%s", CURL_NEW_ENV_VAR, varname,
-                      CURL_NEW_ENV_VALUE, varval);
-            len += tmplen;
-          }
+          int rv;
+          char sep[2] = "";
+          varval[0] = 0;
+          rv = sscanf(v->data, "%127[^,]%1[,]%127s", varname, sep, varval);
+          if(rv == 1)
+            len += msnprintf((char *)&temp[len], sizeof(temp) - len,
+                             "%c%s", CURL_NEW_ENV_VAR, varname);
+          else if(rv >= 2)
+            len += msnprintf((char *)&temp[len], sizeof(temp) - len,
+                             "%c%s%c%s", CURL_NEW_ENV_VAR, varname,
+                             CURL_NEW_ENV_VALUE, varval);
         }
       }
       msnprintf((char *)&temp[len], sizeof(temp) - len,

+ 20 - 20
lib/tftp.c

@@ -239,7 +239,7 @@ static CURLcode tftp_set_timeouts(struct tftp_state_data *state)
 
   infof(state->data,
         "set timeouts for state %d; Total % " CURL_FORMAT_CURL_OFF_T
-        ", retry %d maxtry %d\n",
+        ", retry %d maxtry %d",
         (int)state->state, timeout_ms, state->retry_time, state->retry_max);
 
   /* init RX time */
@@ -325,7 +325,7 @@ static CURLcode tftp_parse_option_ack(struct tftp_state_data *state,
       return CURLE_TFTP_ILLEGAL;
     }
 
-    infof(data, "got option=(%s) value=(%s)\n", option, value);
+    infof(data, "got option=(%s) value=(%s)", option, value);
 
     if(checkprefix(option, TFTP_OPTION_BLKSIZE)) {
       long blksize;
@@ -356,14 +356,14 @@ static CURLcode tftp_parse_option_ack(struct tftp_state_data *state,
       }
 
       state->blksize = (int)blksize;
-      infof(data, "%s (%d) %s (%d)\n", "blksize parsed from OACK",
+      infof(data, "%s (%d) %s (%d)", "blksize parsed from OACK",
             state->blksize, "requested", state->requested_blksize);
     }
     else if(checkprefix(option, TFTP_OPTION_TSIZE)) {
       long tsize = 0;
 
       tsize = strtol(value, NULL, 10);
-      infof(data, "%s (%ld)\n", "tsize parsed from OACK", tsize);
+      infof(data, "%s (%ld)", "tsize parsed from OACK", tsize);
 
       /* tsize should be ignored on upload: Who cares about the size of the
          remote file? */
@@ -397,7 +397,7 @@ static CURLcode tftp_connect_for_tx(struct tftp_state_data *state,
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   struct Curl_easy *data = state->data;
 
-  infof(data, "%s\n", "Connected for transmit");
+  infof(data, "%s", "Connected for transmit");
 #endif
   state->state = TFTP_STATE_TX;
   result = tftp_set_timeouts(state);
@@ -413,7 +413,7 @@ static CURLcode tftp_connect_for_rx(struct tftp_state_data *state,
 #ifndef CURL_DISABLE_VERBOSE_STRINGS
   struct Curl_easy *data = state->data;
 
-  infof(data, "%s\n", "Connected for receive");
+  infof(data, "%s", "Connected for receive");
 #endif
   state->state = TFTP_STATE_RX;
   result = tftp_set_timeouts(state);
@@ -596,12 +596,12 @@ static CURLcode tftp_rx(struct tftp_state_data *state,
     else if(state->block == rblock) {
       /* This is the last recently received block again. Log it and ACK it
          again. */
-      infof(data, "Received last DATA packet block %d again.\n", rblock);
+      infof(data, "Received last DATA packet block %d again.", rblock);
     }
     else {
       /* totally unexpected, just log it */
       infof(data,
-            "Received unexpected DATA packet block %d, expecting block %d\n",
+            "Received unexpected DATA packet block %d, expecting block %d",
             rblock, NEXT_BLOCKNUM(state->block));
       break;
     }
@@ -653,7 +653,7 @@ static CURLcode tftp_rx(struct tftp_state_data *state,
     /* Increment the retry count and fail if over the limit */
     state->retries++;
     infof(data,
-          "Timeout waiting for block %d ACK.  Retries = %d\n",
+          "Timeout waiting for block %d ACK.  Retries = %d",
           NEXT_BLOCKNUM(state->block), state->retries);
     if(state->retries > state->retry_max) {
       state->error = TFTP_ERR_TIMEOUT;
@@ -720,11 +720,11 @@ static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event)
          /* There's a bug in tftpd-hpa that causes it to send us an ack for
           * 65535 when the block number wraps to 0. So when we're expecting
           * 0, also accept 65535. See
-          * http://syslinux.zytor.com/archives/2010-September/015253.html
+          * https://www.syslinux.org/archives/2010-September/015612.html
           * */
          !(state->block == 0 && rblock == 65535)) {
         /* This isn't the expected block.  Log it and up the retry counter */
-        infof(data, "Received ACK for block %d, expecting %d\n",
+        infof(data, "Received ACK for block %d, expecting %d",
               rblock, state->block);
         state->retries++;
         /* Bail out if over the maximum */
@@ -797,7 +797,7 @@ static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event)
     /* Increment the retry counter and log the timeout */
     state->retries++;
     infof(data, "Timeout waiting for block %d ACK. "
-          " Retries = %d\n", NEXT_BLOCKNUM(state->block), state->retries);
+          " Retries = %d", NEXT_BLOCKNUM(state->block), state->retries);
     /* Decide if we've had enough */
     if(state->retries > state->retry_max) {
       state->error = TFTP_ERR_TIMEOUT;
@@ -906,22 +906,22 @@ static CURLcode tftp_state_machine(struct tftp_state_data *state,
 
   switch(state->state) {
   case TFTP_STATE_START:
-    DEBUGF(infof(data, "TFTP_STATE_START\n"));
+    DEBUGF(infof(data, "TFTP_STATE_START"));
     result = tftp_send_first(state, event);
     break;
   case TFTP_STATE_RX:
-    DEBUGF(infof(data, "TFTP_STATE_RX\n"));
+    DEBUGF(infof(data, "TFTP_STATE_RX"));
     result = tftp_rx(state, event);
     break;
   case TFTP_STATE_TX:
-    DEBUGF(infof(data, "TFTP_STATE_TX\n"));
+    DEBUGF(infof(data, "TFTP_STATE_TX"));
     result = tftp_tx(state, event);
     break;
   case TFTP_STATE_FIN:
-    infof(data, "%s\n", "TFTP finished");
+    infof(data, "%s", "TFTP finished");
     break;
   default:
-    DEBUGF(infof(data, "STATE: %d\n", state->state));
+    DEBUGF(infof(data, "STATE: %d", state->state));
     failf(data, "%s", "Internal state machine error");
     result = CURLE_TFTP_ILLEGAL;
     break;
@@ -1153,7 +1153,7 @@ static CURLcode tftp_receive_packet(struct Curl_easy *data)
       size_t strn = state->rbytes - 4;
       state->error = (tftp_error_t)error;
       if(tftp_strnlen(str, strn) < strn)
-        infof(data, "TFTP error: %s\n", str);
+        infof(data, "TFTP error: %s", str);
       break;
     }
     case TFTP_EVENT_ACK:
@@ -1288,7 +1288,7 @@ static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done)
   result = tftp_multi_statemach(data, dophase_done);
 
   if(*dophase_done) {
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
   }
   else if(!result) {
     /* The multi code doesn't have this logic for the DOING state so we
@@ -1325,7 +1325,7 @@ static CURLcode tftp_perform(struct Curl_easy *data, bool *dophase_done)
   tftp_multi_statemach(data, dophase_done);
 
   if(*dophase_done)
-    DEBUGF(infof(data, "DO phase is complete\n"));
+    DEBUGF(infof(data, "DO phase is complete"));
 
   return result;
 }

+ 37 - 29
lib/transfer.c

@@ -188,7 +188,7 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
     /* at this point we already verified that the callback exists
        so we compile and store the trailers buffer, then proceed */
     infof(data,
-          "Moving trailers state machine from initialized to sending.\n");
+          "Moving trailers state machine from initialized to sending.");
     data->state.trailers_state = TRAILERS_SENDING;
     Curl_dyn_init(&data->state.trailers_buf, DYN_TRAILERS);
 
@@ -211,7 +211,7 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
       curl_slist_free_all(trailers);
       return result;
     }
-    infof(data, "Successfully compiled trailers.\r\n");
+    infof(data, "Successfully compiled trailers.");
     curl_slist_free_all(trailers);
   }
 #endif
@@ -376,7 +376,7 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
       data->set.trailer_callback = NULL;
       /* mark the transfer as done */
       data->req.upload_done = TRUE;
-      infof(data, "Signaling end of chunked upload after trailers.\n");
+      infof(data, "Signaling end of chunked upload after trailers.");
     }
     else
 #endif
@@ -385,7 +385,7 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
         /* mark this as done once this chunk is transferred */
         data->req.upload_done = TRUE;
         infof(data,
-              "Signaling end of chunked upload via terminating chunk.\n");
+              "Signaling end of chunked upload via terminating chunk.");
       }
 
     if(added_crlf)
@@ -463,7 +463,7 @@ CURLcode Curl_readrewind(struct Curl_easy *data)
       err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD,
                                    data->set.ioctl_client);
       Curl_set_in_callback(data, false);
-      infof(data, "the ioctl callback returned %d\n", (int)err);
+      infof(data, "the ioctl callback returned %d", (int)err);
 
       if(err) {
         failf(data, "ioctl callback returned error %d", (int)err);
@@ -530,7 +530,7 @@ bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc)
   default:
     if(timeofdoc <= data->set.timevalue) {
       infof(data,
-            "The requested document is not new enough\n");
+            "The requested document is not new enough");
       data->info.timecond = TRUE;
       return FALSE;
     }
@@ -538,7 +538,7 @@ bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc)
   case CURL_TIMECOND_IFUNMODSINCE:
     if(timeofdoc >= data->set.timevalue) {
       infof(data,
-            "The requested document is not old enough\n");
+            "The requested document is not old enough");
       data->info.timecond = TRUE;
       return FALSE;
     }
@@ -615,7 +615,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, "readwrite_data: we're done!\n"));
+      DEBUGF(infof(data, "readwrite_data: we're done!"));
       nread = 0;
     }
 
@@ -638,10 +638,10 @@ static CURLcode readwrite_data(struct Curl_easy *data,
          server closed the connection and we bail out from this! */
 #ifdef USE_NGHTTP2
       if(is_http2 && !nread)
-        DEBUGF(infof(data, "nread == 0, stream closed, bailing\n"));
+        DEBUGF(infof(data, "nread == 0, stream closed, bailing"));
       else
 #endif
-        DEBUGF(infof(data, "nread <= 0, server closed connection, bailing\n"));
+        DEBUGF(infof(data, "nread <= 0, server closed connection, bailing"));
       k->keepon &= ~KEEP_RECV;
       break;
     }
@@ -684,7 +684,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
           infof(data,
                 "Excess found:"
                 " excess = %zd"
-                " url = %s (zero-length body)\n",
+                " url = %s (zero-length body)",
                 nread, data->state.up.path);
         }
 
@@ -764,7 +764,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
              written to the client. */
           if(conn->chunk.datasize) {
             infof(data, "Leftovers after chunking: % "
-                  CURL_FORMAT_CURL_OFF_T "u bytes\n",
+                  CURL_FORMAT_CURL_OFF_T "u bytes",
                   conn->chunk.datasize);
           }
         }
@@ -775,7 +775,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
       /* Account for body content stored in the header buffer */
       if((k->badheader == HEADER_PARTHEADER) && !k->ignorebody) {
         size_t headlen = Curl_dyn_len(&data->state.headerb);
-        DEBUGF(infof(data, "Increasing bytecount by %zu\n", headlen));
+        DEBUGF(infof(data, "Increasing bytecount by %zu", headlen));
         k->bytecount += headlen;
       }
 
@@ -789,7 +789,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
                 " excess = %zu"
                 ", size = %" CURL_FORMAT_CURL_OFF_T
                 ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
-                ", bytecount = %" CURL_FORMAT_CURL_OFF_T "\n",
+                ", bytecount = %" CURL_FORMAT_CURL_OFF_T,
                 excess, k->size, k->maxdownload, k->bytecount);
           connclose(conn, "excess found in a read");
         }
@@ -898,7 +898,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
     /* When we've read the entire thing and the close bit is set, the server
        may now close the connection. If there's now any kind of sending going
        on from our side, we need to stop that immediately. */
-    infof(data, "we are done reading and this is set to close, stop send\n");
+    infof(data, "we are done reading and this is set to close, stop send");
     k->keepon &= ~KEEP_SEND; /* no writing anymore either */
   }
 
@@ -923,7 +923,7 @@ CURLcode Curl_done_sending(struct Curl_easy *data,
   return CURLE_OK;
 }
 
-#if defined(WIN32) && !defined(USE_LWIPSOCK)
+#if defined(WIN32) && defined(USE_WINSOCK)
 #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
 #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
 #endif
@@ -1128,7 +1128,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
        (k->writebytecount == data->state.infilesize)) {
       /* we have sent all data we were supposed to */
       k->upload_done = TRUE;
-      infof(data, "We are completely uploaded and fine\n");
+      infof(data, "We are completely uploaded and fine");
     }
 
     if(k->upload_present != bytes_written) {
@@ -1199,7 +1199,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
 
   if(data->state.drain) {
     select_res |= CURL_CSELECT_IN;
-    DEBUGF(infof(data, "Curl_readwrite: forcibly told to drain data\n"));
+    DEBUGF(infof(data, "Curl_readwrite: forcibly told to drain data"));
   }
 
   if(!select_res) /* Call for select()/poll() only, if read/write/error
@@ -1212,8 +1212,12 @@ CURLcode Curl_readwrite(struct connectdata *conn,
   }
 
 #ifdef USE_HYPER
-  if(conn->datastream)
-    return conn->datastream(data, conn, &didwhat, done, select_res);
+  if(conn->datastream) {
+    result = conn->datastream(data, conn, &didwhat, done, select_res);
+    if(result || *done)
+      return result;
+  }
+  else {
 #endif
   /* We go ahead and do a read if we have a readable socket or if
      the stream was rewound (in which case we have data in a
@@ -1232,6 +1236,9 @@ CURLcode Curl_readwrite(struct connectdata *conn,
     if(result)
       return result;
   }
+#ifdef USE_HYPER
+  }
+#endif
 
   k->now = Curl_now();
   if(!didwhat) {
@@ -1256,7 +1263,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
         k->exp100 = EXP100_SEND_DATA;
         k->keepon |= KEEP_SEND;
         Curl_expire_done(data, EXPIRE_100_TIMEOUT);
-        infof(data, "Done waiting for 100-continue\n");
+        infof(data, "Done waiting for 100-continue");
       }
     }
   }
@@ -1632,7 +1639,8 @@ CURLcode Curl_follow(struct Curl_easy *data,
   DEBUGASSERT(data->state.uh);
   uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl,
                     (type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME :
-                    ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) );
+                    ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) |
+                    CURLU_ALLOW_SPACE);
   if(uc) {
     if(type != FOLLOW_FAKE)
       return Curl_uc_to_curlcode(uc);
@@ -1671,7 +1679,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
   data->state.url = newurl;
   data->state.url_alloc = TRUE;
 
-  infof(data, "Issue another request to this URL: '%s'\n", data->state.url);
+  infof(data, "Issue another request to this URL: '%s'", data->state.url);
 
   /*
    * We get here when the HTTP code is 300-399 (and 401). We need to perform
@@ -1714,7 +1722,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
         || data->state.httpreq == HTTPREQ_POST_FORM
         || data->state.httpreq == HTTPREQ_POST_MIME)
        && !(data->set.keep_post & CURL_REDIR_POST_301)) {
-      infof(data, "Switch from POST to GET\n");
+      infof(data, "Switch from POST to GET");
       data->state.httpreq = HTTPREQ_GET;
     }
     break;
@@ -1739,7 +1747,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
         || data->state.httpreq == HTTPREQ_POST_FORM
         || data->state.httpreq == HTTPREQ_POST_MIME)
        && !(data->set.keep_post & CURL_REDIR_POST_302)) {
-      infof(data, "Switch from POST to GET\n");
+      infof(data, "Switch from POST to GET");
       data->state.httpreq = HTTPREQ_GET;
     }
     break;
@@ -1757,7 +1765,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
         !(data->set.keep_post & CURL_REDIR_POST_303))) {
       data->state.httpreq = HTTPREQ_GET;
       data->set.upload = false;
-      infof(data, "Switch to %s\n",
+      infof(data, "Switch to %s",
             data->set.opt_no_body?"HEAD":"GET");
     }
     break;
@@ -1818,7 +1826,7 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url)
        to issue again, but the nghttp2 API can deliver the message to other
        streams as well, which is why this adds the check the data counters
        too. */
-    infof(data, "REFUSED_STREAM, retrying a fresh connect\n");
+    infof(data, "REFUSED_STREAM, retrying a fresh connect");
     data->state.refused_stream = FALSE; /* clear again */
     retry = TRUE;
   }
@@ -1830,8 +1838,8 @@ CURLcode Curl_retry_request(struct Curl_easy *data, char **url)
       data->state.retrycount = 0;
       return CURLE_SEND_ERROR;
     }
-    infof(data, "Connection died, retrying a fresh connect\
-(retry count: %d)\n", data->state.retrycount);
+    infof(data, "Connection died, retrying a fresh connect (retry count: %d)",
+          data->state.retrycount);
     *url = strdup(data->state.url);
     if(!*url)
       return CURLE_OUT_OF_MEMORY;

+ 90 - 59
lib/url.c

@@ -62,6 +62,14 @@
 #ifdef USE_LIBIDN2
 #include <idn2.h>
 
+#if defined(WIN32) && defined(UNICODE)
+#define IDN2_LOOKUP(name, host, flags) \
+  idn2_lookup_u8((const uint8_t *)name, (uint8_t **)host, flags)
+#else
+#define IDN2_LOOKUP(name, host, flags) \
+  idn2_lookup_ul((const char *)name, (char **)host, flags)
+#endif
+
 #elif defined(USE_WIN32_IDN)
 /* prototype for curl_win32_idn_to_ascii() */
 bool curl_win32_idn_to_ascii(const char *in, char **out);
@@ -92,7 +100,6 @@ bool curl_win32_idn_to_ascii(const char *in, char **out);
 #include "speedcheck.h"
 #include "warnless.h"
 #include "non-ascii.h"
-#include "inet_pton.h"
 #include "getinfo.h"
 #include "urlapi-int.h"
 #include "system_win32.h"
@@ -726,7 +733,16 @@ static void conn_shutdown(struct Curl_easy *data, struct connectdata *conn)
 {
   DEBUGASSERT(conn);
   DEBUGASSERT(data);
-  infof(data, "Closing connection %ld\n", conn->connection_id);
+  infof(data, "Closing connection %ld", conn->connection_id);
+
+#ifndef USE_HYPER
+  if(conn->connect_state && conn->connect_state->prot_save) {
+    /* If this was closed with a CONNECT in progress, cleanup this temporary
+       struct arrangement */
+    data->req.p.http = NULL;
+    Curl_safefree(conn->connect_state->prot_save);
+  }
+#endif
 
   /* possible left-overs from the async name resolvers */
   Curl_resolver_cancel(data);
@@ -824,7 +840,7 @@ CURLcode Curl_disconnect(struct Curl_easy *data,
    * are other users of it
    */
   if(CONN_INUSE(conn) && !dead_connection) {
-    DEBUGF(infof(data, "Curl_disconnect when inuse: %zu\n", CONN_INUSE(conn)));
+    DEBUGF(infof(data, "Curl_disconnect when inuse: %zu", CONN_INUSE(conn)));
     return CURLE_OK;
   }
 
@@ -957,7 +973,7 @@ static bool conn_maxage(struct Curl_easy *data,
   idletime /= 1000; /* integer seconds is fine */
 
   if(idletime > data->set.maxage_conn) {
-    infof(data, "Too old connection (%ld seconds), disconnect it\n",
+    infof(data, "Too old connection (%ld seconds), disconnect it",
           idletime);
     return TRUE;
   }
@@ -1006,7 +1022,7 @@ static bool extract_if_dead(struct connectdata *conn,
     }
 
     if(dead) {
-      infof(data, "Connection %ld seems to be dead!\n", conn->connection_id);
+      infof(data, "Connection %ld seems to be dead!", conn->connection_id);
       Curl_conncache_remove_conn(data, conn, FALSE);
       return TRUE;
     }
@@ -1122,7 +1138,7 @@ ConnectionExists(struct Curl_easy *data,
     /* Max pipe length is zero (unlimited) for multiplexed connections */
     struct Curl_llist_element *curr;
 
-    infof(data, "Found bundle for host %s: %p [%s]\n",
+    infof(data, "Found bundle for host %s: %p [%s]",
           hostbundle, (void *)bundle, (bundle->multiuse == BUNDLE_MULTIPLEX ?
                                        "can multiplex" : "serially"));
 
@@ -1130,22 +1146,22 @@ ConnectionExists(struct Curl_easy *data,
     if(canmultiplex) {
       if(bundle->multiuse == BUNDLE_UNKNOWN) {
         if(data->set.pipewait) {
-          infof(data, "Server doesn't support multiplex yet, wait\n");
+          infof(data, "Server doesn't support multiplex yet, wait");
           *waitpipe = TRUE;
           CONNCACHE_UNLOCK(data);
           return FALSE; /* no re-use */
         }
 
-        infof(data, "Server doesn't support multiplex (yet)\n");
+        infof(data, "Server doesn't support multiplex (yet)");
         canmultiplex = FALSE;
       }
       if((bundle->multiuse == BUNDLE_MULTIPLEX) &&
          !Curl_multiplex_wanted(data->multi)) {
-        infof(data, "Could multiplex, but not asked to!\n");
+        infof(data, "Could multiplex, but not asked to!");
         canmultiplex = FALSE;
       }
       if(bundle->multiuse == BUNDLE_NO_MULTIUSE) {
-        infof(data, "Can not multiplex, even if we wanted to!\n");
+        infof(data, "Can not multiplex, even if we wanted to!");
         canmultiplex = FALSE;
       }
     }
@@ -1193,7 +1209,7 @@ ConnectionExists(struct Curl_easy *data,
              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\n",
+                  "Connection #%ld is still name resolving, can't reuse",
                   check->connection_id);
             continue;
           }
@@ -1202,7 +1218,7 @@ ConnectionExists(struct Curl_easy *data,
         if(check->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) {
           foundPendingCandidate = TRUE;
           /* Don't pick a connection that hasn't connected yet */
-          infof(data, "Connection #%ld isn't open enough, can't reuse\n",
+          infof(data, "Connection #%ld isn't open enough, can't reuse",
                 check->connection_id);
           continue;
         }
@@ -1357,7 +1373,7 @@ ConnectionExists(struct Curl_easy *data,
                                         &check->ssl_config)) {
               DEBUGF(infof(data,
                            "Connection #%ld has different SSL parameters, "
-                           "can't reuse\n",
+                           "can't reuse",
                            check->connection_id));
               continue;
             }
@@ -1365,7 +1381,7 @@ ConnectionExists(struct Curl_easy *data,
               foundPendingCandidate = TRUE;
               DEBUGF(infof(data,
                            "Connection #%ld has not started SSL connect, "
-                           "can't reuse\n",
+                           "can't reuse",
                            check->connection_id));
               continue;
             }
@@ -1452,14 +1468,14 @@ ConnectionExists(struct Curl_easy *data,
             /* Multiplexed connections can only be HTTP/2 for now */
             struct http_conn *httpc = &check->proto.httpc;
             if(multiplexed >= httpc->settings.max_concurrent_streams) {
-              infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)\n",
+              infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)",
                     multiplexed);
               continue;
             }
             else if(multiplexed >=
                     Curl_multi_max_concurrent_streams(data->multi)) {
               infof(data, "client side MAX_CONCURRENT_STREAMS reached"
-                    ", skip (%zu)\n",
+                    ", skip (%zu)",
                     multiplexed);
               continue;
             }
@@ -1467,7 +1483,7 @@ ConnectionExists(struct Curl_easy *data,
 #endif
           /* When not multiplexed, we have a match here! */
           chosen = check;
-          infof(data, "Multiplexed connection found!\n");
+          infof(data, "Multiplexed connection found!");
           break;
         }
         else {
@@ -1490,7 +1506,7 @@ ConnectionExists(struct Curl_easy *data,
 
   if(foundPendingCandidate && data->set.pipewait) {
     infof(data,
-          "Found pending candidate for reuse and CURLOPT_PIPEWAIT is set\n");
+          "Found pending candidate for reuse and CURLOPT_PIPEWAIT is set");
     *waitpipe = TRUE;
   }
 
@@ -1505,7 +1521,7 @@ void Curl_verboseconnect(struct Curl_easy *data,
                          struct connectdata *conn)
 {
   if(data->set.verbose)
-    infof(data, "Connected to %s (%s) port %ld (#%ld)\n",
+    infof(data, "Connected to %s (%s) port %u (#%ld)",
 #ifndef CURL_DISABLE_PROXY
           conn->bits.socksproxy ? conn->socks_proxy.host.dispname :
           conn->bits.httpproxy ? conn->http_proxy.host.dispname :
@@ -1577,12 +1593,12 @@ CURLcode Curl_idnconvert_hostname(struct Curl_easy *data,
 #else
       int flags = IDN2_NFC_INPUT;
 #endif
-      int rc = idn2_lookup_ul((const char *)host->name, &ace_hostname, flags);
+      int rc = IDN2_LOOKUP(host->name, &ace_hostname, flags);
       if(rc != IDN2_OK)
         /* fallback to TR46 Transitional mode for better IDNA2003
            compatibility */
-        rc = idn2_lookup_ul((const char *)host->name, &ace_hostname,
-                            IDN2_TRANSITIONAL);
+        rc = IDN2_LOOKUP(host->name, &ace_hostname,
+                         IDN2_TRANSITIONAL);
       if(rc == IDN2_OK) {
         host->encalloc = (char *)ace_hostname;
         /* change the name pointer to point to the encoded hostname */
@@ -1609,7 +1625,7 @@ CURLcode Curl_idnconvert_hostname(struct Curl_easy *data,
       return CURLE_URL_MALFORMAT;
     }
 #else
-    infof(data, "IDN support not present, can't parse Unicode domains\n");
+    infof(data, "IDN support not present, can't parse Unicode domains");
 #endif
   }
   return CURLE_OK;
@@ -1876,9 +1892,13 @@ static void zonefrom_url(CURLU *uh, struct Curl_easy *data,
 #else
       scopeidx = if_nametoindex(zoneid);
 #endif
-      if(!scopeidx)
-        infof(data, "Invalid zoneid: %s; %s\n", zoneid,
-              strerror(errno));
+      if(!scopeidx) {
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+        char buffer[STRERROR_LEN];
+        infof(data, "Invalid zoneid: %s; %s", zoneid,
+              Curl_strerror(errno, buffer, sizeof(buffer)));
+#endif
+      }
       else
         conn->scope_id = scopeidx;
     }
@@ -1934,7 +1954,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
                      CURLU_DISALLOW_USER : 0) |
                     (data->set.path_as_is ? CURLU_PATH_AS_IS : 0));
     if(uc) {
-      DEBUGF(infof(data, "curl_url_set rejected %s\n", data->state.url));
+      DEBUGF(infof(data, "curl_url_set rejected %s", data->state.url));
       return Curl_uc_to_curlcode(uc);
     }
 
@@ -1979,7 +1999,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
       }
       data->state.url = url;
       data->state.url_alloc = TRUE;
-      infof(data, "Switched from HTTP to HTTPS due to HSTS => %s\n",
+      infof(data, "Switched from HTTP to HTTPS due to HSTS => %s",
             data->state.url);
     }
   }
@@ -2333,7 +2353,7 @@ static char *detect_proxy(struct Curl_easy *data,
     }
   }
   if(proxy)
-    infof(data, "Uses proxy env variable %s == '%s'\n", envp, proxy);
+    infof(data, "Uses proxy env variable %s == '%s'", envp, proxy);
 
   return proxy;
 }
@@ -2448,7 +2468,7 @@ static CURLcode parse_proxy(struct Curl_easy *data,
     conn->bits.proxy_user_passwd = TRUE; /* enable it */
   }
 
-  curl_url_get(uhp, CURLUPART_PORT, &portptr, 0);
+  (void)curl_url_get(uhp, CURLUPART_PORT, &portptr, 0);
 
   if(portptr) {
     port = (int)strtol(portptr, NULL, 10);
@@ -2576,7 +2596,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
       no_proxy = curl_getenv(p);
     }
     if(no_proxy) {
-      infof(data, "Uses proxy env variable %s == '%s'\n", p, no_proxy);
+      infof(data, "Uses proxy env variable %s == '%s'", p, no_proxy);
     }
   }
 
@@ -2871,11 +2891,13 @@ static CURLcode override_login(struct Curl_easy *data,
   char **passwdp = &conn->passwd;
   char **optionsp = &conn->options;
 
+#ifndef CURL_DISABLE_NETRC
   if(data->set.use_netrc == CURL_NETRC_REQUIRED && conn->bits.user_passwd) {
     Curl_safefree(*userp);
     Curl_safefree(*passwdp);
     conn->bits.user_passwd = FALSE; /* disable user+password */
   }
+#endif
 
   if(data->set.str[STRING_OPTIONS]) {
     free(*optionsp);
@@ -2884,6 +2906,7 @@ static CURLcode override_login(struct Curl_easy *data,
       return CURLE_OUT_OF_MEMORY;
   }
 
+#ifndef CURL_DISABLE_NETRC
   conn->bits.netrc = FALSE;
   if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) {
     bool netrc_user_changed = FALSE;
@@ -2895,7 +2918,7 @@ static CURLcode override_login(struct Curl_easy *data,
                           &netrc_user_changed, &netrc_passwd_changed,
                           data->set.str[STRING_NETRC_FILE]);
     if(ret > 0) {
-      infof(data, "Couldn't find host %s in the %s file; using defaults\n",
+      infof(data, "Couldn't find host %s in the %s file; using defaults",
             conn->host.name, data->set.str[STRING_NETRC_FILE]);
     }
     else if(ret < 0) {
@@ -2909,6 +2932,7 @@ static CURLcode override_login(struct Curl_easy *data,
       conn->bits.user_passwd = TRUE; /* enable user+password */
     }
   }
+#endif
 
   /* for updated strings, we update them in the URL */
   if(*userp) {
@@ -2996,6 +3020,7 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data,
   char *host_portno;
   char *portptr;
   int port = -1;
+  CURLcode result = CURLE_OK;
 
 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
   (void) data;
@@ -3025,7 +3050,7 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data,
     if(*ptr == '%') {
       /* There might be a zone identifier */
       if(strncmp("%25", ptr, 3))
-        infof(data, "Please URL encode %% as %%25, see RFC 6874.\n");
+        infof(data, "Please URL encode %% as %%25, see RFC 6874.");
       ptr++;
       /* Allow unreserved characters as defined in RFC 3986 */
       while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') ||
@@ -3036,7 +3061,7 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data,
       /* yeps, it ended nicely with a bracket as well */
       *ptr++ = '\0';
     else
-      infof(data, "Invalid IPv6 address format\n");
+      infof(data, "Invalid IPv6 address format");
     portptr = ptr;
     /* Note that if this didn't end with a bracket, we still advanced the
      * hostptr first, but I can't see anything wrong with that as no host
@@ -3044,8 +3069,8 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data,
      */
 #else
     failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in!");
-    free(host_dup);
-    return CURLE_NOT_BUILT_IN;
+    result = CURLE_NOT_BUILT_IN;
+    goto error;
 #endif
   }
 
@@ -3058,10 +3083,10 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data,
     if(*host_portno) {
       long portparse = strtol(host_portno, &endp, 10);
       if((endp && *endp) || (portparse < 0) || (portparse > 65535)) {
-        infof(data, "No valid port number in connect to host string (%s)\n",
+        failf(data, "No valid port number in connect to host string (%s)",
               host_portno);
-        hostptr = NULL;
-        port = -1;
+        result = CURLE_SETOPT_OPTION_SYNTAX;
+        goto error;
       }
       else
         port = (int)portparse; /* we know it will fit */
@@ -3072,15 +3097,16 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data,
   if(hostptr) {
     *hostname_result = strdup(hostptr);
     if(!*hostname_result) {
-      free(host_dup);
-      return CURLE_OUT_OF_MEMORY;
+      result = CURLE_OUT_OF_MEMORY;
+      goto error;
     }
   }
 
   *port_result = port;
 
+  error:
   free(host_dup);
-  return CURLE_OK;
+  return result;
 }
 
 /*
@@ -3176,7 +3202,7 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data,
       conn->conn_to_host.name = host;
       conn->bits.conn_to_host = TRUE;
 
-      infof(data, "Connecting to hostname: %s\n", host);
+      infof(data, "Connecting to hostname: %s", host);
     }
     else {
       /* no "connect to host" */
@@ -3187,7 +3213,7 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data,
     if(port >= 0) {
       conn->conn_to_port = port;
       conn->bits.conn_to_port = TRUE;
-      infof(data, "Connecting to port: %d\n", port);
+      infof(data, "Connecting to port: %d", port);
     }
     else {
       /* no "connect to port" */
@@ -3248,7 +3274,7 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data,
       conn->conn_to_port = as->dst.port;
       conn->bits.conn_to_port = TRUE;
       conn->bits.altused = TRUE;
-      infof(data, "Alt-svc connecting from [%s]%s:%d to [%s]%s:%d\n",
+      infof(data, "Alt-svc connecting from [%s]%s:%d to [%s]%s:%d",
             Curl_alpnid2str(srcalpnid), host, conn->remote_port,
             Curl_alpnid2str(as->dst.alpnid), hostd, as->dst.port);
       if(srcalpnid != as->dst.alpnid) {
@@ -3356,9 +3382,12 @@ static CURLcode resolve_server(struct Curl_easy *data,
       if(rc == CURLRESOLV_PENDING)
         *async = TRUE;
 
-      else if(rc == CURLRESOLV_TIMEDOUT)
+      else if(rc == CURLRESOLV_TIMEDOUT) {
+        failf(data, "Failed to resolve host '%s' with timeout after %ld ms",
+              connhost->dispname,
+              Curl_timediff(Curl_now(), data->progress.t_startsingle));
         result = CURLE_OPERATION_TIMEDOUT;
-
+      }
       else if(!hostaddr) {
         failf(data, "Could not resolve host: %s", connhost->dispname);
         result = CURLE_COULDNT_RESOLVE_HOST;
@@ -3600,7 +3629,7 @@ static CURLcode create_conn(struct Curl_easy *data,
   if(result)
     goto out;
 
-  /* Check for overridden login details and set them accordingly so they
+  /* Check for overridden login details and set them accordingly so that
      they are known when protocol->setup_connection is called! */
   result = override_login(data, conn);
   if(result)
@@ -3736,6 +3765,8 @@ static CURLcode create_conn(struct Curl_easy *data,
   */
   data->set.ssl.primary.CApath = data->set.str[STRING_SSL_CAPATH];
   data->set.ssl.primary.CAfile = data->set.str[STRING_SSL_CAFILE];
+  data->set.ssl.primary.issuercert = data->set.str[STRING_SSL_ISSUERCERT];
+  data->set.ssl.primary.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT];
   data->set.ssl.primary.random_file = data->set.str[STRING_SSL_RANDOM_FILE];
   data->set.ssl.primary.egdsocket = data->set.str[STRING_SSL_EGDSOCKET];
   data->set.ssl.primary.cipher_list =
@@ -3763,8 +3794,11 @@ static CURLcode create_conn(struct Curl_easy *data,
   data->set.proxy_ssl.primary.cert_blob = data->set.blobs[BLOB_CERT_PROXY];
   data->set.proxy_ssl.primary.ca_info_blob =
     data->set.blobs[BLOB_CAINFO_PROXY];
+  data->set.proxy_ssl.primary.issuercert =
+    data->set.str[STRING_SSL_ISSUERCERT_PROXY];
+  data->set.proxy_ssl.primary.issuercert_blob =
+    data->set.blobs[BLOB_SSL_ISSUERCERT_PROXY];
   data->set.proxy_ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY];
-  data->set.proxy_ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT_PROXY];
   data->set.proxy_ssl.cert_type = data->set.str[STRING_CERT_TYPE_PROXY];
   data->set.proxy_ssl.key = data->set.str[STRING_KEY_PROXY];
   data->set.proxy_ssl.key_type = data->set.str[STRING_KEY_TYPE_PROXY];
@@ -3773,7 +3807,6 @@ static CURLcode create_conn(struct Curl_easy *data,
   data->set.proxy_ssl.key_blob = data->set.blobs[BLOB_KEY_PROXY];
 #endif
   data->set.ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE];
-  data->set.ssl.issuercert = data->set.str[STRING_SSL_ISSUERCERT];
   data->set.ssl.cert_type = data->set.str[STRING_CERT_TYPE];
   data->set.ssl.key = data->set.str[STRING_KEY];
   data->set.ssl.key_type = data->set.str[STRING_KEY_TYPE];
@@ -3787,9 +3820,7 @@ static CURLcode create_conn(struct Curl_easy *data,
   data->set.proxy_ssl.password = data->set.str[STRING_TLSAUTH_PASSWORD_PROXY];
 #endif
 #endif
-
   data->set.ssl.key_blob = data->set.blobs[BLOB_KEY];
-  data->set.ssl.issuercert_blob = data->set.blobs[BLOB_SSL_ISSUERCERT];
 
   if(!Curl_clone_primary_ssl_config(&data->set.ssl.primary,
                                     &conn->ssl_config)) {
@@ -3841,14 +3872,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\n",
+    infof(data, "Re-using existing connection! (#%ld) with %s %s",
           conn->connection_id,
           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\n",
+    infof(data, "Re-using existing connection! (#%ld) with host %s",
           conn->connection_id, conn->host.dispname);
 #endif
   }
@@ -3888,7 +3919,7 @@ static CURLcode create_conn(struct Curl_easy *data,
         if(conn_candidate)
           (void)Curl_disconnect(data, conn_candidate, FALSE);
         else {
-          infof(data, "No more connections allowed to host %s: %zu\n",
+          infof(data, "No more connections allowed to host %s: %zu",
                 bundlehost, max_host_connections);
           connections_available = FALSE;
         }
@@ -3908,13 +3939,13 @@ static CURLcode create_conn(struct Curl_easy *data,
       if(conn_candidate)
         (void)Curl_disconnect(data, conn_candidate, FALSE);
       else {
-        infof(data, "No connections available in cache\n");
+        infof(data, "No connections available in cache");
         connections_available = FALSE;
       }
     }
 
     if(!connections_available) {
-      infof(data, "No connections available.\n");
+      infof(data, "No connections available.");
 
       conn_free(conn);
       *in_connect = NULL;
@@ -3939,14 +3970,14 @@ static CURLcode create_conn(struct Curl_easy *data,
        connection based. */
     if((data->state.authhost.picked & (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) &&
        data->state.authhost.done) {
-      infof(data, "NTLM picked AND auth done set, clear picked!\n");
+      infof(data, "NTLM picked AND auth done set, clear picked!");
       data->state.authhost.picked = CURLAUTH_NONE;
       data->state.authhost.done = FALSE;
     }
 
     if((data->state.authproxy.picked & (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) &&
        data->state.authproxy.done) {
-      infof(data, "NTLM-proxy picked AND auth done set, clear picked!\n");
+      infof(data, "NTLM-proxy picked AND auth done set, clear picked!");
       data->state.authproxy.picked = CURLAUTH_NONE;
       data->state.authproxy.done = FALSE;
     }

+ 13 - 14
lib/urlapi.c

@@ -131,7 +131,7 @@ static const char *find_host_sep(const char *url)
  */
 static bool urlchar_needs_escaping(int c)
 {
-    return !(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c));
+  return !(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c));
 }
 
 /*
@@ -580,7 +580,7 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, char *hostname,
 }
 
 /* scan for byte values < 31 or 127 */
-static CURLUcode junkscan(const char *part)
+static bool junkscan(const char *part, unsigned int flags)
 {
   if(part) {
     static const char badbytes[]={
@@ -588,17 +588,18 @@ static CURLUcode junkscan(const char *part)
       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
-      0x7f,
-      0x00 /* null-terminate */
+      0x7f, 0x00 /* null-terminate */
     };
     size_t n = strlen(part);
     size_t nfine = strcspn(part, badbytes);
     if(nfine != n)
       /* since we don't know which part is scanned, return a generic error
          code */
-      return CURLUE_MALFORMED_INPUT;
+      return TRUE;
+    if(!(flags & CURLU_ALLOW_SPACE) && strchr(part, ' '))
+      return TRUE;
   }
-  return CURLUE_OK;
+  return FALSE;
 }
 
 static CURLUcode hostname_check(struct Curl_URL *u, char *hostname)
@@ -769,8 +770,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
   size_t schemelen = 0;
   size_t urllen;
 
-  if(!url)
-    return CURLUE_MALFORMED_INPUT;
+  DEBUGASSERT(url);
 
   /*************************************************************
    * Parse the URL.
@@ -884,9 +884,8 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
          !(flags & CURLU_NON_SUPPORT_SCHEME))
         return CURLUE_UNSUPPORTED_SCHEME;
 
-      if(junkscan(schemep))
+      if(junkscan(schemep, flags))
         return CURLUE_MALFORMED_INPUT;
-
     }
     else {
       /* no scheme! */
@@ -927,7 +926,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
     }
   }
 
-  if(junkscan(path))
+  if(junkscan(path, flags))
     return CURLUE_MALFORMED_INPUT;
 
   if((flags & CURLU_URLENCODE) && path[0]) {
@@ -991,7 +990,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
     /*
      * Parse the login details and strip them out of the host name.
      */
-    if(junkscan(hostname))
+    if(junkscan(hostname, flags))
       return CURLUE_MALFORMED_INPUT;
 
     result = parse_hostname_login(u, &hostname, flags);
@@ -1155,7 +1154,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
       const struct Curl_handler *h =
         Curl_builtin_scheme(u->scheme);
       if(h) {
-        msnprintf(portbuf, sizeof(portbuf), "%ld", h->defport);
+        msnprintf(portbuf, sizeof(portbuf), "%u", h->defport);
         ptr = portbuf;
       }
     }
@@ -1214,7 +1213,7 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
         /* there's no stored port number, but asked to deliver
            a default one for the scheme */
         if(h) {
-          msnprintf(portbuf, sizeof(portbuf), "%ld", h->defport);
+          msnprintf(portbuf, sizeof(portbuf), "%u", h->defport);
           port = portbuf;
         }
       }

+ 16 - 10
lib/urldata.h

@@ -246,6 +246,7 @@ struct ssl_primary_config {
   long version_max;      /* max supported version the client wants to use*/
   char *CApath;          /* certificate dir (doesn't work on windows) */
   char *CAfile;          /* certificate to verify peer against */
+  char *issuercert;      /* optional issuer certificate filename */
   char *clientcert;
   char *random_file;     /* path to file containing "random" data */
   char *egdsocket;       /* path to file containing the EGD daemon socket */
@@ -254,6 +255,7 @@ struct ssl_primary_config {
   char *pinned_key;
   struct curl_blob *cert_blob;
   struct curl_blob *ca_info_blob;
+  struct curl_blob *issuercert_blob;
   char *curves;          /* list of curves to use */
   BIT(verifypeer);       /* set TRUE if this is desired */
   BIT(verifyhost);       /* set TRUE if CN/SAN must match hostname */
@@ -265,8 +267,6 @@ struct ssl_config_data {
   struct ssl_primary_config primary;
   long certverifyresult; /* result from the certificate verification */
   char *CRLfile;   /* CRL to check certificate revocation */
-  char *issuercert;/* optional issuer certificate filename */
-  struct curl_blob *issuercert_blob;
   curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */
   void *fsslctxp;        /* parameter for call back */
   char *cert_type; /* format for certificate (default: PEM)*/
@@ -508,7 +508,9 @@ struct ConnectBits {
   BIT(ftp_use_data_ssl); /* Enabled SSL for the data connection */
   BIT(ftp_use_control_ssl); /* Enabled SSL for the control connection */
 #endif
+#ifndef CURL_DISABLE_NETRC
   BIT(netrc);         /* name+password provided by netrc */
+#endif
   BIT(bound); /* set true if bind() has already been done on this socket/
                  connection */
   BIT(multiplex); /* connection is multiplexed */
@@ -702,14 +704,15 @@ struct SingleRequest {
 #ifndef CURL_DISABLE_DOH
   struct dohdata *doh; /* DoH specific data for this request */
 #endif
-  BIT(header);       /* incoming data has HTTP header */
+  BIT(header);        /* incoming data has HTTP header */
   BIT(content_range); /* set TRUE if Content-Range: was found */
-  BIT(upload_done);  /* set to TRUE when doing chunked transfer-encoding
-                        upload and we're uploading the last chunk */
-  BIT(ignorebody);   /* we read a response-body but we ignore it! */
+  BIT(upload_done);   /* set to TRUE when doing chunked transfer-encoding
+                         upload and we're uploading the last chunk */
+  BIT(ignorebody);    /* we read a response-body but we ignore it! */
   BIT(http_bodyless); /* HTTP response status code is between 100 and 199,
                          204 or 304 */
-  BIT(chunk); /* if set, this is a chunked transfer-encoding */
+  BIT(chunk);         /* if set, this is a chunked transfer-encoding */
+  BIT(ignore_cl);     /* ignore content-length */
   BIT(upload_chunky); /* set TRUE if we are doing chunked transfer-encoding
                          on upload */
   BIT(getheader);    /* TRUE if header parsing is wanted */
@@ -1411,6 +1414,7 @@ struct UrlState {
   trailers_state trailers_state; /* whether we are sending trailers
                                        and what stage are we at */
 #ifdef USE_HYPER
+  bool hconnect;  /* set if a CONNECT request */
   CURLcode hresult; /* used to pass return codes back from hyper callbacks */
 #endif
 
@@ -1726,8 +1730,10 @@ struct UserDefined {
                                */
   curl_sshkeycallback ssh_keyfunc; /* key matching callback */
   void *ssh_keyfunc_userp;         /* custom pointer to callback */
+#ifndef CURL_DISABLE_NETRC
   enum CURL_NETRC_OPTION
        use_netrc;        /* defined in include/curl.h */
+#endif
   curl_usessl use_ssl;   /* if AUTH TLS is to be attempted etc, for FTP or
                             IMAP or POP3 or others! */
   long new_file_perms;    /* Permissions to use when creating remote files */
@@ -1847,9 +1853,9 @@ struct UserDefined {
   BIT(disallow_username_in_url); /* disallow username in url */
   BIT(doh); /* DNS-over-HTTPS enabled */
   BIT(doh_get); /* use GET for DoH requests, instead of POST */
-  BIT(doh_verifypeer);     /* DOH certificate peer verification */
-  BIT(doh_verifyhost);     /* DOH certificate hostname verification */
-  BIT(doh_verifystatus);   /* DOH certificate status verification */
+  BIT(doh_verifypeer);     /* DoH certificate peer verification */
+  BIT(doh_verifyhost);     /* DoH certificate hostname verification */
+  BIT(doh_verifystatus);   /* DoH certificate status verification */
   BIT(http09_allowed); /* allow HTTP/0.9 responses */
   BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some
                                 recipients */

+ 8 - 4
lib/vauth/digest_sspi.c

@@ -112,7 +112,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
 
   /* Ensure we have a valid challenge message */
   if(!Curl_bufref_len(chlg)) {
-    infof(data, "DIGEST-MD5 handshake failure (empty challenge message)\n");
+    infof(data, "DIGEST-MD5 handshake failure (empty challenge message)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
@@ -197,7 +197,9 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
      status == SEC_I_COMPLETE_AND_CONTINUE)
     s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
   else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
     char buffer[STRERROR_LEN];
+#endif
 
     s_pSecFn->FreeCredentialsHandle(&credentials);
     Curl_sspi_free_identity(p_identity);
@@ -207,7 +209,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
     if(status == SEC_E_INSUFFICIENT_MEMORY)
       return CURLE_OUT_OF_MEMORY;
 
-    infof(data, "schannel: InitializeSecurityContext failed: %s\n",
+    infof(data, "schannel: InitializeSecurityContext failed: %s",
           Curl_sspi_strerror(status, buffer, sizeof(buffer)));
 
     return CURLE_AUTH_ERROR;
@@ -461,7 +463,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
     if(status == SEC_E_OK)
       output_token_len = chlg_buf[4].cbBuffer;
     else { /* delete the context so a new one can be made */
-      infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx\n",
+      infof(data, "digest_sspi: MakeSignature failed, error 0x%08lx",
             (long)status);
       s_pSecFn->DeleteSecurityContext(digest->http_context);
       Curl_safefree(digest->http_context);
@@ -585,7 +587,9 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
        status == SEC_I_COMPLETE_AND_CONTINUE)
       s_pSecFn->CompleteAuthToken(&credentials, &resp_desc);
     else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
+#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
       char buffer[STRERROR_LEN];
+#endif
 
       s_pSecFn->FreeCredentialsHandle(&credentials);
 
@@ -597,7 +601,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
       if(status == SEC_E_INSUFFICIENT_MEMORY)
         return CURLE_OUT_OF_MEMORY;
 
-      infof(data, "schannel: InitializeSecurityContext failed: %s\n",
+      infof(data, "schannel: InitializeSecurityContext failed: %s",
             Curl_sspi_strerror(status, buffer, sizeof(buffer)));
 
       return CURLE_AUTH_ERROR;

+ 31 - 53
lib/vauth/krb5_gssapi.c

@@ -123,7 +123,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
 
   if(chlg) {
     if(!Curl_bufref_len(chlg)) {
-      infof(data, "GSSAPI handshake failure (empty challenge message)\n");
+      infof(data, "GSSAPI handshake failure (empty challenge message)");
       return CURLE_BAD_CONTENT_ENCODING;
     }
     input_token.value = (void *) Curl_bufref_ptr(chlg);
@@ -170,6 +170,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
  * Parameters:
  *
  * data    [in]     - The session handle.
+ * authzid [in]     - The authorization identity if some.
  * chlg    [in]     - Optional challenge message.
  * krb5    [in/out] - The Kerberos 5 data struct being used and modified.
  * out     [out]    - The result storage.
@@ -177,6 +178,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
  * Returns CURLE_OK on success.
  */
 CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
+                                                  const char *authzid,
                                                   const struct bufref *chlg,
                                                   struct kerberos5data *krb5,
                                                   struct bufref *out)
@@ -189,39 +191,17 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
   OM_uint32 unused_status;
   gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
   gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
-  unsigned int indata = 0;
-  unsigned int outdata = 0;
+  unsigned char *indata;
   gss_qop_t qop = GSS_C_QOP_DEFAULT;
   unsigned int sec_layer = 0;
   unsigned int max_size = 0;
-  gss_name_t username = GSS_C_NO_NAME;
-  gss_buffer_desc username_token;
 
   /* Ensure we have a valid challenge message */
   if(!Curl_bufref_len(chlg)) {
-    infof(data, "GSSAPI handshake failure (empty security message)\n");
+    infof(data, "GSSAPI handshake failure (empty security message)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
-  /* Get the fully qualified username back from the context */
-  major_status = gss_inquire_context(&minor_status, krb5->context,
-                                     &username, NULL, NULL, NULL, NULL,
-                                     NULL, NULL);
-  if(GSS_ERROR(major_status)) {
-    Curl_gss_log_error(data, "gss_inquire_context() failed: ",
-                       major_status, minor_status);
-    return CURLE_AUTH_ERROR;
-  }
-
-  /* Convert the username from internal format to a displayable token */
-  major_status = gss_display_name(&minor_status, username,
-                                  &username_token, NULL);
-  if(GSS_ERROR(major_status)) {
-    Curl_gss_log_error(data, "gss_display_name() failed: ",
-                       major_status, minor_status);
-    return CURLE_AUTH_ERROR;
-  }
-
   /* Setup the challenge "input" security buffer */
   input_token.value = (void *) Curl_bufref_ptr(chlg);
   input_token.length = Curl_bufref_len(chlg);
@@ -232,32 +212,32 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
   if(GSS_ERROR(major_status)) {
     Curl_gss_log_error(data, "gss_unwrap() failed: ",
                        major_status, minor_status);
-    gss_release_buffer(&unused_status, &username_token);
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
   /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
   if(output_token.length != 4) {
-    infof(data, "GSSAPI handshake failure (invalid security data)\n");
-    gss_release_buffer(&unused_status, &username_token);
+    infof(data, "GSSAPI handshake failure (invalid security data)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
-  /* Copy the data out and free the challenge as it is not required anymore */
-  memcpy(&indata, output_token.value, 4);
+  /* Extract the security layer and the maximum message size */
+  indata = output_token.value;
+  sec_layer = indata[0];
+  max_size = (indata[1] << 16) | (indata[2] << 8) | indata[3];
+
+  /* Free the challenge as it is not required anymore */
   gss_release_buffer(&unused_status, &output_token);
 
-  /* Extract the security layer */
-  sec_layer = indata & 0x000000FF;
+  /* Process the security layer */
   if(!(sec_layer & GSSAUTH_P_NONE)) {
-    infof(data, "GSSAPI handshake failure (invalid security layer)\n");
+    infof(data, "GSSAPI handshake failure (invalid security layer)");
 
-    gss_release_buffer(&unused_status, &username_token);
     return CURLE_BAD_CONTENT_ENCODING;
   }
+  sec_layer &= GSSAUTH_P_NONE;  /* We do not support a security layer */
 
-  /* Extract the maximum message size the server can receive */
-  max_size = ntohl(indata & 0xFFFFFF00);
+  /* Process the maximum message size the server can receive */
   if(max_size > 0) {
     /* The server has told us it supports a maximum receive buffer, however, as
        we don't require one unless we are encrypting data, we tell the server
@@ -266,26 +246,24 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
   }
 
   /* Allocate our message */
-  messagelen = sizeof(outdata) + username_token.length + 1;
+  messagelen = 4;
+  if(authzid)
+    messagelen += strlen(authzid);
   message = malloc(messagelen);
-  if(!message) {
-    gss_release_buffer(&unused_status, &username_token);
+  if(!message)
     return CURLE_OUT_OF_MEMORY;
-  }
 
-  /* Populate the message with the security layer, client supported receive
-     message size and authorization identity including the 0x00 based
-     terminator. Note: Despite RFC4752 Section 3.1 stating "The authorization
-     identity is not terminated with the zero-valued (%x00) octet." it seems
-     necessary to include it. */
-  outdata = htonl(max_size) | sec_layer;
-  memcpy(message, &outdata, sizeof(outdata));
-  memcpy(message + sizeof(outdata), username_token.value,
-         username_token.length);
-  message[messagelen - 1] = '\0';
-
-  /* Free the username token as it is not required anymore */
-  gss_release_buffer(&unused_status, &username_token);
+  /* Populate the message with the security layer and client supported receive
+     message size. */
+  message[0] = sec_layer & 0xFF;
+  message[1] = (max_size >> 16) & 0xFF;
+  message[2] = (max_size >> 8) & 0xFF;
+  message[3] = max_size & 0xFF;
+
+  /* If given, append the authorization identity. */
+
+  if(authzid && *authzid)
+    memcpy(message + 4, authzid, messagelen - 4);
 
   /* Setup the "authentication data" security buffer */
   input_token.value = message;

+ 36 - 46
lib/vauth/krb5_sspi.c

@@ -173,7 +173,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
 
   if(chlg) {
     if(!Curl_bufref_len(chlg)) {
-      infof(data, "GSSAPI handshake failure (empty challenge message)\n");
+      infof(data, "GSSAPI handshake failure (empty challenge message)");
       return CURLE_BAD_CONTENT_ENCODING;
     }
 
@@ -238,13 +238,15 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
  * Parameters:
  *
  * data    [in]     - The session handle.
- * chlg     [in]     - The optional challenge message.
+ * authzid [in]     - The authorization identity if some.
+ * chlg    [in]     - The optional challenge message.
  * krb5    [in/out] - The Kerberos 5 data struct being used and modified.
  * out     [out]    - The result storage.
  *
  * Returns CURLE_OK on success.
  */
 CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
+                                                  const char *authzid,
                                                   const struct bufref *chlg,
                                                   struct kerberos5data *krb5,
                                                   struct bufref *out)
@@ -260,19 +262,20 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
   SecBuffer wrap_buf[3];
   SecBufferDesc input_desc;
   SecBufferDesc wrap_desc;
-  unsigned long indata = 0;
-  unsigned long outdata = 0;
+  unsigned char *indata;
   unsigned long qop = 0;
   unsigned long sec_layer = 0;
   unsigned long max_size = 0;
   SecPkgContext_Sizes sizes;
-  SecPkgCredentials_Names names;
   SECURITY_STATUS status;
-  char *user_name;
+
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+  (void) data;
+#endif
 
   /* Ensure we have a valid challenge message */
   if(!Curl_bufref_len(chlg)) {
-    infof(data, "GSSAPI handshake failure (empty security message)\n");
+    infof(data, "GSSAPI handshake failure (empty security message)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
@@ -287,17 +290,6 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
   if(status != SEC_E_OK)
     return CURLE_AUTH_ERROR;
 
-  /* Get the fully qualified username back from the context */
-  status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials,
-                                                SECPKG_CRED_ATTR_NAMES,
-                                                &names);
-
-  if(status == SEC_E_INSUFFICIENT_MEMORY)
-    return CURLE_OUT_OF_MEMORY;
-
-  if(status != SEC_E_OK)
-    return CURLE_AUTH_ERROR;
-
   /* Setup the "input" security buffer */
   input_desc.ulVersion = SECBUFFER_VERSION;
   input_desc.cBuffers = 2;
@@ -312,29 +304,32 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
   /* Decrypt the inbound challenge and obtain the qop */
   status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop);
   if(status != SEC_E_OK) {
-    infof(data, "GSSAPI handshake failure (empty security message)\n");
+    infof(data, "GSSAPI handshake failure (empty security message)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
   /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
   if(input_buf[1].cbBuffer != 4) {
-    infof(data, "GSSAPI handshake failure (invalid security data)\n");
+    infof(data, "GSSAPI handshake failure (invalid security data)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
-  /* Copy the data out and free the challenge as it is not required anymore */
-  memcpy(&indata, input_buf[1].pvBuffer, 4);
+  /* Extract the security layer and the maximum message size */
+  indata = input_buf[1].pvBuffer;
+  sec_layer = indata[0];
+  max_size = (indata[1] << 16) | (indata[2] << 8) | indata[3];
+
+  /* Free the challenge as it is not required anymore */
   s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer);
 
-  /* Extract the security layer */
-  sec_layer = indata & 0x000000FF;
+  /* Process the security layer */
   if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) {
-    infof(data, "GSSAPI handshake failure (invalid security layer)\n");
+    infof(data, "GSSAPI handshake failure (invalid security layer)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
+  sec_layer &= KERB_WRAP_NO_ENCRYPT;  /* We do not support a security layer */
 
-  /* Extract the maximum message size the server can receive */
-  max_size = ntohl(indata & 0xFFFFFF00);
+  /* Process the maximum message size the server can receive */
   if(max_size > 0) {
     /* The server has told us it supports a maximum receive buffer, however, as
        we don't require one unless we are encrypting data, we tell the server
@@ -347,33 +342,28 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
   if(!trailer)
     return CURLE_OUT_OF_MEMORY;
 
-  /* Convert the user name to UTF8 when operating with Unicode */
-  user_name = curlx_convert_tchar_to_UTF8(names.sUserName);
-  if(!user_name) {
-    free(trailer);
-
-    return CURLE_OUT_OF_MEMORY;
-  }
-
   /* Allocate our message */
-  messagelen = sizeof(outdata) + strlen(user_name) + 1;
+  messagelen = 4;
+  if(authzid)
+    messagelen += strlen(authzid);
   message = malloc(messagelen);
   if(!message) {
     free(trailer);
-    curlx_unicodefree(user_name);
 
     return CURLE_OUT_OF_MEMORY;
   }
 
-  /* Populate the message with the security layer, client supported receive
-     message size and authorization identity including the 0x00 based
-     terminator. Note: Despite RFC4752 Section 3.1 stating "The authorization
-     identity is not terminated with the zero-valued (%x00) octet." it seems
-     necessary to include it. */
-  outdata = htonl(max_size) | sec_layer;
-  memcpy(message, &outdata, sizeof(outdata));
-  strcpy((char *) message + sizeof(outdata), user_name);
-  curlx_unicodefree(user_name);
+  /* Populate the message with the security layer and client supported receive
+     message size. */
+  message[0] = sec_layer & 0xFF;
+  message[1] = (max_size >> 16) & 0xFF;
+  message[2] = (max_size >> 8) & 0xFF;
+  message[3] = max_size & 0xFF;
+
+  /* If given, append the authorization identity. */
+
+  if(authzid && *authzid)
+    memcpy(message + 4, authzid, messagelen - 4);
 
   /* Allocate the padding */
   padding = malloc(sizes.cbBlockSize);

+ 4 - 4
lib/vauth/ntlm.c

@@ -182,7 +182,7 @@ static CURLcode ntlm_decode_type2_target(struct Curl_easy *data,
          (target_info_offset + target_info_len) > type2len ||
          target_info_offset < 48) {
         infof(data, "NTLM handshake failure (bad type-2 message). "
-              "Target Info Offset Len is set incorrect by the peer\n");
+              "Target Info Offset Len is set incorrect by the peer");
         return CURLE_BAD_CONTENT_ENCODING;
       }
 
@@ -286,7 +286,7 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data,
      (memcmp(type2, NTLMSSP_SIGNATURE, 8) != 0) ||
      (memcmp(type2 + 8, type2_marker, sizeof(type2_marker)) != 0)) {
     /* This was not a good enough type-2 message */
-    infof(data, "NTLM handshake failure (bad type-2 message)\n");
+    infof(data, "NTLM handshake failure (bad type-2 message)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
@@ -296,7 +296,7 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data,
   if(ntlm->flags & NTLMFLAG_NEGOTIATE_TARGET_INFO) {
     result = ntlm_decode_type2_target(data, type2ref, ntlm);
     if(result) {
-      infof(data, "NTLM handshake failure (bad type-2 message)\n");
+      infof(data, "NTLM handshake failure (bad type-2 message)");
       return result;
     }
   }
@@ -533,7 +533,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
   /* Get the machine's un-qualified host name as NTLM doesn't like the fully
      qualified domain name */
   if(Curl_gethostname(host, sizeof(host))) {
-    infof(data, "gethostname() failed, continuing without!\n");
+    infof(data, "gethostname() failed, continuing without!");
     hostlen = 0;
   }
   else {

+ 5 - 2
lib/vauth/ntlm_sspi.c

@@ -206,7 +206,7 @@ CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data,
 
   /* Ensure we have a valid type-2 message */
   if(!Curl_bufref_len(type2)) {
-    infof(data, "NTLM handshake failure (empty type-2 message)\n");
+    infof(data, "NTLM handshake failure (empty type-2 message)");
     return CURLE_BAD_CONTENT_ENCODING;
   }
 
@@ -253,6 +253,9 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
   unsigned long attrs;
   TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
 
+#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+  (void) data;
+#endif
   (void) passwdp;
   (void) userp;
 
@@ -309,7 +312,7 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
                                                &type_3_desc,
                                                &attrs, &expiry);
   if(status != SEC_E_OK) {
-    infof(data, "NTLM handshake failure (type-3 message): Status=%x\n",
+    infof(data, "NTLM handshake failure (type-3 message): Status=%x",
           status);
 
     if(status == SEC_E_INSUFFICIENT_MEMORY)

+ 2 - 3
lib/vauth/spnego_gssapi.c

@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2021, 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
@@ -137,8 +137,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
 
     /* Ensure we have a valid challenge message */
     if(!chlg) {
-      infof(data, "SPNEGO handshake failure (empty challenge message)\n");
-
+      infof(data, "SPNEGO handshake failure (empty challenge message)");
       return CURLE_BAD_CONTENT_ENCODING;
     }
 

+ 2 - 3
lib/vauth/spnego_sspi.c

@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *
- * Copyright (C) 1998 - 2020, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2021, 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
@@ -191,8 +191,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
 
     /* Ensure we have a valid challenge message */
     if(!chlg) {
-      infof(data, "SPNEGO handshake failure (empty challenge message)\n");
-
+      infof(data, "SPNEGO handshake failure (empty challenge message)");
       return CURLE_BAD_CONTENT_ENCODING;
     }
 

+ 1 - 0
lib/vauth/vauth.h

@@ -194,6 +194,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
 /* This is used to generate a base64 encoded GSSAPI (Kerberos V5) security
    token message */
 CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
+                                                  const char *authzid,
                                                   const struct bufref *chlg,
                                                   struct kerberos5data *krb5,
                                                   struct bufref *out);

+ 6 - 8
lib/version.c

@@ -75,28 +75,26 @@
 #endif
 
 #ifdef HAVE_BROTLI
-static size_t brotli_version(char *buf, size_t bufsz)
+static void brotli_version(char *buf, size_t bufsz)
 {
   uint32_t brotli_version = BrotliDecoderVersion();
   unsigned int major = brotli_version >> 24;
   unsigned int minor = (brotli_version & 0x00FFFFFF) >> 12;
   unsigned int patch = brotli_version & 0x00000FFF;
-
-  return msnprintf(buf, bufsz, "%u.%u.%u", major, minor, patch);
+  (void)msnprintf(buf, bufsz, "%u.%u.%u", major, minor, patch);
 }
 #endif
 
 #ifdef HAVE_ZSTD
-static size_t zstd_version(char *buf, size_t bufsz)
+static void zstd_version(char *buf, size_t bufsz)
 {
   unsigned long zstd_version = (unsigned long)ZSTD_versionNumber();
   unsigned int major = (unsigned int)(zstd_version / (100 * 100));
   unsigned int minor = (unsigned int)((zstd_version -
-                         (major * 100 * 100)) / 100);
+                                       (major * 100 * 100)) / 100);
   unsigned int patch = (unsigned int)(zstd_version -
-                         (major * 100 * 100) - (minor * 100));
-
-  return msnprintf(buf, bufsz, "%u.%u.%u", major, minor, patch);
+                                      (major * 100 * 100) - (minor * 100));
+  (void)msnprintf(buf, bufsz, "%u.%u.%u", major, minor, patch);
 }
 #endif
 

+ 79 - 126
lib/vquic/ngtcp2.c

@@ -28,6 +28,9 @@
 #include <nghttp3/nghttp3.h>
 #ifdef USE_OPENSSL
 #include <openssl/err.h>
+#include <ngtcp2/ngtcp2_crypto_openssl.h>
+#elif defined(USE_GNUTLS)
+#include <ngtcp2/ngtcp2_crypto_gnutls.h>
 #endif
 #include "urldata.h"
 #include "sendf.h"
@@ -86,7 +89,8 @@ struct h3out {
 #define QUIC_PRIORITY \
   "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
   "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
-  "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1"
+  "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
+  "%DISABLE_TLS13_COMPAT_MODE"
 #endif
 
 static CURLcode ng_process_ingress(struct Curl_easy *data,
@@ -116,42 +120,6 @@ static void quic_printf(void *user_data, const char *fmt, ...)
 }
 #endif
 
-#ifdef USE_OPENSSL
-static ngtcp2_crypto_level
-quic_from_ossl_level(OSSL_ENCRYPTION_LEVEL ossl_level)
-{
-  switch(ossl_level) {
-  case ssl_encryption_initial:
-    return NGTCP2_CRYPTO_LEVEL_INITIAL;
-  case ssl_encryption_early_data:
-    return NGTCP2_CRYPTO_LEVEL_EARLY;
-  case ssl_encryption_handshake:
-    return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
-  case ssl_encryption_application:
-    return NGTCP2_CRYPTO_LEVEL_APPLICATION;
-  default:
-    assert(0);
-  }
-}
-#elif defined(USE_GNUTLS)
-static ngtcp2_crypto_level
-quic_from_gtls_level(gnutls_record_encryption_level_t gtls_level)
-{
-  switch(gtls_level) {
-  case GNUTLS_ENCRYPTION_LEVEL_INITIAL:
-    return NGTCP2_CRYPTO_LEVEL_INITIAL;
-  case GNUTLS_ENCRYPTION_LEVEL_EARLY:
-    return NGTCP2_CRYPTO_LEVEL_EARLY;
-  case GNUTLS_ENCRYPTION_LEVEL_HANDSHAKE:
-    return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
-  case GNUTLS_ENCRYPTION_LEVEL_APPLICATION:
-    return NGTCP2_CRYPTO_LEVEL_APPLICATION;
-  default:
-    assert(0);
-  }
-}
-#endif
-
 static void qlog_callback(void *user_data, uint32_t flags,
                           const void *data, size_t datalen)
 {
@@ -222,27 +190,9 @@ static int write_client_handshake(struct quicsocket *qs,
                                   ngtcp2_crypto_level level,
                                   const uint8_t *data, size_t len)
 {
-  struct quic_handshake *crypto_data;
   int rv;
 
-  crypto_data = &qs->crypto_data[level];
-  if(!crypto_data->buf) {
-    crypto_data->buf = malloc(4096);
-    if(!crypto_data->buf)
-      return 0;
-    crypto_data->alloclen = 4096;
-  }
-
-  /* TODO Just pretend that handshake does not grow more than 4KiB for
-     now */
-  assert(crypto_data->len + len <= crypto_data->alloclen);
-
-  memcpy(&crypto_data->buf[crypto_data->len], data, len);
-  crypto_data->len += len;
-
-  rv = ngtcp2_conn_submit_crypto_data(
-    qs->qconn, level, (uint8_t *)(&crypto_data->buf[crypto_data->len] - len),
-    len);
+  rv = ngtcp2_conn_submit_crypto_data(qs->qconn, level, data, len);
   if(rv) {
     H3BUGF(fprintf(stderr, "write_client_handshake failed\n"));
   }
@@ -259,7 +209,7 @@ static int quic_set_encryption_secrets(SSL *ssl,
                                        size_t secretlen)
 {
   struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
-  int level = quic_from_ossl_level(ossl_level);
+  int level = ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
 
   if(ngtcp2_crypto_derive_and_install_rx_key(
        qs->qconn, NULL, NULL, NULL, level, rx_secret, secretlen) != 0)
@@ -281,7 +231,8 @@ static int quic_add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
                                    const uint8_t *data, size_t len)
 {
   struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
-  ngtcp2_crypto_level level = quic_from_ossl_level(ossl_level);
+  ngtcp2_crypto_level level =
+      ngtcp2_crypto_openssl_from_ossl_encryption_level(ossl_level);
 
   return write_client_handshake(qs, level, data, len);
 }
@@ -369,7 +320,8 @@ static int secret_func(gnutls_session_t ssl,
                        const void *tx_secret, size_t secretlen)
 {
   struct quicsocket *qs = gnutls_session_get_ptr(ssl);
-  int level = quic_from_gtls_level(gtls_level);
+  int level =
+      ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level(gtls_level);
 
   if(level != NGTCP2_CRYPTO_LEVEL_EARLY &&
      ngtcp2_crypto_derive_and_install_rx_key(
@@ -394,7 +346,8 @@ static int read_func(gnutls_session_t ssl,
                      size_t len)
 {
   struct quicsocket *qs = gnutls_session_get_ptr(ssl);
-  ngtcp2_crypto_level level = quic_from_gtls_level(gtls_level);
+  ngtcp2_crypto_level level =
+      ngtcp2_crypto_gnutls_from_gnutls_record_encryption_level(gtls_level);
   int rv;
 
   if(htype == GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC)
@@ -542,22 +495,6 @@ static int quic_init_ssl(struct quicsocket *qs)
 }
 #endif
 
-static int
-cb_recv_crypto_data(ngtcp2_conn *tconn, ngtcp2_crypto_level crypto_level,
-                    uint64_t offset,
-                    const uint8_t *data, size_t datalen,
-                    void *user_data)
-{
-  (void)offset;
-  (void)user_data;
-
-  if(ngtcp2_crypto_read_write_crypto_data(tconn, crypto_level, data,
-                                          datalen) != 0)
-    return NGTCP2_ERR_CRYPTO;
-
-  return 0;
-}
-
 static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
 {
   (void)user_data;
@@ -622,8 +559,8 @@ cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id,
   return 0;
 }
 
-static int cb_stream_close(ngtcp2_conn *tconn, int64_t stream_id,
-                           uint64_t app_error_code,
+static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags,
+                           int64_t stream_id, uint64_t app_error_code,
                            void *user_data, void *stream_user_data)
 {
   struct quicsocket *qs = (struct quicsocket *)user_data;
@@ -632,6 +569,10 @@ static int cb_stream_close(ngtcp2_conn *tconn, int64_t stream_id,
   (void)stream_user_data;
   /* stream is closed... */
 
+  if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) {
+    app_error_code = NGHTTP3_H3_NO_ERROR;
+  }
+
   rv = nghttp3_conn_close_stream(qs->h3conn, stream_id,
                                  app_error_code);
   if(rv) {
@@ -652,7 +593,25 @@ static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id,
   (void)app_error_code;
   (void)stream_user_data;
 
-  rv = nghttp3_conn_reset_stream(qs->h3conn, stream_id);
+  rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id);
+  if(rv) {
+    return NGTCP2_ERR_CALLBACK_FAILURE;
+  }
+
+  return 0;
+}
+
+static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id,
+                                  uint64_t app_error_code, void *user_data,
+                                  void *stream_user_data)
+{
+  struct quicsocket *qs = (struct quicsocket *)user_data;
+  int rv;
+  (void)tconn;
+  (void)app_error_code;
+  (void)stream_user_data;
+
+  rv = nghttp3_conn_shutdown_stream_read(qs->h3conn, stream_id);
   if(rv) {
     return NGTCP2_ERR_CALLBACK_FAILURE;
   }
@@ -712,14 +671,13 @@ static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid,
 static ngtcp2_callbacks ng_callbacks = {
   ngtcp2_crypto_client_initial_cb,
   NULL, /* recv_client_initial */
-  cb_recv_crypto_data,
+  ngtcp2_crypto_recv_crypto_data_cb,
   cb_handshake_completed,
   NULL, /* recv_version_negotiation */
   ngtcp2_crypto_encrypt_cb,
   ngtcp2_crypto_decrypt_cb,
   ngtcp2_crypto_hp_mask_cb,
   cb_recv_stream_data,
-  NULL, /* acked_crypto_offset */
   cb_acked_stream_data_offset,
   NULL, /* stream_open */
   cb_stream_close,
@@ -744,7 +702,9 @@ static ngtcp2_callbacks ng_callbacks = {
   ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
   NULL, /* recv_datagram */
   NULL, /* ack_datagram */
-  NULL  /* lost_datagram */
+  NULL, /* lost_datagram */
+  NULL, /* get_path_challenge_data */
+  cb_stream_stop_sending
 };
 
 /*
@@ -778,7 +738,7 @@ CURLcode Curl_quic_connect(struct Curl_easy *data,
     return CURLE_BAD_FUNCTION_ARGUMENT;
   }
 
-  infof(data, "Connect socket %d over QUIC to %s:%d\n",
+  infof(data, "Connect socket %d over QUIC to %s:%d",
         sockfd, ipbuf, port);
 
   qs->version = NGTCP2_PROTO_VER_MAX;
@@ -827,15 +787,14 @@ CURLcode Curl_quic_connect(struct Curl_easy *data,
 }
 
 /*
- * Store ngtp2 version info in this buffer, Prefix with a space.  Return total
- * length written.
+ * Store ngtcp2 version info in this buffer.
  */
-int Curl_quic_ver(char *p, size_t len)
+void Curl_quic_ver(char *p, size_t len)
 {
   const ngtcp2_info *ng2 = ngtcp2_version(0);
   nghttp3_info *ht3 = nghttp3_version(0);
-  return msnprintf(p, len, "ngtcp2/%s nghttp3/%s",
-                   ng2->version_str, ht3->version_str);
+  (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s",
+                  ng2->version_str, ht3->version_str);
 }
 
 static int ng_getsock(struct Curl_easy *data, struct connectdata *conn,
@@ -859,7 +818,6 @@ static int ng_getsock(struct Curl_easy *data, struct connectdata *conn,
 
 static void qs_disconnect(struct quicsocket *qs)
 {
-  int i;
   if(!qs->conn) /* already closed */
     return;
   qs->conn = NULL;
@@ -880,8 +838,6 @@ static void qs_disconnect(struct quicsocket *qs)
     qs->cred = NULL;
   }
 #endif
-  for(i = 0; i < 3; i++)
-    Curl_safefree(qs->crypto_data[i].buf);
   nghttp3_conn_del(qs->h3conn);
   ngtcp2_conn_del(qs->qconn);
 #ifdef USE_OPENSSL
@@ -951,7 +907,7 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
   (void)stream_id;
   (void)app_error_code;
   (void)user_data;
-  H3BUGF(infof(data, "cb_h3_stream_close CALLED\n"));
+  H3BUGF(infof(data, "cb_h3_stream_close CALLED"));
 
   stream->closed = TRUE;
   Curl_expire(data, 0, EXPIRE_QUIC);
@@ -1143,14 +1099,10 @@ static nghttp3_callbacks ngh3_callbacks = {
   NULL, /* begin_trailers */
   cb_h3_recv_header,
   NULL, /* end_trailers */
-  NULL, /* http_begin_push_promise */
-  NULL, /* http_recv_push_promise */
-  NULL, /* http_end_push_promise */
-  NULL, /* http_cancel_push */
   cb_h3_send_stop_sending,
-  NULL, /* push_stream */
   NULL, /* end_stream */
   NULL, /* reset_stream */
+  NULL /* shutdown */
 };
 
 static int init_ngh3_conn(struct quicsocket *qs)
@@ -1287,7 +1239,7 @@ static ssize_t ngh3_stream_recv(struct Curl_easy *data,
     return 0;
   }
 
-  infof(data, "ngh3_stream_recv returns 0 bytes and EAGAIN\n");
+  infof(data, "ngh3_stream_recv returns 0 bytes and EAGAIN");
   *curlcode = CURLE_AGAIN;
   return -1;
 }
@@ -1304,7 +1256,7 @@ static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
   if(!data->set.postfields) {
     stream->h3out->used -= datalen;
     H3BUGF(infof(data,
-                 "cb_h3_acked_stream_data, %zd bytes, %zd left unacked\n",
+                 "cb_h3_acked_stream_data, %zd bytes, %zd left unacked",
                  datalen, stream->h3out->used));
     DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE);
 
@@ -1366,13 +1318,13 @@ static ssize_t cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id,
       if(!stream->upload_left)
         *pflags = NGHTTP3_DATA_FLAG_EOF;
     }
-    H3BUGF(infof(data, "cb_h3_readfunction %zd bytes%s (at %zd unacked)\n",
+    H3BUGF(infof(data, "cb_h3_readfunction %zd bytes%s (at %zd unacked)",
                  nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
                  out->used));
   }
   if(stream->upload_done && !stream->upload_len &&
      (stream->upload_left <= 0)) {
-    H3BUGF(infof(data, "!!!!!!!!! cb_h3_readfunction sets EOF\n"));
+    H3BUGF(infof(data, "!!!!!!!!! cb_h3_readfunction sets EOF"));
     *pflags = NGHTTP3_DATA_FLAG_EOF;
     return nread ? 1 : 0;
   }
@@ -1565,7 +1517,7 @@ static CURLcode http_request(struct Curl_easy *data, const void *mem,
     if(acc > MAX_ACC) {
       infof(data, "http_request: Warning: The cumulative length of all "
             "headers exceeds %d bytes and that could cause the "
-            "stream to be rejected.\n", MAX_ACC);
+            "stream to be rejected.", MAX_ACC);
     }
   }
 
@@ -1611,7 +1563,7 @@ static CURLcode http_request(struct Curl_easy *data, const void *mem,
 
   Curl_safefree(nva);
 
-  infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)\n",
+  infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)",
         stream3_id, (void *)data);
 
   return CURLE_OK;
@@ -1641,7 +1593,7 @@ static ssize_t ngh3_stream_send(struct Curl_easy *data,
     sent = len;
   }
   else {
-    H3BUGF(infof(data, "ngh3_stream_send() wants to send %zd bytes\n",
+    H3BUGF(infof(data, "ngh3_stream_send() wants to send %zd bytes",
                  len));
     if(!stream->upload_len) {
       stream->upload_mem = mem;
@@ -1660,6 +1612,12 @@ static ssize_t ngh3_stream_send(struct Curl_easy *data,
     return -1;
   }
 
+  /* Reset post upload buffer after resumed. */
+  if(stream->upload_mem) {
+    stream->upload_mem = NULL;
+    stream->upload_len = 0;
+  }
+
   *curlcode = CURLE_OK;
   return sent;
 }
@@ -1758,8 +1716,7 @@ static CURLcode ng_flush_egress(struct Curl_easy *data,
   int rv;
   ssize_t sent;
   ssize_t outlen;
-  uint8_t out[NGTCP2_MAX_PKTLEN_IPV4];
-  size_t pktlen;
+  uint8_t out[NGTCP2_MAX_UDP_PAYLOAD_SIZE];
   ngtcp2_path_storage ps;
   ngtcp2_tstamp ts = timestamp();
   struct sockaddr_storage remote_addr;
@@ -1772,19 +1729,6 @@ static CURLcode ng_flush_egress(struct Curl_easy *data,
   ssize_t ndatalen;
   uint32_t flags;
 
-  switch(qs->local_addr.ss_family) {
-  case AF_INET:
-    pktlen = NGTCP2_MAX_PKTLEN_IPV4;
-    break;
-#ifdef ENABLE_IPV6
-  case AF_INET6:
-    pktlen = NGTCP2_MAX_PKTLEN_IPV6;
-    break;
-#endif
-  default:
-    assert(0);
-  }
-
   rv = ngtcp2_conn_handle_expiry(qs->qconn, ts);
   if(rv) {
     failf(data, "ngtcp2_conn_handle_expiry returned error: %s",
@@ -1811,15 +1755,16 @@ static CURLcode ng_flush_egress(struct Curl_easy *data,
 
     flags = NGTCP2_WRITE_STREAM_FLAG_MORE |
             (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0);
-    outlen = ngtcp2_conn_writev_stream(qs->qconn, &ps.path, NULL, out, pktlen,
+    outlen = ngtcp2_conn_writev_stream(qs->qconn, &ps.path, NULL, out,
+                                       sizeof(out),
                                        &ndatalen, flags, stream_id,
                                        (const ngtcp2_vec *)vec, veccnt, ts);
     if(outlen == 0) {
       break;
     }
     if(outlen < 0) {
-      if(outlen == NGTCP2_ERR_STREAM_DATA_BLOCKED ||
-         outlen == NGTCP2_ERR_STREAM_SHUT_WR) {
+      switch(outlen) {
+      case NGTCP2_ERR_STREAM_DATA_BLOCKED:
         assert(ndatalen == -1);
         rv = nghttp3_conn_block_stream(qs->h3conn, stream_id);
         if(rv) {
@@ -1828,8 +1773,17 @@ static CURLcode ng_flush_egress(struct Curl_easy *data,
           return CURLE_SEND_ERROR;
         }
         continue;
-      }
-      else if(outlen == NGTCP2_ERR_WRITE_MORE) {
+      case NGTCP2_ERR_STREAM_SHUT_WR:
+        assert(ndatalen == -1);
+        rv = nghttp3_conn_shutdown_stream_write(qs->h3conn, stream_id);
+        if(rv) {
+          failf(data,
+                "nghttp3_conn_shutdown_stream_write returned error: %s\n",
+                nghttp3_strerror(rv));
+          return CURLE_SEND_ERROR;
+        }
+        continue;
+      case NGTCP2_ERR_WRITE_MORE:
         assert(ndatalen >= 0);
         rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen);
         if(rv) {
@@ -1838,8 +1792,7 @@ static CURLcode ng_flush_egress(struct Curl_easy *data,
           return CURLE_SEND_ERROR;
         }
         continue;
-      }
-      else {
+      default:
         assert(ndatalen == -1);
         failf(data, "ngtcp2_conn_writev_stream returned error: %s",
               ngtcp2_strerror((int)outlen));

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