Browse Source

curl 2018-10-30 (19667715)

Code extracted from:

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

at commit 196677150f711a96c38ed123e621f1d4e995b2e5 (curl-7_62_0).
Curl Upstream 7 years ago
parent
commit
9835e90750
100 changed files with 3656 additions and 1605 deletions
  1. 0 1
      CMake/CMakeConfigurableFile.in
  2. 16 0
      CMake/CurlTests.c
  3. 7 59
      CMake/curl-config.cmake.in
  4. 54 46
      CMakeLists.txt
  5. 21 4
      include/curl/curl.h
  6. 4 4
      include/curl/curlver.h
  7. 10 0
      include/curl/easy.h
  8. 1 1
      include/curl/system.h
  9. 4 2
      include/curl/typecheck-gcc.h
  10. 120 0
      include/curl/urlapi.h
  11. 6 12
      lib/CMakeLists.txt
  12. 5 4
      lib/Makefile.inc
  13. 0 1
      lib/amigaos.h
  14. 5 1
      lib/arpa_telnet.h
  15. 11 15
      lib/cookie.c
  16. 3 3
      lib/curl_config.h.cmake
  17. 0 1
      lib/curl_ldap.h
  18. 17 1
      lib/curl_ntlm_wb.c
  19. 2 2
      lib/curl_path.c
  20. 1 1
      lib/curl_path.h
  21. 3 1
      lib/curl_rtmp.c
  22. 4 1
      lib/curl_setup.h
  23. 0 1
      lib/curl_setup_once.h
  24. 4 3
      lib/curl_sspi.c
  25. 11 3
      lib/curl_threads.c
  26. 2 1
      lib/curl_threads.h
  27. 0 1
      lib/curlx.h
  28. 1 1
      lib/dict.c
  29. 920 0
      lib/doh.c
  30. 105 0
      lib/doh.h
  31. 2 0
      lib/dotdot.c
  32. 1 1
      lib/dotdot.h
  33. 19 4
      lib/easy.c
  34. 0 1
      lib/easyif.h
  35. 12 8
      lib/escape.c
  36. 2 2
      lib/escape.h
  37. 16 17
      lib/file.c
  38. 0 1
      lib/file.h
  39. 26 20
      lib/ftp.c
  40. 2 0
      lib/ftp.h
  41. 0 1
      lib/getinfo.c
  42. 1 1
      lib/gopher.c
  43. 0 25
      lib/hostasyn.c
  44. 0 1
      lib/hostcheck.h
  45. 67 11
      lib/hostip.c
  46. 7 6
      lib/hostip.h
  47. 138 107
      lib/http.c
  48. 11 8
      lib/http.h
  49. 101 34
      lib/http2.c
  50. 0 1
      lib/http2.h
  51. 0 1
      lib/http_chunks.h
  52. 7 7
      lib/http_proxy.c
  53. 48 36
      lib/imap.c
  54. 1 0
      lib/imap.h
  55. 0 1
      lib/inet_ntop.h
  56. 0 1
      lib/inet_pton.h
  57. 3 3
      lib/krb5.c
  58. 12 6
      lib/ldap.c
  59. 0 1
      lib/llist.h
  60. 1 1
      lib/md4.c
  61. 1 1
      lib/md5.c
  62. 42 35
      lib/multi.c
  63. 39 9
      lib/netrc.c
  64. 2 1
      lib/nonblock.c
  65. 0 1
      lib/nonblock.h
  66. 5 2
      lib/nwlib.c
  67. 0 1
      lib/parsedate.h
  68. 1 4
      lib/pop3.c
  69. 0 1
      lib/progress.h
  70. 3 1
      lib/rand.c
  71. 14 13
      lib/rtsp.c
  72. 0 1
      lib/rtsp.h
  73. 4 2
      lib/security.c
  74. 0 1
      lib/select.h
  75. 1 1
      lib/sendf.h
  76. 31 0
      lib/setopt.c
  77. 0 1
      lib/slist.c
  78. 0 1
      lib/slist.h
  79. 3 2
      lib/smb.c
  80. 3 6
      lib/smtp.c
  81. 0 1
      lib/sockaddr.h
  82. 1 2
      lib/socks.c
  83. 0 1
      lib/socks.h
  84. 0 1
      lib/splay.c
  85. 1 1
      lib/ssh.c
  86. 1 1
      lib/strdup.c
  87. 3 6
      lib/strerror.c
  88. 0 1
      lib/telnet.h
  89. 2 2
      lib/tftp.c
  90. 0 1
      lib/tftp.h
  91. 20 3
      lib/timeval.c
  92. 28 340
      lib/transfer.c
  93. 0 1
      lib/transfer.h
  94. 180 654
      lib/url.c
  95. 20 5
      lib/url.h
  96. 33 0
      lib/urlapi-int.h
  97. 1340 0
      lib/urlapi.c
  98. 61 22
      lib/urldata.h
  99. 2 2
      lib/vauth/cleartext.c
  100. 1 1
      lib/vauth/cram.c

+ 0 - 1
CMake/CMakeConfigurableFile.in

@@ -1,2 +1 @@
 @CMAKE_CONFIGURABLE_FILE_CONTENT@
 @CMAKE_CONFIGURABLE_FILE_CONTENT@
-

+ 16 - 0
CMake/CurlTests.c

@@ -549,3 +549,19 @@ main() {
   return 0;
   return 0;
 }
 }
 #endif
 #endif
+#ifdef HAVE_CLOCK_GETTIME_MONOTONIC
+#include <time.h>
+int
+main() {
+  struct timespec ts = {0, 0}; 
+  clock_gettime(CLOCK_MONOTONIC, &ts); 
+  return 0;
+}
+#endif
+#ifdef HAVE_BUILTIN_AVAILABLE
+int
+main() {
+  if(__builtin_available(macOS 10.12, *)) {}
+  return 0;
+}
+#endif

+ 7 - 59
CMake/curl-config.cmake.in

@@ -1,64 +1,12 @@
-
-get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
-
-if(NOT CURL_FIND_COMPONENTS)
-  set(CURL_FIND_COMPONENTS curl libcurl)
-  if(CURL_FIND_REQUIRED)
-    set(CURL_FIND_REQUIRED_curl TRUE)
-    set(CURL_FIND_REQUIRED_libcurl TRUE)
-  endif()
-endif()
+@PACKAGE_INIT@
 
 
 include(CMakeFindDependencyMacro)
 include(CMakeFindDependencyMacro)
-if(CURL_FIND_REQUIRED_libcurl)
-    find_dependency(OpenSSL "@OPENSSL_VERSION_MAJOR@")
+if(@USE_OPENSSL@)
+  find_dependency(OpenSSL @OPENSSL_VERSION_MAJOR@)
 endif()
 endif()
-
-set(_curl_missing_components)
-foreach(_comp ${CURL_FIND_COMPONENTS})
-  if(EXISTS "${_DIR}/${_comp}-target.cmake")
-    include("${_DIR}/${_comp}-target.cmake")
-    set(CURL_${_comp}_FOUND TRUE)
-  else()
-    set(CURL_${_comp}_FOUND FALSE)
-    if(CURL_FIND_REQUIRED_${_comp})
-      set(CURL_FOUND FALSE)
-      list(APPEND _curl_missing_components ${_comp})
-    endif()
-  endif()
-endforeach()
-
-if(_curl_missing_components)
-  set(CURL_NOT_FOUND_MESSAGE "Following required components not found: " ${_curl_missing_components})
-else()
-  if(TARGET CURL::libcurl)
-    string(TOUPPER "${CMAKE_BUILD_TYPE}" _curl_current_config)
-    if(NOT _curl_current_config)
-      set(_curl_current_config "NOCONFIG")
-    endif()
-    get_target_property(_curl_configurations CURL::libcurl IMPORTED_CONFIGURATIONS)
-    list(FIND _curl_configurations "${_curl_current_config}" _i)
-    if(_i LESS 0)
-      set(_curl_config "RELEASE")
-      list(FIND _curl_configurations "${_curl_current_config}" _i)
-      if(_i LESS 0)
-        set(_curl_config "NOCONFIG")
-        list(FIND _curl_configurations "${_curl_current_config}" _i)
-      endif()
-    endif()
-
-    if(_i LESS 0)
-      set(_curl_current_config "") # let CMake pick config at random
-    else()
-      set(_curl_current_config "_${_curl_current_config}")
-    endif()
-
-    get_target_property(CURL_INCLUDE_DIRS CURL::libcurl INTERFACE_INCLUDE_DIRECTORIES)
-    get_target_property(CURL_LIBRARIES CURL::libcurl "LOCATION${_curl_current_config}")
-    set(_curl_current_config)
-    set(_curl_configurations)
-    set(_i)
-  endif()
+if(@USE_ZLIB@)
+  find_dependency(ZLIB @ZLIB_VERSION_MAJOR@)
 endif()
 endif()
 
 
-unset(_curl_missing_components)
+include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
+check_required_components("@PROJECT_NAME@")

+ 54 - 46
CMakeLists.txt

@@ -38,7 +38,7 @@
 # To check:
 # To check:
 # (From Daniel Stenberg) The cmake build selected to run gcc with -fPIC on my box while the plain configure script did not.
 # (From Daniel Stenberg) The cmake build selected to run gcc with -fPIC on my box while the plain configure script did not.
 # (From Daniel Stenberg) The gcc command line use neither -g nor any -O options. As a developer, I also treasure our configure scripts's --enable-debug option that sets a long range of "picky" compiler options.
 # (From Daniel Stenberg) The gcc command line use neither -g nor any -O options. As a developer, I also treasure our configure scripts's --enable-debug option that sets a long range of "picky" compiler options.
-cmake_minimum_required(VERSION 3.4 FATAL_ERROR)
+cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
 set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}")
 set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake;${CMAKE_MODULE_PATH}")
 include(Utilities)
 include(Utilities)
 include(Macros)
 include(Macros)
@@ -92,7 +92,7 @@ option(ENABLE_CURLDEBUG "Set to ON to build with TrackMemory feature enabled" OF
 
 
 if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG)
 if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG)
   if(PICKY_COMPILER)
   if(PICKY_COMPILER)
-    foreach(_CCOPT -pedantic -Wall -W -Wpointer-arith -Wwrite-strings -Wunused -Wshadow -Winline -Wnested-externs -Wmissing-declarations -Wmissing-prototypes -Wno-long-long -Wfloat-equal -Wno-multichar -Wsign-compare -Wundef -Wno-format-nonliteral -Wendif-labels -Wstrict-prototypes -Wdeclaration-after-statement -Wstrict-aliasing=3 -Wcast-align -Wtype-limits -Wold-style-declaration -Wmissing-parameter-type -Wempty-body -Wclobbered -Wignored-qualifiers -Wconversion -Wno-sign-conversion -Wvla -Wdouble-promotion -Wno-system-headers)
+    foreach(_CCOPT -pedantic -Wall -W -Wpointer-arith -Wwrite-strings -Wunused -Wshadow -Winline -Wnested-externs -Wmissing-declarations -Wmissing-prototypes -Wno-long-long -Wfloat-equal -Wno-multichar -Wsign-compare -Wundef -Wno-format-nonliteral -Wendif-labels -Wstrict-prototypes -Wdeclaration-after-statement -Wstrict-aliasing=3 -Wcast-align -Wtype-limits -Wold-style-declaration -Wmissing-parameter-type -Wempty-body -Wclobbered -Wignored-qualifiers -Wconversion -Wno-sign-conversion -Wvla -Wdouble-promotion -Wno-system-headers -Wno-pedantic-ms-format)
       # surprisingly, CHECK_C_COMPILER_FLAG needs a new variable to store each new
       # surprisingly, CHECK_C_COMPILER_FLAG needs a new variable to store each new
       # test result in.
       # test result in.
       check_c_compiler_flag(${_CCOPT} OPT${_CCOPT})
       check_c_compiler_flag(${_CCOPT} OPT${_CCOPT})
@@ -352,7 +352,16 @@ if(CMAKE_USE_OPENSSL)
   set(USE_OPENSSL ON)
   set(USE_OPENSSL ON)
   set(HAVE_LIBCRYPTO ON)
   set(HAVE_LIBCRYPTO ON)
   set(HAVE_LIBSSL ON)
   set(HAVE_LIBSSL ON)
-  list(APPEND CURL_LIBS OpenSSL::SSL OpenSSL::Crypto)
+
+  # Depend on OpenSSL via imported targets if supported by the running
+  # version of CMake.  This allows our dependents to get our dependencies
+  # transitively.
+  if(NOT CMAKE_VERSION VERSION_LESS 3.4)
+    list(APPEND CURL_LIBS OpenSSL::SSL OpenSSL::Crypto)
+  else()
+    list(APPEND CURL_LIBS ${OPENSSL_LIBRARIES})
+    include_directories(${OPENSSL_INCLUDE_DIR})
+  endif()
 
 
   set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
   set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
   check_include_file("openssl/crypto.h" HAVE_OPENSSL_CRYPTO_H)
   check_include_file("openssl/crypto.h" HAVE_OPENSSL_CRYPTO_H)
@@ -499,15 +508,23 @@ check_library_exists("${CURL_LIBS}" dlopen "" HAVE_DLOPEN)
 option(CURL_ZLIB "Set to ON to enable building curl with zlib support." ON)
 option(CURL_ZLIB "Set to ON to enable building curl with zlib support." ON)
 set(HAVE_LIBZ OFF)
 set(HAVE_LIBZ OFF)
 set(HAVE_ZLIB_H OFF)
 set(HAVE_ZLIB_H OFF)
-set(HAVE_ZLIB OFF)
+set(USE_ZLIB OFF)
 if(CURL_ZLIB)
 if(CURL_ZLIB)
   find_package(ZLIB QUIET)
   find_package(ZLIB QUIET)
   if(ZLIB_FOUND)
   if(ZLIB_FOUND)
     set(HAVE_ZLIB_H ON)
     set(HAVE_ZLIB_H ON)
-    set(HAVE_ZLIB ON)
     set(HAVE_LIBZ ON)
     set(HAVE_LIBZ ON)
-    list(APPEND CURL_LIBS ${ZLIB_LIBRARIES})
-    include_directories(${ZLIB_INCLUDE_DIRS})
+    set(USE_ZLIB ON)
+
+    # Depend on ZLIB via imported targets if supported by the running
+    # version of CMake.  This allows our dependents to get our dependencies
+    # transitively.
+    if(NOT CMAKE_VERSION VERSION_LESS 3.4)
+      list(APPEND CURL_LIBS ZLIB::ZLIB)
+    else()
+      list(APPEND CURL_LIBS ${ZLIB_LIBRARIES})
+      include_directories(${ZLIB_INCLUDE_DIRS})
+    endif()
     list(APPEND CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIRS})
     list(APPEND CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIRS})
   endif()
   endif()
 endif()
 endif()
@@ -947,7 +964,6 @@ foreach(CURL_TEST
     HAVE_GETHOSTBYNAME_R_3_REENTRANT
     HAVE_GETHOSTBYNAME_R_3_REENTRANT
     HAVE_GETHOSTBYNAME_R_5_REENTRANT
     HAVE_GETHOSTBYNAME_R_5_REENTRANT
     HAVE_GETHOSTBYNAME_R_6_REENTRANT
     HAVE_GETHOSTBYNAME_R_6_REENTRANT
-    HAVE_SOCKLEN_T
     HAVE_IN_ADDR_T
     HAVE_IN_ADDR_T
     HAVE_BOOL_T
     HAVE_BOOL_T
     STDC_HEADERS
     STDC_HEADERS
@@ -1017,6 +1033,12 @@ if(HAVE_INET_NTOA_R_DECL_REENTRANT)
   set(NEED_REENTRANT 1)
   set(NEED_REENTRANT 1)
 endif()
 endif()
 
 
+# Check clock_gettime(CLOCK_MONOTONIC, x) support
+curl_internal_test(HAVE_CLOCK_GETTIME_MONOTONIC)
+
+# Check compiler support of __builtin_available()
+curl_internal_test(HAVE_BUILTIN_AVAILABLE)
+
 # Some other minor tests
 # Some other minor tests
 
 
 if(NOT HAVE_IN_ADDR_T)
 if(NOT HAVE_IN_ADDR_T)
@@ -1066,24 +1088,6 @@ if(CMAKE_COMPILER_IS_GNUCC AND APPLE)
   endif()
   endif()
 endif()
 endif()
 
 
-if(HAVE_SOCKLEN_T)
-  set(CURL_TYPEOF_CURL_SOCKLEN_T "socklen_t")
-  if(WIN32)
-    set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h;ws2tcpip.h")
-  elseif(HAVE_SYS_SOCKET_H)
-    set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h")
-  endif()
-  check_type_size("socklen_t" CURL_SIZEOF_CURL_SOCKLEN_T)
-  set(CMAKE_EXTRA_INCLUDE_FILES)
-  if(NOT HAVE_CURL_SIZEOF_CURL_SOCKLEN_T)
-    message(FATAL_ERROR
-     "Check for sizeof socklen_t failed, see CMakeFiles/CMakerror.log")
-  endif()
-else()
-  set(CURL_TYPEOF_CURL_SOCKLEN_T int)
-  set(CURL_SIZEOF_CURL_SOCKLEN_T ${SIZEOF_INT})
-endif()
-
 # TODO test which of these headers are required
 # TODO test which of these headers are required
 if(WIN32)
 if(WIN32)
   set(CURL_PULL_WS2TCPIP_H ${HAVE_WS2TCPIP_H})
   set(CURL_PULL_WS2TCPIP_H ${HAVE_WS2TCPIP_H})
@@ -1144,11 +1148,13 @@ function(TRANSFORM_MAKEFILE_INC INPUT_FILE OUTPUT_FILE)
 
 
 endfunction()
 endfunction()
 
 
-if(WIN32 AND NOT CYGWIN)
-  set(CURL_INSTALL_CMAKE_DIR CMake)
-else()
-  set(CURL_INSTALL_CMAKE_DIR lib/cmake/curl)
-endif()
+include(GNUInstallDirs)
+
+set(CURL_INSTALL_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
+set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets")
+set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
+set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
+set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
 
 
 if(USE_MANUAL)
 if(USE_MANUAL)
   add_subdirectory(docs)
   add_subdirectory(docs)
@@ -1282,7 +1288,7 @@ set(VERSIONNUM              "${CURL_VERSION_NUM}")
 configure_file("${CURL_SOURCE_DIR}/curl-config.in"
 configure_file("${CURL_SOURCE_DIR}/curl-config.in"
                "${CURL_BINARY_DIR}/curl-config" @ONLY)
                "${CURL_BINARY_DIR}/curl-config" @ONLY)
 install(FILES "${CURL_BINARY_DIR}/curl-config"
 install(FILES "${CURL_BINARY_DIR}/curl-config"
-        DESTINATION bin
+        DESTINATION ${CMAKE_INSTALL_BINDIR}
         PERMISSIONS
         PERMISSIONS
           OWNER_READ OWNER_WRITE OWNER_EXECUTE
           OWNER_READ OWNER_WRITE OWNER_EXECUTE
           GROUP_READ GROUP_EXECUTE
           GROUP_READ GROUP_EXECUTE
@@ -1292,34 +1298,36 @@ install(FILES "${CURL_BINARY_DIR}/curl-config"
 configure_file("${CURL_SOURCE_DIR}/libcurl.pc.in"
 configure_file("${CURL_SOURCE_DIR}/libcurl.pc.in"
                "${CURL_BINARY_DIR}/libcurl.pc" @ONLY)
                "${CURL_BINARY_DIR}/libcurl.pc" @ONLY)
 install(FILES "${CURL_BINARY_DIR}/libcurl.pc"
 install(FILES "${CURL_BINARY_DIR}/libcurl.pc"
-        DESTINATION lib/pkgconfig)
-
-# This needs to be run very last so other parts of the scripts can take advantage of this.
-if(NOT CURL_CONFIG_HAS_BEEN_RUN_BEFORE)
-  set(CURL_CONFIG_HAS_BEEN_RUN_BEFORE 1 CACHE INTERNAL "Flag to track whether this is the first time running CMake or if CMake has been configured before")
-endif()
+        DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
 
 
 # install headers
 # install headers
 install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/curl"
 install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/curl"
-    DESTINATION include
+    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
     FILES_MATCHING PATTERN "*.h")
     FILES_MATCHING PATTERN "*.h")
 
 
-
 include(CMakePackageConfigHelpers)
 include(CMakePackageConfigHelpers)
 write_basic_package_version_file(
 write_basic_package_version_file(
-    "${PROJECT_BINARY_DIR}/curl-config-version.cmake"
+    "${version_config}"
     VERSION ${CURL_VERSION}
     VERSION ${CURL_VERSION}
     COMPATIBILITY SameMajorVersion
     COMPATIBILITY SameMajorVersion
 )
 )
 
 
-configure_file(CMake/curl-config.cmake.in
-        "${PROJECT_BINARY_DIR}/curl-config.cmake"
-        @ONLY
+# Use:
+# * TARGETS_EXPORT_NAME
+# * PROJECT_NAME
+configure_package_config_file(CMake/curl-config.cmake.in
+        "${project_config}"
+        INSTALL_DESTINATION ${CURL_INSTALL_CMAKE_DIR}
+)
+
+install(
+        EXPORT "${TARGETS_EXPORT_NAME}"
+        NAMESPACE "${PROJECT_NAME}::"
+        DESTINATION ${CURL_INSTALL_CMAKE_DIR}
 )
 )
 
 
 install(
 install(
-        FILES ${PROJECT_BINARY_DIR}/curl-config.cmake
-              ${PROJECT_BINARY_DIR}/curl-config-version.cmake
+        FILES ${version_config} ${project_config}
         DESTINATION ${CURL_INSTALL_CMAKE_DIR}
         DESTINATION ${CURL_INSTALL_CMAKE_DIR}
 )
 )
 
 

+ 21 - 4
include/curl/curl.h

@@ -146,7 +146,8 @@ typedef enum {
   CURLSSLBACKEND_SCHANNEL = 8,
   CURLSSLBACKEND_SCHANNEL = 8,
   CURLSSLBACKEND_DARWINSSL = 9,
   CURLSSLBACKEND_DARWINSSL = 9,
   CURLSSLBACKEND_AXTLS = 10,
   CURLSSLBACKEND_AXTLS = 10,
-  CURLSSLBACKEND_MBEDTLS = 11
+  CURLSSLBACKEND_MBEDTLS = 11,
+  CURLSSLBACKEND_MESALINK = 12
 } curl_sslbackend;
 } curl_sslbackend;
 
 
 /* aliases for library clones and renames */
 /* aliases for library clones and renames */
@@ -517,8 +518,7 @@ typedef enum {
   CURLE_UNKNOWN_OPTION,          /* 48 - User specified an unknown option */
   CURLE_UNKNOWN_OPTION,          /* 48 - User specified an unknown option */
   CURLE_TELNET_OPTION_SYNTAX,    /* 49 - Malformed telnet option */
   CURLE_TELNET_OPTION_SYNTAX,    /* 49 - Malformed telnet option */
   CURLE_OBSOLETE50,              /* 50 - NOT USED */
   CURLE_OBSOLETE50,              /* 50 - NOT USED */
-  CURLE_PEER_FAILED_VERIFICATION, /* 51 - peer's certificate or fingerprint
-                                     wasn't verified fine */
+  CURLE_OBSOLETE51,              /* 51 - NOT USED */
   CURLE_GOT_NOTHING,             /* 52 - when this is a specific error */
   CURLE_GOT_NOTHING,             /* 52 - when this is a specific error */
   CURLE_SSL_ENGINE_NOTFOUND,     /* 53 - SSL crypto engine not found */
   CURLE_SSL_ENGINE_NOTFOUND,     /* 53 - SSL crypto engine not found */
   CURLE_SSL_ENGINE_SETFAILED,    /* 54 - can not set SSL crypto engine as
   CURLE_SSL_ENGINE_SETFAILED,    /* 54 - can not set SSL crypto engine as
@@ -528,7 +528,8 @@ typedef enum {
   CURLE_OBSOLETE57,              /* 57 - NOT IN USE */
   CURLE_OBSOLETE57,              /* 57 - NOT IN USE */
   CURLE_SSL_CERTPROBLEM,         /* 58 - problem with the local certificate */
   CURLE_SSL_CERTPROBLEM,         /* 58 - problem with the local certificate */
   CURLE_SSL_CIPHER,              /* 59 - couldn't use specified cipher */
   CURLE_SSL_CIPHER,              /* 59 - couldn't use specified cipher */
-  CURLE_SSL_CACERT,              /* 60 - problem with the CA cert (path?) */
+  CURLE_PEER_FAILED_VERIFICATION, /* 60 - peer's certificate or fingerprint
+                                     wasn't verified fine */
   CURLE_BAD_CONTENT_ENCODING,    /* 61 - Unrecognized/bad encoding */
   CURLE_BAD_CONTENT_ENCODING,    /* 61 - Unrecognized/bad encoding */
   CURLE_LDAP_INVALID_URL,        /* 62 - Invalid LDAP URL */
   CURLE_LDAP_INVALID_URL,        /* 62 - Invalid LDAP URL */
   CURLE_FILESIZE_EXCEEDED,       /* 63 - Maximum file size exceeded */
   CURLE_FILESIZE_EXCEEDED,       /* 63 - Maximum file size exceeded */
@@ -584,6 +585,9 @@ typedef enum {
   CURL_LAST /* never use! */
   CURL_LAST /* never use! */
 } CURLcode;
 } CURLcode;
 
 
+/* added in 7.62.0 */
+#define CURLE_SSL_CACERT CURLE_PEER_FAILED_VERIFICATION
+
 #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
 #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
                           the obsolete stuff removed! */
                           the obsolete stuff removed! */
 
 
@@ -800,6 +804,9 @@ typedef enum {
    this value, keep them in sync. */
    this value, keep them in sync. */
 #define CURL_HET_DEFAULT 200L
 #define CURL_HET_DEFAULT 200L
 
 
+/* The default connection upkeep interval in milliseconds. */
+#define CURL_UPKEEP_INTERVAL_DEFAULT 60000L
+
 #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
 #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
                           the obsolete stuff removed! */
                           the obsolete stuff removed! */
 
 
@@ -1856,6 +1863,15 @@ typedef enum {
   /* Disallow specifying username/login in URL. */
   /* Disallow specifying username/login in URL. */
   CINIT(DISALLOW_USERNAME_IN_URL, LONG, 278),
   CINIT(DISALLOW_USERNAME_IN_URL, LONG, 278),
 
 
+  /* DNS-over-HTTPS URL */
+  CINIT(DOH_URL, STRINGPOINT, 279),
+
+  /* Preferred buffer size to use for uploads */
+  CINIT(UPLOAD_BUFFERSIZE, LONG, 280),
+
+  /* Time in ms between connection upkeep calls for long-lived connections. */
+  CINIT(UPKEEP_INTERVAL_MS, LONG, 281),
+
   CURLOPT_LASTENTRY /* the last unused */
   CURLOPT_LASTENTRY /* the last unused */
 } CURLoption;
 } CURLoption;
 
 
@@ -2779,6 +2795,7 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask);
   stuff before they can be included! */
   stuff before they can be included! */
 #include "easy.h" /* nothing in curl is fun without the easy stuff */
 #include "easy.h" /* nothing in curl is fun without the easy stuff */
 #include "multi.h"
 #include "multi.h"
+#include "urlapi.h"
 
 
 /* the typechecker doesn't work in C++ (yet) */
 /* the typechecker doesn't work in C++ (yet) */
 #if defined(__GNUC__) && defined(__GNUC_MINOR__) && \
 #if defined(__GNUC__) && defined(__GNUC_MINOR__) && \

+ 4 - 4
include/curl/curlver.h

@@ -30,13 +30,13 @@
 
 
 /* This is the version number of the libcurl package from which this header
 /* This is the version number of the libcurl package from which this header
    file origins: */
    file origins: */
-#define LIBCURL_VERSION "7.61.1-DEV"
+#define LIBCURL_VERSION "7.62.0-DEV"
 
 
 /* The numeric version number is also available "in parts" by using these
 /* The numeric version number is also available "in parts" by using these
    defines: */
    defines: */
 #define LIBCURL_VERSION_MAJOR 7
 #define LIBCURL_VERSION_MAJOR 7
-#define LIBCURL_VERSION_MINOR 61
-#define LIBCURL_VERSION_PATCH 1
+#define LIBCURL_VERSION_MINOR 62
+#define LIBCURL_VERSION_PATCH 0
 
 
 /* This is the numeric version of the libcurl version number, meant for easier
 /* This is the numeric version of the libcurl version number, meant for easier
    parsing and comparions by programs. The LIBCURL_VERSION_NUM define will
    parsing and comparions by programs. The LIBCURL_VERSION_NUM define will
@@ -57,7 +57,7 @@
    CURL_VERSION_BITS() macro since curl's own configure script greps for it
    CURL_VERSION_BITS() macro since curl's own configure script greps for it
    and needs it to contain the full number.
    and needs it to contain the full number.
 */
 */
-#define LIBCURL_VERSION_NUM 0x073D01
+#define LIBCURL_VERSION_NUM 0x073E00
 
 
 /*
 /*
  * This is the date and time when the full source package was created. The
  * This is the date and time when the full source package was created. The

+ 10 - 0
include/curl/easy.h

@@ -95,6 +95,16 @@ CURL_EXTERN CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen,
 CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer,
 CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer,
                                     size_t buflen, size_t *n);
                                     size_t buflen, size_t *n);
 
 
+
+/*
+ * NAME curl_easy_upkeep()
+ *
+ * DESCRIPTION
+ *
+ * Performs connection upkeep for the given session handle.
+ */
+CURL_EXTERN CURLcode curl_easy_upkeep(CURL *curl);
+
 #ifdef  __cplusplus
 #ifdef  __cplusplus
 }
 }
 #endif
 #endif

+ 1 - 1
include/curl/system.h

@@ -298,7 +298,7 @@
 #  define CURL_PULL_SYS_TYPES_H      1
 #  define CURL_PULL_SYS_TYPES_H      1
 #  define CURL_PULL_SYS_SOCKET_H     1
 #  define CURL_PULL_SYS_SOCKET_H     1
 
 
-#elif defined(__SUNPRO_C) /* Oracle Solaris Studio */
+#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) /* Oracle Solaris Studio */
 #  if !defined(__LP64) && (defined(__ILP32) ||                          \
 #  if !defined(__LP64) && (defined(__ILP32) ||                          \
                            defined(__i386) ||                           \
                            defined(__i386) ||                           \
                            defined(__sparcv8) ||                        \
                            defined(__sparcv8) ||                        \

+ 4 - 2
include/curl/typecheck-gcc.h

@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *                             \___|\___/|_| \_\_____|
  *
  *
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <[email protected]>, et al.
  *
  *
  * This software is licensed as described in the file COPYING, which
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * you should have received as part of this distribution. The terms
@@ -269,6 +269,7 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
    (option) == CURLOPT_DNS_LOCAL_IP4 ||                                       \
    (option) == CURLOPT_DNS_LOCAL_IP4 ||                                       \
    (option) == CURLOPT_DNS_LOCAL_IP6 ||                                       \
    (option) == CURLOPT_DNS_LOCAL_IP6 ||                                       \
    (option) == CURLOPT_DNS_SERVERS ||                                         \
    (option) == CURLOPT_DNS_SERVERS ||                                         \
+   (option) == CURLOPT_DOH_URL ||                                             \
    (option) == CURLOPT_EGDSOCKET ||                                           \
    (option) == CURLOPT_EGDSOCKET ||                                           \
    (option) == CURLOPT_FTPPORT ||                                             \
    (option) == CURLOPT_FTPPORT ||                                             \
    (option) == CURLOPT_FTP_ACCOUNT ||                                         \
    (option) == CURLOPT_FTP_ACCOUNT ||                                         \
@@ -500,7 +501,8 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
 /* evaluates to true if expr can be passed as POST data (void* or char*) */
 /* evaluates to true if expr can be passed as POST data (void* or char*) */
 #define _curl_is_postfields(expr)                                             \
 #define _curl_is_postfields(expr)                                             \
   (_curl_is_ptr((expr), void) ||                                              \
   (_curl_is_ptr((expr), void) ||                                              \
-   _curl_is_arr((expr), char))
+   _curl_is_arr((expr), char) ||                                              \
+   _curl_is_arr((expr), unsigned char))
 
 
 /* FIXME: the whole callback checking is messy...
 /* FIXME: the whole callback checking is messy...
  * The idea is to tolerate char vs. void and const vs. not const
  * The idea is to tolerate char vs. void and const vs. not const

+ 120 - 0
include/curl/urlapi.h

@@ -0,0 +1,120 @@
+#ifndef __CURL_URLAPI_H
+#define __CURL_URLAPI_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2018, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/* the error codes for the URL API */
+typedef enum {
+  CURLUE_OK,
+  CURLUE_BAD_HANDLE,          /* 1 */
+  CURLUE_BAD_PARTPOINTER,     /* 2 */
+  CURLUE_MALFORMED_INPUT,     /* 3 */
+  CURLUE_BAD_PORT_NUMBER,     /* 4 */
+  CURLUE_UNSUPPORTED_SCHEME,  /* 5 */
+  CURLUE_URLDECODE,           /* 6 */
+  CURLUE_OUT_OF_MEMORY,       /* 7 */
+  CURLUE_USER_NOT_ALLOWED,    /* 8 */
+  CURLUE_UNKNOWN_PART,        /* 9 */
+  CURLUE_NO_SCHEME,           /* 10 */
+  CURLUE_NO_USER,             /* 11 */
+  CURLUE_NO_PASSWORD,         /* 12 */
+  CURLUE_NO_OPTIONS,          /* 13 */
+  CURLUE_NO_HOST,             /* 14 */
+  CURLUE_NO_PORT,             /* 15 */
+  CURLUE_NO_QUERY,            /* 16 */
+  CURLUE_NO_FRAGMENT          /* 17 */
+} CURLUcode;
+
+typedef enum {
+  CURLUPART_URL,
+  CURLUPART_SCHEME,
+  CURLUPART_USER,
+  CURLUPART_PASSWORD,
+  CURLUPART_OPTIONS,
+  CURLUPART_HOST,
+  CURLUPART_PORT,
+  CURLUPART_PATH,
+  CURLUPART_QUERY,
+  CURLUPART_FRAGMENT
+} CURLUPart;
+
+#define CURLU_DEFAULT_PORT (1<<0)       /* return default port number */
+#define CURLU_NO_DEFAULT_PORT (1<<1)    /* act as if no port number was set,
+                                           if the port number matches the
+                                           default for the scheme */
+#define CURLU_DEFAULT_SCHEME (1<<2)     /* return default scheme if
+                                           missing */
+#define CURLU_NON_SUPPORT_SCHEME (1<<3) /* allow non-supported scheme */
+#define CURLU_PATH_AS_IS (1<<4)         /* leave dot sequences */
+#define CURLU_DISALLOW_USER (1<<5)      /* no user+password allowed */
+#define CURLU_URLDECODE (1<<6)          /* URL decode on get */
+#define CURLU_URLENCODE (1<<7)          /* URL encode on set */
+#define CURLU_APPENDQUERY (1<<8)        /* append a form style part */
+#define CURLU_GUESS_SCHEME (1<<9)       /* legacy curl-style guessing */
+
+typedef struct Curl_URL CURLU;
+
+/*
+ * curl_url() creates a new CURLU handle and returns a pointer to it.
+ * Must be freed with curl_url_cleanup().
+ */
+CURL_EXTERN CURLU *curl_url(void);
+
+/*
+ * curl_url_cleanup() frees the CURLU handle and related resources used for
+ * the URL parsing. It will not free strings previously returned with the URL
+ * API.
+ */
+CURL_EXTERN void curl_url_cleanup(CURLU *handle);
+
+/*
+ * curl_url_dup() duplicates a CURLU handle and returns a new copy. The new
+ * handle must also be freed with curl_url_cleanup().
+ */
+CURL_EXTERN CURLU *curl_url_dup(CURLU *in);
+
+/*
+ * curl_url_get() extracts a specific part of the URL from a CURLU
+ * handle. Returns error code. The returned pointer MUST be freed with
+ * curl_free() afterwards.
+ */
+CURL_EXTERN CURLUcode curl_url_get(CURLU *handle, CURLUPart what,
+                                   char **part, unsigned int flags);
+
+/*
+ * curl_url_set() sets a specific part of the URL in a CURLU handle. Returns
+ * error code. The passed in string will be copied. Passing a NULL instead of
+ * a part string, clears that part.
+ */
+CURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what,
+                                   const char *part, unsigned int flags);
+
+
+#ifdef __cplusplus
+} /* end of extern "C" */
+#endif
+
+#endif

+ 6 - 12
lib/CMakeLists.txt

@@ -105,23 +105,17 @@ if(WIN32)
 endif()
 endif()
 
 
 target_include_directories(${LIB_NAME} INTERFACE
 target_include_directories(${LIB_NAME} INTERFACE
-  $<INSTALL_INTERFACE:include>)
+  $<INSTALL_INTERFACE:include>
+  $<BUILD_INTERFACE:${CURL_SOURCE_DIR}/include>)
 
 
 install(TARGETS ${LIB_NAME}
 install(TARGETS ${LIB_NAME}
-  EXPORT libcurl-target
-  ARCHIVE DESTINATION lib
-  LIBRARY DESTINATION lib
-  RUNTIME DESTINATION bin
+  EXPORT ${TARGETS_EXPORT_NAME}
+  ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
 )
 )
 
 
 export(TARGETS ${LIB_NAME}
 export(TARGETS ${LIB_NAME}
        APPEND FILE ${PROJECT_BINARY_DIR}/libcurl-target.cmake
        APPEND FILE ${PROJECT_BINARY_DIR}/libcurl-target.cmake
        NAMESPACE CURL::
        NAMESPACE CURL::
 )
 )
-
-install(EXPORT libcurl-target
-        FILE libcurl-target.cmake
-        NAMESPACE CURL::
-        DESTINATION ${CURL_INSTALL_CMAKE_DIR}
-)
-

+ 5 - 4
lib/Makefile.inc

@@ -30,12 +30,12 @@ LIB_VAUTH_HFILES = vauth/vauth.h vauth/digest.h vauth/ntlm.h
 LIB_VTLS_CFILES = vtls/openssl.c vtls/gtls.c vtls/vtls.c vtls/nss.c     \
 LIB_VTLS_CFILES = vtls/openssl.c vtls/gtls.c vtls/vtls.c vtls/nss.c     \
   vtls/polarssl.c vtls/polarssl_threadlock.c vtls/axtls.c               \
   vtls/polarssl.c vtls/polarssl_threadlock.c vtls/axtls.c               \
   vtls/cyassl.c vtls/schannel.c vtls/schannel_verify.c                  \
   vtls/cyassl.c vtls/schannel.c vtls/schannel_verify.c                  \
-  vtls/darwinssl.c vtls/gskit.c vtls/mbedtls.c
+  vtls/darwinssl.c vtls/gskit.c vtls/mbedtls.c vtls/mesalink.c
 
 
 LIB_VTLS_HFILES = vtls/openssl.h vtls/vtls.h vtls/gtls.h                \
 LIB_VTLS_HFILES = vtls/openssl.h vtls/vtls.h vtls/gtls.h                \
   vtls/nssg.h vtls/polarssl.h vtls/polarssl_threadlock.h vtls/axtls.h   \
   vtls/nssg.h vtls/polarssl.h vtls/polarssl_threadlock.h vtls/axtls.h   \
   vtls/cyassl.h vtls/schannel.h vtls/darwinssl.h vtls/gskit.h           \
   vtls/cyassl.h vtls/schannel.h vtls/darwinssl.h vtls/gskit.h           \
-  vtls/mbedtls.h
+  vtls/mbedtls.h vtls/mesalink.h
 
 
 LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c   \
 LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c   \
   cookie.c http.c sendf.c ftp.c url.c dict.c if2ip.c speedcheck.c       \
   cookie.c http.c sendf.c ftp.c url.c dict.c if2ip.c speedcheck.c       \
@@ -54,7 +54,8 @@ LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c   \
   http_ntlm.c curl_ntlm_wb.c curl_ntlm_core.c curl_sasl.c rand.c        \
   http_ntlm.c curl_ntlm_wb.c curl_ntlm_core.c curl_sasl.c rand.c        \
   curl_multibyte.c hostcheck.c conncache.c pipeline.c dotdot.c          \
   curl_multibyte.c hostcheck.c conncache.c pipeline.c dotdot.c          \
   x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c      \
   x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c      \
-  mime.c sha256.c setopt.c curl_path.c curl_ctype.c curl_range.c psl.c
+  mime.c sha256.c setopt.c curl_path.c curl_ctype.c curl_range.c psl.c  \
+  doh.c urlapi.c
 
 
 LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
 LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
   formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h         \
   formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h         \
@@ -74,7 +75,7 @@ LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
   curl_setup_once.h multihandle.h setup-vms.h pipeline.h dotdot.h       \
   curl_setup_once.h multihandle.h setup-vms.h pipeline.h dotdot.h       \
   x509asn1.h http2.h sigpipe.h smb.h curl_endian.h curl_des.h           \
   x509asn1.h http2.h sigpipe.h smb.h curl_endian.h curl_des.h           \
   curl_printf.h system_win32.h rand.h mime.h curl_sha256.h setopt.h     \
   curl_printf.h system_win32.h rand.h mime.h curl_sha256.h setopt.h     \
-  curl_path.h curl_ctype.h curl_range.h psl.h
+  curl_path.h curl_ctype.h curl_range.h psl.h doh.h urlapi-int.h
 
 
 LIB_RCFILES = libcurl.rc
 LIB_RCFILES = libcurl.rc
 
 

+ 0 - 1
lib/amigaos.h

@@ -36,4 +36,3 @@ void Curl_amiga_cleanup();
 #endif
 #endif
 
 
 #endif /* HEADER_CURL_AMIGAOS_H */
 #endif /* HEADER_CURL_AMIGAOS_H */
-

+ 5 - 1
lib/arpa_telnet.h

@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *                             \___|\___/|_| \_\_____|
  *
  *
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <[email protected]>, et al.
  *
  *
  * This software is licensed as described in the file COPYING, which
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * you should have received as part of this distribution. The terms
@@ -37,6 +37,7 @@
 #define CURL_NEW_ENV_VAR   0
 #define CURL_NEW_ENV_VAR   0
 #define CURL_NEW_ENV_VALUE 1
 #define CURL_NEW_ENV_VALUE 1
 
 
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
 /*
 /*
  * The telnet options represented as strings
  * The telnet options represented as strings
  */
  */
@@ -53,6 +54,7 @@ static const char * const telnetoptions[]=
   "TERM SPEED",  "LFLOW",          "LINEMODE",      "XDISPLOC",
   "TERM SPEED",  "LFLOW",          "LINEMODE",      "XDISPLOC",
   "OLD-ENVIRON", "AUTHENTICATION", "ENCRYPT",       "NEW-ENVIRON"
   "OLD-ENVIRON", "AUTHENTICATION", "ENCRYPT",       "NEW-ENVIRON"
 };
 };
+#endif
 
 
 #define CURL_TELOPT_MAXIMUM CURL_TELOPT_NEW_ENVIRON
 #define CURL_TELOPT_MAXIMUM CURL_TELOPT_NEW_ENVIRON
 
 
@@ -76,6 +78,7 @@ static const char * const telnetoptions[]=
 #define CURL_DONT 254 /* DON'T use this option! */
 #define CURL_DONT 254 /* DON'T use this option! */
 #define CURL_IAC  255 /* Interpret As Command */
 #define CURL_IAC  255 /* Interpret As Command */
 
 
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
 /*
 /*
  * Then those numbers represented as strings:
  * Then those numbers represented as strings:
  */
  */
@@ -86,6 +89,7 @@ static const char * const telnetcmds[]=
   "AYT",  "EC",    "EL",    "GA",   "SB",
   "AYT",  "EC",    "EL",    "GA",   "SB",
   "WILL", "WONT",  "DO",    "DONT", "IAC"
   "WILL", "WONT",  "DO",    "DONT", "IAC"
 };
 };
+#endif
 
 
 #define CURL_TELCMD_MINIMUM CURL_xEOF /* the first one */
 #define CURL_TELCMD_MINIMUM CURL_xEOF /* the first one */
 #define CURL_TELCMD_MAXIMUM CURL_IAC  /* surprise, 255 is the last one! ;-) */
 #define CURL_TELCMD_MAXIMUM CURL_IAC  /* surprise, 255 is the last one! ;-) */

+ 11 - 15
lib/cookie.c

@@ -1218,7 +1218,6 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
 {
 {
   struct Cookie *newco;
   struct Cookie *newco;
   struct Cookie *co;
   struct Cookie *co;
-  time_t now = time(NULL);
   struct Cookie *mainco = NULL;
   struct Cookie *mainco = NULL;
   size_t matches = 0;
   size_t matches = 0;
   bool is_ip;
   bool is_ip;
@@ -1236,11 +1235,8 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
   co = c->cookies[myhash];
   co = c->cookies[myhash];
 
 
   while(co) {
   while(co) {
-    /* only process this cookie if it is not expired or had no expire
-       date AND that if the cookie requires we're secure we must only
-       continue if we are! */
-    if((!co->expires || (co->expires > now)) &&
-       (co->secure?secure:TRUE)) {
+    /* if the cookie requires we're secure we must only continue if we are! */
+    if(co->secure?secure:TRUE) {
 
 
       /* now check if the domain is correct */
       /* now check if the domain is correct */
       if(!co->domain ||
       if(!co->domain ||
@@ -1266,12 +1262,8 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
 
 
             matches++;
             matches++;
           }
           }
-          else {
-            fail:
-            /* failure, clear up the allocated chain and return NULL */
-            Curl_cookie_freelist(mainco);
-            return NULL;
-          }
+          else
+            goto fail;
         }
         }
       }
       }
     }
     }
@@ -1309,6 +1301,11 @@ struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
   }
   }
 
 
   return mainco; /* return the new list */
   return mainco; /* return the new list */
+
+fail:
+  /* failure, clear up the allocated chain and return NULL */
+  Curl_cookie_freelist(mainco);
+  return NULL;
 }
 }
 
 
 /*****************************************************************************
 /*****************************************************************************
@@ -1508,10 +1505,9 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere)
     format_ptr = get_netscape_format(array[i]);
     format_ptr = get_netscape_format(array[i]);
     if(format_ptr == NULL) {
     if(format_ptr == NULL) {
       fprintf(out, "#\n# Fatal libcurl error\n");
       fprintf(out, "#\n# Fatal libcurl error\n");
-      if(!use_stdout) {
-        free(array);
+      free(array);
+      if(!use_stdout)
         fclose(out);
         fclose(out);
-      }
       return 1;
       return 1;
     }
     }
     fprintf(out, "%s\n", format_ptr);
     fprintf(out, "%s\n", format_ptr);

+ 3 - 3
lib/curl_config.h.cmake

@@ -127,6 +127,9 @@
 /* Define to 1 if bool is an available type. */
 /* Define to 1 if bool is an available type. */
 #cmakedefine HAVE_BOOL_T 1
 #cmakedefine HAVE_BOOL_T 1
 
 
+/* Define to 1 if you have the __builtin_available function. */
+#cmakedefine HAVE_BUILTIN_AVAILABLE 1
+
 /* Define to 1 if you have the clock_gettime function and monotonic timer. */
 /* Define to 1 if you have the clock_gettime function and monotonic timer. */
 #cmakedefine HAVE_CLOCK_GETTIME_MONOTONIC 1
 #cmakedefine HAVE_CLOCK_GETTIME_MONOTONIC 1
 
 
@@ -900,9 +903,6 @@
 /* The size of `time_t', as computed by sizeof. */
 /* The size of `time_t', as computed by sizeof. */
 #cmakedefine SIZEOF_TIME_T ${SIZEOF_TIME_T}
 #cmakedefine SIZEOF_TIME_T ${SIZEOF_TIME_T}
 
 
-/* The size of `void*', as computed by sizeof. */
-#cmakedefine SIZEOF_VOIDP ${SIZEOF_VOIDP}
-
 /* Define to 1 if you have the ANSI C header files. */
 /* Define to 1 if you have the ANSI C header files. */
 #cmakedefine STDC_HEADERS 1
 #cmakedefine STDC_HEADERS 1
 
 

+ 0 - 1
lib/curl_ldap.h

@@ -32,4 +32,3 @@ extern const struct Curl_handler Curl_handler_ldaps;
 
 
 #endif
 #endif
 #endif /* HEADER_CURL_LDAP_H */
 #endif /* HEADER_CURL_LDAP_H */
-

+ 17 - 1
lib/curl_ntlm_wb.c

@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *                             \___|\___/|_| \_\_____|
  *
  *
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <[email protected]>, et al.
  *
  *
  * This software is licensed as described in the file COPYING, which
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * you should have received as part of this distribution. The terms
@@ -249,6 +249,9 @@ done:
   return CURLE_REMOTE_ACCESS_DENIED;
   return CURLE_REMOTE_ACCESS_DENIED;
 }
 }
 
 
+/* if larger than this, something is seriously wrong */
+#define MAX_NTLM_WB_RESPONSE 100000
+
 static CURLcode ntlm_wb_response(struct connectdata *conn,
 static CURLcode ntlm_wb_response(struct connectdata *conn,
                                  const char *input, curlntlm state)
                                  const char *input, curlntlm state)
 {
 {
@@ -289,6 +292,13 @@ static CURLcode ntlm_wb_response(struct connectdata *conn,
       buf[len_out - 1] = '\0';
       buf[len_out - 1] = '\0';
       break;
       break;
     }
     }
+
+    if(len_out > MAX_NTLM_WB_RESPONSE) {
+      failf(conn->data, "too large ntlm_wb response!");
+      free(buf);
+      return CURLE_OUT_OF_MEMORY;
+    }
+
     newbuf = Curl_saferealloc(buf, len_out + NTLM_BUFSIZE);
     newbuf = Curl_saferealloc(buf, len_out + NTLM_BUFSIZE);
     if(!newbuf)
     if(!newbuf)
       return CURLE_OUT_OF_MEMORY;
       return CURLE_OUT_OF_MEMORY;
@@ -314,6 +324,8 @@ static CURLcode ntlm_wb_response(struct connectdata *conn,
 
 
   conn->response_header = aprintf("NTLM %.*s", len_out - 4, buf + 3);
   conn->response_header = aprintf("NTLM %.*s", len_out - 4, buf + 3);
   free(buf);
   free(buf);
+  if(!conn->response_header)
+    return CURLE_OUT_OF_MEMORY;
   return CURLE_OK;
   return CURLE_OK;
 done:
 done:
   free(buf);
   free(buf);
@@ -389,6 +401,8 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn,
                             conn->response_header);
                             conn->response_header);
     DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd));
     DEBUG_OUT(fprintf(stderr, "**** Header %s\n ", *allocuserpwd));
     free(conn->response_header);
     free(conn->response_header);
+    if(!*allocuserpwd)
+      return CURLE_OUT_OF_MEMORY;
     conn->response_header = NULL;
     conn->response_header = NULL;
     break;
     break;
   case NTLMSTATE_TYPE2:
   case NTLMSTATE_TYPE2:
@@ -409,6 +423,8 @@ CURLcode Curl_output_ntlm_wb(struct connectdata *conn,
     ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
     ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
     authp->done = TRUE;
     authp->done = TRUE;
     Curl_ntlm_wb_cleanup(conn);
     Curl_ntlm_wb_cleanup(conn);
+    if(!*allocuserpwd)
+      return CURLE_OUT_OF_MEMORY;
     break;
     break;
   case NTLMSTATE_TYPE3:
   case NTLMSTATE_TYPE3:
     /* connection is already authenticated,
     /* connection is already authenticated,

+ 2 - 2
lib/curl_path.c

@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *                             \___|\___/|_| \_\_____|
  *
  *
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <[email protected]>, et al.
  *
  *
  * This software is licensed as described in the file COPYING, which
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * you should have received as part of this distribution. The terms
@@ -39,7 +39,7 @@ CURLcode Curl_getworkingpath(struct connectdata *conn,
   char *working_path;
   char *working_path;
   size_t working_path_len;
   size_t working_path_len;
   CURLcode result =
   CURLcode result =
-    Curl_urldecode(data, data->state.path, 0, &working_path,
+    Curl_urldecode(data, data->state.up.path, 0, &working_path,
                    &working_path_len, FALSE);
                    &working_path_len, FALSE);
   if(result)
   if(result)
     return result;
     return result;

+ 1 - 1
lib/curl_path.h

@@ -44,4 +44,4 @@ CURLcode Curl_getworkingpath(struct connectdata *conn,
                              char **path);
                              char **path);
 
 
 CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir);
 CURLcode Curl_get_pathname(const char **cpp, char **path, char *homedir);
-#endif
+#endif /* HEADER_CURL_PATH_H */

+ 3 - 1
lib/curl_rtmp.c

@@ -37,9 +37,11 @@
 /* The last #include file should be: */
 /* The last #include file should be: */
 #include "memdebug.h"
 #include "memdebug.h"
 
 
-#ifdef _WIN32
+#if defined(WIN32) && !defined(USE_LWIPSOCK)
 #define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e)
 #define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e)
 #define SET_RCVTIMEO(tv,s)   int tv = s*1000
 #define SET_RCVTIMEO(tv,s)   int tv = s*1000
+#elif defined(LWIP_SO_SNDRCVTIMEO_NONSTANDARD)
+#define SET_RCVTIMEO(tv,s)   int tv = s*1000
 #else
 #else
 #define SET_RCVTIMEO(tv,s)   struct timeval tv = {s,0}
 #define SET_RCVTIMEO(tv,s)   struct timeval tv = {s,0}
 #endif
 #endif

+ 4 - 1
lib/curl_setup.h

@@ -44,6 +44,9 @@
 #  ifndef WIN32_LEAN_AND_MEAN
 #  ifndef WIN32_LEAN_AND_MEAN
 #    define WIN32_LEAN_AND_MEAN
 #    define WIN32_LEAN_AND_MEAN
 #  endif
 #  endif
+#  ifndef NOGDI
+#    define NOGDI
+#  endif
 #endif
 #endif
 
 
 /*
 /*
@@ -645,7 +648,7 @@ int netware_init(void);
 #if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_NSS) || \
 #if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_NSS) || \
     defined(USE_POLARSSL) || defined(USE_AXTLS) || defined(USE_MBEDTLS) || \
     defined(USE_POLARSSL) || defined(USE_AXTLS) || defined(USE_MBEDTLS) || \
     defined(USE_CYASSL) || defined(USE_SCHANNEL) || \
     defined(USE_CYASSL) || defined(USE_SCHANNEL) || \
-    defined(USE_DARWINSSL) || defined(USE_GSKIT)
+    defined(USE_DARWINSSL) || defined(USE_GSKIT) || defined(USE_MESALINK)
 #define USE_SSL    /* SSL support has been enabled */
 #define USE_SSL    /* SSL support has been enabled */
 #endif
 #endif
 
 

+ 0 - 1
lib/curl_setup_once.h

@@ -515,4 +515,3 @@ typedef int sig_atomic_t;
 
 
 
 
 #endif /* HEADER_CURL_SETUP_ONCE_H */
 #endif /* HEADER_CURL_SETUP_ONCE_H */
-

+ 4 - 3
lib/curl_sspi.c

@@ -90,8 +90,9 @@ CURLcode Curl_sspi_global_init(void)
       return CURLE_FAILED_INIT;
       return CURLE_FAILED_INIT;
 
 
     /* Get address of the InitSecurityInterfaceA function from the SSPI dll */
     /* Get address of the InitSecurityInterfaceA function from the SSPI dll */
-    pInitSecurityInterface = (INITSECURITYINTERFACE_FN)
-      GetProcAddress(s_hSecDll, SECURITYENTRYPOINT);
+    pInitSecurityInterface =
+      CURLX_FUNCTION_CAST(INITSECURITYINTERFACE_FN,
+                          (GetProcAddress(s_hSecDll, SECURITYENTRYPOINT)));
     if(!pInitSecurityInterface)
     if(!pInitSecurityInterface)
       return CURLE_FAILED_INIT;
       return CURLE_FAILED_INIT;
 
 
@@ -131,7 +132,7 @@ void Curl_sspi_global_cleanup(void)
  * Parameters:
  * Parameters:
  *
  *
  * userp    [in]     - The user name in the format User or Domain\User.
  * userp    [in]     - The user name in the format User or Domain\User.
- * passdwp  [in]     - The user's password.
+ * passwdp  [in]     - The user's password.
  * identity [in/out] - The identity structure.
  * identity [in/out] - The identity structure.
  *
  *
  * Returns CURLE_OK on success.
  * Returns CURLE_OK on success.

+ 11 - 3
lib/curl_threads.c

@@ -104,13 +104,21 @@ int Curl_thread_join(curl_thread_t *hnd)
 curl_thread_t Curl_thread_create(unsigned int (CURL_STDCALL *func) (void *),
 curl_thread_t Curl_thread_create(unsigned int (CURL_STDCALL *func) (void *),
                                  void *arg)
                                  void *arg)
 {
 {
+#ifdef _WIN32_WCE
+  typedef HANDLE curl_win_thread_handle_t;
+#elif defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR)
+  typedef unsigned long curl_win_thread_handle_t;
+#else
+  typedef uintptr_t curl_win_thread_handle_t;
+#endif
   curl_thread_t t;
   curl_thread_t t;
+  curl_win_thread_handle_t thread_handle;
 #ifdef _WIN32_WCE
 #ifdef _WIN32_WCE
-  t = CreateThread(NULL, 0, func, arg, 0, NULL);
+  thread_handle = CreateThread(NULL, 0, func, arg, 0, NULL);
 #else
 #else
-  uintptr_t thread_handle = _beginthreadex(NULL, 0, func, arg, 0, NULL);
-  t = (curl_thread_t)thread_handle;
+  thread_handle = _beginthreadex(NULL, 0, func, arg, 0, NULL);
 #endif
 #endif
+  t = (curl_thread_t)thread_handle;
   if((t == 0) || (t == LongToHandle(-1L))) {
   if((t == 0) || (t == LongToHandle(-1L))) {
 #ifdef _WIN32_WCE
 #ifdef _WIN32_WCE
     DWORD gle = GetLastError();
     DWORD gle = GetLastError();

+ 2 - 1
lib/curl_threads.h

@@ -38,7 +38,8 @@
 #  define curl_thread_t          HANDLE
 #  define curl_thread_t          HANDLE
 #  define curl_thread_t_null     (HANDLE)0
 #  define curl_thread_t_null     (HANDLE)0
 #  if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \
 #  if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \
-      (_WIN32_WINNT < _WIN32_WINNT_VISTA)
+      (_WIN32_WINNT < _WIN32_WINNT_VISTA) || \
+      (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
 #    define Curl_mutex_init(m)   InitializeCriticalSection(m)
 #    define Curl_mutex_init(m)   InitializeCriticalSection(m)
 #  else
 #  else
 #    define Curl_mutex_init(m)   InitializeCriticalSectionEx(m, 0, 1)
 #    define Curl_mutex_init(m)   InitializeCriticalSectionEx(m, 0, 1)

+ 0 - 1
lib/curlx.h

@@ -102,4 +102,3 @@
 #endif /* ENABLE_CURLX_PRINTF */
 #endif /* ENABLE_CURLX_PRINTF */
 
 
 #endif /* HEADER_CURL_CURLX_H */
 #endif /* HEADER_CURL_CURLX_H */
-

+ 1 - 1
lib/dict.c

@@ -136,7 +136,7 @@ static CURLcode dict_do(struct connectdata *conn, bool *done)
   struct Curl_easy *data = conn->data;
   struct Curl_easy *data = conn->data;
   curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
   curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
 
 
-  char *path = data->state.path;
+  char *path = data->state.up.path;
   curl_off_t *bytecount = &data->req.bytecount;
   curl_off_t *bytecount = &data->req.bytecount;
 
 
   *done = TRUE; /* unconditionally */
   *done = TRUE; /* unconditionally */

+ 920 - 0
lib/doh.c

@@ -0,0 +1,920 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2018, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include "urldata.h"
+#include "curl_addrinfo.h"
+#include "doh.h"
+
+#ifdef USE_NGHTTP2
+#include "sendf.h"
+#include "multiif.h"
+#include "url.h"
+#include "share.h"
+#include "curl_base64.h"
+#include "connect.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define DNS_CLASS_IN 0x01
+#define DOH_MAX_RESPONSE_SIZE 3000 /* bytes */
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static const char * const errors[]={
+  "",
+  "Bad label",
+  "Out of range",
+  "Label loop",
+  "Too small",
+  "Out of memory",
+  "RDATA length",
+  "Malformat",
+  "Bad RCODE",
+  "Unexpected TYPE",
+  "Unexpected CLASS",
+  "No content",
+  "Bad ID"
+};
+
+static const char *doh_strerror(DOHcode code)
+{
+  if((code >= DOH_OK) && (code <= DOH_DNS_BAD_ID))
+    return errors[code];
+  return "bad error code";
+}
+#endif
+
+#ifdef DEBUGBUILD
+#define UNITTEST
+#else
+#define UNITTEST static
+#endif
+
+UNITTEST DOHcode doh_encode(const char *host,
+                            DNStype dnstype,
+                            unsigned char *dnsp, /* buffer */
+                            size_t len,  /* buffer size */
+                            size_t *olen) /* output length */
+{
+  size_t hostlen = strlen(host);
+  unsigned char *orig = dnsp;
+  const char *hostp = host;
+
+  if(len < (12 + hostlen + 4))
+    return DOH_TOO_SMALL_BUFFER;
+
+  *dnsp++ = 0; /* 16 bit id */
+  *dnsp++ = 0;
+  *dnsp++ = 0x01; /* |QR|   Opcode  |AA|TC|RD| Set the RD bit */
+  *dnsp++ = '\0'; /* |RA|   Z    |   RCODE   |                */
+  *dnsp++ = '\0';
+  *dnsp++ = 1;    /* QDCOUNT (number of entries in the question section) */
+  *dnsp++ = '\0';
+  *dnsp++ = '\0'; /* ANCOUNT */
+  *dnsp++ = '\0';
+  *dnsp++ = '\0'; /* NSCOUNT */
+  *dnsp++ = '\0';
+  *dnsp++ = '\0'; /* ARCOUNT */
+
+  /* store a QNAME */
+  do {
+    char *dot = strchr(hostp, '.');
+    size_t labellen;
+    bool found = false;
+    if(dot) {
+      found = true;
+      labellen = dot - hostp;
+    }
+    else
+      labellen = strlen(hostp);
+    if(labellen > 63) {
+      /* too long label, error out */
+      *olen = 0;
+      return DOH_DNS_BAD_LABEL;
+    }
+    *dnsp++ = (unsigned char)labellen;
+    memcpy(dnsp, hostp, labellen);
+    dnsp += labellen;
+    hostp += labellen + 1;
+    if(!found) {
+      *dnsp++ = 0; /* terminating zero */
+      break;
+    }
+  } while(1);
+
+  *dnsp++ = '\0'; /* upper 8 bit TYPE */
+  *dnsp++ = (unsigned char)dnstype;
+  *dnsp++ = '\0'; /* upper 8 bit CLASS */
+  *dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */
+
+  *olen = dnsp - orig;
+  return DOH_OK;
+}
+
+static size_t
+doh_write_cb(void *contents, size_t size, size_t nmemb, void *userp)
+{
+  size_t realsize = size * nmemb;
+  struct dohresponse *mem = (struct dohresponse *)userp;
+
+  if((mem->size + realsize) > DOH_MAX_RESPONSE_SIZE)
+    /* suspiciously much for us */
+    return 0;
+
+  mem->memory = realloc(mem->memory, mem->size + realsize);
+  if(mem->memory == NULL)
+    /* out of memory! */
+    return 0;
+
+  memcpy(&(mem->memory[mem->size]), contents, realsize);
+  mem->size += realsize;
+
+  return realsize;
+}
+
+/* called from multi.c when this DOH transfer is complete */
+static int Curl_doh_done(struct Curl_easy *doh, CURLcode result)
+{
+  struct Curl_easy *data = doh->set.dohfor;
+  /* so one of the DOH request done for the 'data' transfer is now complete! */
+  data->req.doh.pending--;
+  infof(data, "a DOH request is completed, %d to go\n", data->req.doh.pending);
+  if(result)
+    infof(data, "DOH request %s\n", curl_easy_strerror(result));
+
+  if(!data->req.doh.pending) {
+    /* DOH completed */
+    curl_slist_free_all(data->req.doh.headers);
+    data->req.doh.headers = NULL;
+    Curl_expire(data, 0, EXPIRE_RUN_NOW);
+  }
+  return 0;
+}
+
+#define ERROR_CHECK_SETOPT(x,y) result = curl_easy_setopt(doh, x, y);   \
+  if(result) goto error
+
+static CURLcode dohprobe(struct Curl_easy *data,
+                         struct dnsprobe *p, DNStype dnstype,
+                         const char *host,
+                         const char *url, CURLM *multi,
+                         struct curl_slist *headers)
+{
+  struct Curl_easy *doh = NULL;
+  char *nurl = NULL;
+  CURLcode result = CURLE_OK;
+  timediff_t timeout_ms;
+  DOHcode d = doh_encode(host, dnstype, p->dohbuffer, sizeof(p->dohbuffer),
+                         &p->dohlen);
+  if(d) {
+    failf(data, "Failed to encode DOH packet [%d]\n", d);
+    return CURLE_OUT_OF_MEMORY;
+  }
+
+  p->dnstype = dnstype;
+  p->serverdoh.memory = NULL;
+  /* the memory will be grown as needed by realloc in the doh_write_cb
+     function */
+  p->serverdoh.size = 0;
+
+  /* Note: this is code for sending the DoH request with GET but there's still
+     no logic that actually enables this. We should either add that ability or
+     yank out the GET code. Discuss! */
+  if(data->set.doh_get) {
+    char *b64;
+    size_t b64len;
+    result = Curl_base64url_encode(data, (char *)p->dohbuffer, p->dohlen,
+                                   &b64, &b64len);
+    if(result)
+      goto error;
+    nurl = aprintf("%s?dns=%s", url, b64);
+    free(b64);
+    if(!nurl) {
+      result = CURLE_OUT_OF_MEMORY;
+      goto error;
+    }
+    url = nurl;
+  }
+
+  timeout_ms = Curl_timeleft(data, NULL, TRUE);
+
+  /* Curl_open() is the internal version of curl_easy_init() */
+  result = Curl_open(&doh);
+  if(!result) {
+    /* pass in the struct pointer via a local variable to please coverity and
+       the gcc typecheck helpers */
+    struct dohresponse *resp = &p->serverdoh;
+    ERROR_CHECK_SETOPT(CURLOPT_URL, url);
+    ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
+    ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, resp);
+    if(!data->set.doh_get) {
+      ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->dohbuffer);
+      ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->dohlen);
+    }
+    ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
+    ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
+#ifndef CURLDEBUG
+    /* enforce HTTPS if not debug */
+    ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
+#endif
+    ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
+    ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
+    doh->set.fmultidone = Curl_doh_done;
+    doh->set.dohfor = data; /* identify for which transfer this is done */
+    p->easy = doh;
+
+    /* add this transfer to the multi handle */
+    if(curl_multi_add_handle(multi, doh))
+      goto error;
+  }
+  else
+    goto error;
+  free(nurl);
+  return CURLE_OK;
+
+  error:
+  free(nurl);
+  Curl_close(doh);
+  return result;
+}
+
+/*
+ * Curl_doh() resolves a name using DOH. It resolves a name and returns a
+ * 'Curl_addrinfo *' with the address information.
+ */
+
+Curl_addrinfo *Curl_doh(struct connectdata *conn,
+                        const char *hostname,
+                        int port,
+                        int *waitp)
+{
+  struct Curl_easy *data = conn->data;
+  CURLcode result = CURLE_OK;
+  *waitp = TRUE; /* this never returns synchronously */
+  (void)conn;
+  (void)hostname;
+  (void)port;
+
+  /* start clean, consider allocating this struct on demand */
+  memset(&data->req.doh, 0, sizeof(struct dohdata));
+
+  data->req.doh.host = hostname;
+  data->req.doh.port = port;
+  data->req.doh.headers =
+    curl_slist_append(NULL,
+                      "Content-Type: application/dns-message");
+  if(!data->req.doh.headers)
+    goto error;
+
+  if(conn->ip_version != CURL_IPRESOLVE_V6) {
+    /* create IPv4 DOH request */
+    result = dohprobe(data, &data->req.doh.probe[0], DNS_TYPE_A,
+                      hostname, data->set.str[STRING_DOH],
+                      data->multi, data->req.doh.headers);
+    if(result)
+      goto error;
+    data->req.doh.pending++;
+  }
+
+  if(conn->ip_version != CURL_IPRESOLVE_V4) {
+    /* create IPv6 DOH request */
+    result = dohprobe(data, &data->req.doh.probe[1], DNS_TYPE_AAAA,
+                      hostname, data->set.str[STRING_DOH],
+                      data->multi, data->req.doh.headers);
+    if(result)
+      goto error;
+    data->req.doh.pending++;
+  }
+  return NULL;
+
+  error:
+  curl_slist_free_all(data->req.doh.headers);
+  data->req.doh.headers = NULL;
+  curl_easy_cleanup(data->req.doh.probe[0].easy);
+  data->req.doh.probe[0].easy = NULL;
+  curl_easy_cleanup(data->req.doh.probe[1].easy);
+  data->req.doh.probe[1].easy = NULL;
+  return NULL;
+}
+
+static DOHcode skipqname(unsigned char *doh, size_t dohlen,
+                         unsigned int *indexp)
+{
+  unsigned char length;
+  do {
+    if(dohlen < (*indexp + 1))
+      return DOH_DNS_OUT_OF_RANGE;
+    length = doh[*indexp];
+    if((length & 0xc0) == 0xc0) {
+      /* name pointer, advance over it and be done */
+      if(dohlen < (*indexp + 2))
+        return DOH_DNS_OUT_OF_RANGE;
+      *indexp += 2;
+      break;
+    }
+    if(length & 0xc0)
+      return DOH_DNS_BAD_LABEL;
+    if(dohlen < (*indexp + 1 + length))
+      return DOH_DNS_OUT_OF_RANGE;
+    *indexp += 1 + length;
+  } while(length);
+  return DOH_OK;
+}
+
+static unsigned short get16bit(unsigned char *doh, int index)
+{
+  return (unsigned short)((doh[index] << 8) | doh[index + 1]);
+}
+
+static unsigned int get32bit(unsigned char *doh, int index)
+{
+  return (doh[index] << 24) | (doh[index + 1] << 16) |
+    (doh[index + 2] << 8) | doh[index + 3];
+}
+
+static DOHcode store_a(unsigned char *doh, int index, struct dohentry *d)
+{
+  /* silently ignore addresses over the limit */
+  if(d->numaddr < DOH_MAX_ADDR) {
+    struct dohaddr *a = &d->addr[d->numaddr];
+    a->type = DNS_TYPE_A;
+    memcpy(&a->ip.v4, &doh[index], 4);
+    d->numaddr++;
+  }
+  return DOH_OK;
+}
+
+static DOHcode store_aaaa(unsigned char *doh, int index, struct dohentry *d)
+{
+  /* silently ignore addresses over the limit */
+  if(d->numaddr < DOH_MAX_ADDR) {
+    struct dohaddr *a = &d->addr[d->numaddr];
+    a->type = DNS_TYPE_AAAA;
+    memcpy(&a->ip.v6, &doh[index], 16);
+    d->numaddr++;
+  }
+  return DOH_OK;
+}
+
+static DOHcode cnameappend(struct cnamestore *c,
+                           unsigned char *src,
+                           size_t len)
+{
+  if(!c->alloc) {
+    c->allocsize = len + 1;
+    c->alloc = malloc(c->allocsize);
+    if(!c->alloc)
+      return DOH_OUT_OF_MEM;
+  }
+  else if(c->allocsize < (c->allocsize + len + 1)) {
+    char *ptr;
+    c->allocsize += len + 1;
+    ptr = realloc(c->alloc, c->allocsize);
+    if(!ptr) {
+      free(c->alloc);
+      return DOH_OUT_OF_MEM;
+    }
+    c->alloc = ptr;
+  }
+  memcpy(&c->alloc[c->len], src, len);
+  c->len += len;
+  c->alloc[c->len] = 0; /* keep it zero terminated */
+  return DOH_OK;
+}
+
+static DOHcode store_cname(unsigned char *doh,
+                           size_t dohlen,
+                           unsigned int index,
+                           struct dohentry *d)
+{
+  struct cnamestore *c;
+  unsigned int loop = 128; /* a valid DNS name can never loop this much */
+  unsigned char length;
+
+  if(d->numcname == DOH_MAX_CNAME)
+    return DOH_OK; /* skip! */
+
+  c = &d->cname[d->numcname++];
+  do {
+    if(index >= dohlen)
+      return DOH_DNS_OUT_OF_RANGE;
+    length = doh[index];
+    if((length & 0xc0) == 0xc0) {
+      int newpos;
+      /* name pointer, get the new offset (14 bits) */
+      if((index + 1) >= dohlen)
+        return DOH_DNS_OUT_OF_RANGE;
+
+      /* move to the the new index */
+      newpos = (length & 0x3f) << 8 | doh[index + 1];
+      index = newpos;
+      continue;
+    }
+    else if(length & 0xc0)
+      return DOH_DNS_BAD_LABEL; /* bad input */
+    else
+      index++;
+
+    if(length) {
+      DOHcode rc;
+      if(c->len) {
+        rc = cnameappend(c, (unsigned char *)".", 1);
+        if(rc)
+          return rc;
+      }
+      if((index + length) > dohlen)
+        return DOH_DNS_BAD_LABEL;
+
+      rc = cnameappend(c, &doh[index], length);
+      if(rc)
+        return rc;
+      index += length;
+    }
+  } while(length && --loop);
+
+  if(!loop)
+    return DOH_DNS_LABEL_LOOP;
+  return DOH_OK;
+}
+
+static DOHcode rdata(unsigned char *doh,
+                     size_t dohlen,
+                     unsigned short rdlength,
+                     unsigned short type,
+                     int index,
+                     struct dohentry *d)
+{
+  /* RDATA
+     - A (TYPE 1):  4 bytes
+     - AAAA (TYPE 28): 16 bytes
+     - NS (TYPE 2): N bytes */
+  DOHcode rc;
+
+  switch(type) {
+  case DNS_TYPE_A:
+    if(rdlength != 4)
+      return DOH_DNS_RDATA_LEN;
+    rc = store_a(doh, index, d);
+    if(rc)
+      return rc;
+    break;
+  case DNS_TYPE_AAAA:
+    if(rdlength != 16)
+      return DOH_DNS_RDATA_LEN;
+    rc = store_aaaa(doh, index, d);
+    if(rc)
+      return rc;
+    break;
+  case DNS_TYPE_CNAME:
+    rc = store_cname(doh, dohlen, index, d);
+    if(rc)
+      return rc;
+    break;
+  default:
+    /* unsupported type, just skip it */
+    break;
+  }
+  return DOH_OK;
+}
+
+static void init_dohentry(struct dohentry *de)
+{
+  memset(de, 0, sizeof(*de));
+  de->ttl = INT_MAX;
+}
+
+
+UNITTEST DOHcode doh_decode(unsigned char *doh,
+                            size_t dohlen,
+                            DNStype dnstype,
+                            struct dohentry *d)
+{
+  unsigned char rcode;
+  unsigned short qdcount;
+  unsigned short ancount;
+  unsigned short type = 0;
+  unsigned short class;
+  unsigned short rdlength;
+  unsigned short nscount;
+  unsigned short arcount;
+  unsigned int index = 12;
+  DOHcode rc;
+
+  if(dohlen < 12)
+    return DOH_TOO_SMALL_BUFFER; /* too small */
+  if(doh[0] || doh[1])
+    return DOH_DNS_BAD_ID; /* bad ID */
+  rcode = doh[3] & 0x0f;
+  if(rcode)
+    return DOH_DNS_BAD_RCODE; /* bad rcode */
+
+  qdcount = get16bit(doh, 4);
+  while(qdcount) {
+    rc = skipqname(doh, dohlen, &index);
+    if(rc)
+      return rc; /* bad qname */
+    if(dohlen < (index + 4))
+      return DOH_DNS_OUT_OF_RANGE;
+    index += 4; /* skip question's type and class */
+    qdcount--;
+  }
+
+  ancount = get16bit(doh, 6);
+  while(ancount) {
+    unsigned int ttl;
+
+    rc = skipqname(doh, dohlen, &index);
+    if(rc)
+      return rc; /* bad qname */
+
+    if(dohlen < (index + 2))
+      return DOH_DNS_OUT_OF_RANGE;
+
+    type = get16bit(doh, index);
+    if((type != DNS_TYPE_CNAME) && (type != dnstype))
+      /* Not the same type as was asked for nor CNAME */
+      return DOH_DNS_UNEXPECTED_TYPE;
+    index += 2;
+
+    if(dohlen < (index + 2))
+      return DOH_DNS_OUT_OF_RANGE;
+    class = get16bit(doh, index);
+    if(DNS_CLASS_IN != class)
+      return DOH_DNS_UNEXPECTED_CLASS; /* unsupported */
+    index += 2;
+
+    if(dohlen < (index + 4))
+      return DOH_DNS_OUT_OF_RANGE;
+
+    ttl = get32bit(doh, index);
+    if(ttl < d->ttl)
+      d->ttl = ttl;
+    index += 4;
+
+    if(dohlen < (index + 2))
+      return DOH_DNS_OUT_OF_RANGE;
+
+    rdlength = get16bit(doh, index);
+    index += 2;
+    if(dohlen < (index + rdlength))
+      return DOH_DNS_OUT_OF_RANGE;
+
+    rc = rdata(doh, dohlen, rdlength, type, index, d);
+    if(rc)
+      return rc; /* bad rdata */
+    index += rdlength;
+    ancount--;
+  }
+
+  nscount = get16bit(doh, 8);
+  while(nscount) {
+    rc = skipqname(doh, dohlen, &index);
+    if(rc)
+      return rc; /* bad qname */
+
+    if(dohlen < (index + 8))
+      return DOH_DNS_OUT_OF_RANGE;
+
+    index += 2 + 2 + 4; /* type, class and ttl */
+
+    if(dohlen < (index + 2))
+      return DOH_DNS_OUT_OF_RANGE;
+
+    rdlength = get16bit(doh, index);
+    index += 2;
+    if(dohlen < (index + rdlength))
+      return DOH_DNS_OUT_OF_RANGE;
+    index += rdlength;
+    nscount--;
+  }
+
+  arcount = get16bit(doh, 10);
+  while(arcount) {
+    rc = skipqname(doh, dohlen, &index);
+    if(rc)
+      return rc; /* bad qname */
+
+    if(dohlen < (index + 8))
+      return DOH_DNS_OUT_OF_RANGE;
+
+    index += 2 + 2 + 4; /* type, class and ttl */
+
+    if(dohlen < (index + 2))
+      return DOH_DNS_OUT_OF_RANGE;
+
+    rdlength = get16bit(doh, index);
+    index += 2;
+    if(dohlen < (index + rdlength))
+      return DOH_DNS_OUT_OF_RANGE;
+    index += rdlength;
+    arcount--;
+  }
+
+  if(index != dohlen)
+    return DOH_DNS_MALFORMAT; /* something is wrong */
+
+  if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
+    /* nothing stored! */
+    return DOH_NO_CONTENT;
+
+  return DOH_OK; /* ok */
+}
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void showdoh(struct Curl_easy *data,
+                    struct dohentry *d)
+{
+  int i;
+  infof(data, "TTL: %u seconds\n", d->ttl);
+  for(i = 0; i < d->numaddr; i++) {
+    struct dohaddr *a = &d->addr[i];
+    if(a->type == DNS_TYPE_A) {
+      infof(data, "DOH A: %u.%u.%u.%u\n",
+            a->ip.v4[0], a->ip.v4[1],
+            a->ip.v4[2], a->ip.v4[3]);
+    }
+    else if(a->type == DNS_TYPE_AAAA) {
+      int j;
+      char buffer[128];
+      char *ptr;
+      size_t len;
+      snprintf(buffer, 128, "DOH AAAA: ");
+      ptr = &buffer[10];
+      len = 118;
+      for(j = 0; j < 16; j += 2) {
+        size_t l;
+        snprintf(ptr, len, "%s%02x%02x", j?":":"", d->addr[i].ip.v6[j],
+                 d->addr[i].ip.v6[j + 1]);
+        l = strlen(ptr);
+        len -= l;
+        ptr += l;
+      }
+      infof(data, "%s\n", buffer);
+    }
+  }
+  for(i = 0; i < d->numcname; i++) {
+    infof(data, "CNAME: %s\n", d->cname[i].alloc);
+  }
+}
+#else
+#define showdoh(x,y)
+#endif
+
+/*
+ * 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
+ * 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.
+ *
+ * The memory allocated by this function *MUST* be free'd later on calling
+ * Curl_freeaddrinfo().  For each successful call to this function there
+ * must be an associated call later to Curl_freeaddrinfo().
+ */
+
+static Curl_addrinfo *
+doh2ai(const struct dohentry *de, const char *hostname, int port)
+{
+  Curl_addrinfo *ai;
+  Curl_addrinfo *prevai = NULL;
+  Curl_addrinfo *firstai = NULL;
+  struct sockaddr_in *addr;
+#ifdef ENABLE_IPV6
+  struct sockaddr_in6 *addr6;
+#endif
+  CURLcode result = CURLE_OK;
+  int i;
+
+  if(!de)
+    /* no input == no output! */
+    return NULL;
+
+  for(i = 0; i < de->numaddr; i++) {
+    size_t ss_size;
+    CURL_SA_FAMILY_T addrtype;
+    if(de->addr[i].type == DNS_TYPE_AAAA) {
+#ifndef ENABLE_IPV6
+      /* we can't handle IPv6 addresses */
+      continue;
+#else
+      ss_size = sizeof(struct sockaddr_in6);
+      addrtype = AF_INET6;
+#endif
+    }
+    else {
+      ss_size = sizeof(struct sockaddr_in);
+      addrtype = AF_INET;
+    }
+
+    ai = calloc(1, sizeof(Curl_addrinfo));
+    if(!ai) {
+      result = CURLE_OUT_OF_MEMORY;
+      break;
+    }
+    ai->ai_canonname = strdup(hostname);
+    if(!ai->ai_canonname) {
+      result = CURLE_OUT_OF_MEMORY;
+      free(ai);
+      break;
+    }
+    ai->ai_addr = calloc(1, ss_size);
+    if(!ai->ai_addr) {
+      result = CURLE_OUT_OF_MEMORY;
+      free(ai->ai_canonname);
+      free(ai);
+      break;
+    }
+
+    if(!firstai)
+      /* store the pointer we want to return from this function */
+      firstai = ai;
+
+    if(prevai)
+      /* make the previous entry point to this */
+      prevai->ai_next = ai;
+
+    ai->ai_family = addrtype;
+
+    /* we return all names as STREAM, so when using this address for TFTP
+       the type must be ignored and conn->socktype be used instead! */
+    ai->ai_socktype = SOCK_STREAM;
+
+    ai->ai_addrlen = (curl_socklen_t)ss_size;
+
+    /* leave the rest of the struct filled with zero */
+
+    switch(ai->ai_family) {
+    case AF_INET:
+      addr = (void *)ai->ai_addr; /* storage area for this info */
+      DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
+      memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
+      addr->sin_family = (CURL_SA_FAMILY_T)addrtype;
+      addr->sin_port = htons((unsigned short)port);
+      break;
+
+#ifdef ENABLE_IPV6
+    case AF_INET6:
+      addr6 = (void *)ai->ai_addr; /* storage area for this info */
+      DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
+      memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
+      addr6->sin6_family = (CURL_SA_FAMILY_T)addrtype;
+      addr6->sin6_port = htons((unsigned short)port);
+      break;
+#endif
+    }
+
+    prevai = ai;
+  }
+
+  if(result) {
+    Curl_freeaddrinfo(firstai);
+    firstai = NULL;
+  }
+
+  return firstai;
+}
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static const char *type2name(DNStype dnstype)
+{
+  return (dnstype == DNS_TYPE_A)?"A":"AAAA";
+}
+#endif
+
+UNITTEST void de_cleanup(struct dohentry *d)
+{
+  int i = 0;
+  for(i = 0; i < d->numcname; i++) {
+    free(d->cname[i].alloc);
+  }
+}
+
+CURLcode Curl_doh_is_resolved(struct connectdata *conn,
+                              struct Curl_dns_entry **dnsp)
+{
+  struct Curl_easy *data = conn->data;
+  *dnsp = NULL; /* defaults to no response */
+
+  if(!data->req.doh.probe[0].easy && !data->req.doh.probe[1].easy) {
+    failf(data, "Could not DOH-resolve: %s", conn->async.hostname);
+    return conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
+      CURLE_COULDNT_RESOLVE_HOST;
+  }
+  else if(!data->req.doh.pending) {
+    DOHcode rc;
+    DOHcode rc2;
+    struct dohentry de;
+    struct Curl_dns_entry *dns;
+    struct Curl_addrinfo *ai;
+    /* remove DOH handles from multi handle and close them */
+    curl_multi_remove_handle(data->multi, data->req.doh.probe[0].easy);
+    Curl_close(data->req.doh.probe[0].easy);
+    curl_multi_remove_handle(data->multi, data->req.doh.probe[1].easy);
+    Curl_close(data->req.doh.probe[1].easy);
+
+    /* parse the responses, create the struct and return it! */
+    init_dohentry(&de);
+    rc = doh_decode(data->req.doh.probe[0].serverdoh.memory,
+                    data->req.doh.probe[0].serverdoh.size,
+                    data->req.doh.probe[0].dnstype,
+                    &de);
+    free(data->req.doh.probe[0].serverdoh.memory);
+    if(rc) {
+      infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc),
+            type2name(data->req.doh.probe[0].dnstype),
+            data->req.doh.host);
+    }
+    rc2 = doh_decode(data->req.doh.probe[1].serverdoh.memory,
+                     data->req.doh.probe[1].serverdoh.size,
+                     data->req.doh.probe[1].dnstype,
+                     &de);
+    free(data->req.doh.probe[1].serverdoh.memory);
+    if(rc2) {
+      infof(data, "DOG: %s type %s for %s\n", doh_strerror(rc2),
+            type2name(data->req.doh.probe[1].dnstype),
+            data->req.doh.host);
+    }
+    if(!rc || !rc2) {
+      infof(data, "DOH Host name: %s\n", data->req.doh.host);
+      showdoh(data, &de);
+
+      ai = doh2ai(&de, data->req.doh.host, data->req.doh.port);
+      if(!ai) {
+        de_cleanup(&de);
+        return CURLE_OUT_OF_MEMORY;
+      }
+
+      if(data->share)
+        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+
+      /* we got a response, store it in the cache */
+      dns = Curl_cache_addr(data, ai, data->req.doh.host, data->req.doh.port);
+
+      if(data->share)
+        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+
+      de_cleanup(&de);
+      if(!dns)
+        /* returned failure, bail out nicely */
+        Curl_freeaddrinfo(ai);
+      else {
+        conn->async.dns = dns;
+        *dnsp = dns;
+        return CURLE_OK;
+      }
+    }
+    de_cleanup(&de);
+
+    return CURLE_COULDNT_RESOLVE_HOST;
+  }
+
+  return CURLE_OK;
+}
+
+#else /* !USE_NGHTTP2 */
+/*
+ */
+Curl_addrinfo *Curl_doh(struct connectdata *conn,
+                        const char *hostname,
+                        int port,
+                        int *waitp)
+{
+  (void)conn;
+  (void)hostname;
+  (void)port;
+  (void)waitp;
+  return NULL;
+}
+
+CURLcode Curl_doh_is_resolved(struct connectdata *conn,
+                              struct Curl_dns_entry **dnsp)
+{
+  (void)conn;
+  (void)dnsp;
+  return CURLE_NOT_BUILT_IN;
+}
+
+#endif /* USE_NGHTTP2 */

+ 105 - 0
lib/doh.h

@@ -0,0 +1,105 @@
+#ifndef HEADER_CURL_DOH_H
+#define HEADER_CURL_DOH_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 2018, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "urldata.h"
+#include "curl_addrinfo.h"
+
+/*
+ * Curl_doh() resolve a name using DoH (DNS-over-HTTPS). It resolves a name
+ * and returns a 'Curl_addrinfo *' with the address information.
+ */
+
+Curl_addrinfo *Curl_doh(struct connectdata *conn,
+                        const char *hostname,
+                        int port,
+                        int *waitp);
+
+CURLcode Curl_doh_is_resolved(struct connectdata *conn,
+                              struct Curl_dns_entry **dns);
+
+int Curl_doh_getsock(struct connectdata *conn, curl_socket_t *socks,
+                     int numsocks);
+
+typedef enum {
+  DOH_OK,
+  DOH_DNS_BAD_LABEL,    /* 1 */
+  DOH_DNS_OUT_OF_RANGE, /* 2 */
+  DOH_DNS_LABEL_LOOP,   /* 3 */
+  DOH_TOO_SMALL_BUFFER, /* 4 */
+  DOH_OUT_OF_MEM,       /* 5 */
+  DOH_DNS_RDATA_LEN,    /* 6 */
+  DOH_DNS_MALFORMAT,    /* 7 */
+  DOH_DNS_BAD_RCODE,    /* 8 - no such name */
+  DOH_DNS_UNEXPECTED_TYPE,  /* 9 */
+  DOH_DNS_UNEXPECTED_CLASS, /* 10 */
+  DOH_NO_CONTENT,           /* 11 */
+  DOH_DNS_BAD_ID            /* 12 */
+} DOHcode;
+
+typedef enum {
+  DNS_TYPE_A = 1,
+  DNS_TYPE_NS = 2,
+  DNS_TYPE_CNAME = 5,
+  DNS_TYPE_AAAA = 28
+} DNStype;
+
+#define DOH_MAX_ADDR 24
+#define DOH_MAX_CNAME 4
+
+struct cnamestore {
+  size_t len;       /* length of cname */
+  char *alloc;      /* allocated pointer */
+  size_t allocsize; /* allocated size */
+};
+
+struct dohaddr {
+  int type;
+  union {
+    unsigned char v4[4]; /* network byte order */
+    unsigned char v6[16];
+  } ip;
+};
+
+struct dohentry {
+  unsigned int ttl;
+  int numaddr;
+  struct dohaddr addr[DOH_MAX_ADDR];
+  int numcname;
+  struct cnamestore cname[DOH_MAX_CNAME];
+};
+
+
+#ifdef DEBUGBUILD
+DOHcode doh_encode(const char *host,
+                   DNStype dnstype,
+                   unsigned char *dnsp, /* buffer */
+                   size_t len,  /* buffer size */
+                   size_t *olen); /* output length */
+DOHcode doh_decode(unsigned char *doh,
+                   size_t dohlen,
+                   DNStype dnstype,
+                   struct dohentry *d);
+void de_cleanup(struct dohentry *d);
+#endif
+#endif /* HEADER_CURL_DOH_H */

+ 2 - 0
lib/dotdot.c

@@ -62,6 +62,8 @@ char *Curl_dedotdotify(const char *input)
   if(!out)
   if(!out)
     return NULL; /* out of memory */
     return NULL; /* out of memory */
 
 
+  *out = 0; /* zero terminates, for inputs like "./" */
+
   /* get a cloned copy of the input */
   /* get a cloned copy of the input */
   clone = strdup(input);
   clone = strdup(input);
   if(!clone) {
   if(!clone) {

+ 1 - 1
lib/dotdot.h

@@ -22,4 +22,4 @@
  *
  *
  ***************************************************************************/
  ***************************************************************************/
 char *Curl_dedotdotify(const char *input);
 char *Curl_dedotdotify(const char *input);
-#endif
+#endif /* HEADER_CURL_DOTDOT_H */

+ 19 - 4
lib/easy.c

@@ -1002,10 +1002,6 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
  */
  */
 void curl_easy_reset(struct Curl_easy *data)
 void curl_easy_reset(struct Curl_easy *data)
 {
 {
-  Curl_safefree(data->state.pathbuffer);
-
-  data->state.path = NULL;
-
   Curl_free_request_state(data);
   Curl_free_request_state(data);
 
 
   /* zero out UserDefined data: */
   /* zero out UserDefined data: */
@@ -1197,3 +1193,22 @@ CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer,
 
 
   return result;
   return result;
 }
 }
+
+/*
+ * Performs connection upkeep for the given session handle.
+ */
+CURLcode curl_easy_upkeep(struct Curl_easy *data)
+{
+  /* Verify that we got an easy handle we can work with. */
+  if(!GOOD_EASY_HANDLE(data))
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+
+  if(data->multi_easy) {
+    /* Use the common function to keep connections alive. */
+    return Curl_upkeep(&data->multi_easy->conn_cache, data);
+  }
+  else {
+    /* No connections, so just return success */
+    return CURLE_OK;
+  }
+}

+ 0 - 1
lib/easyif.h

@@ -30,4 +30,3 @@ CURL_EXTERN CURLcode curl_easy_perform_ev(struct Curl_easy *easy);
 #endif
 #endif
 
 
 #endif /* HEADER_CURL_EASYIF_H */
 #endif /* HEADER_CURL_EASYIF_H */
-

+ 12 - 8
lib/escape.c

@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *                             \___|\___/|_| \_\_____|
  *
  *
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <[email protected]>, et al.
  *
  *
  * This software is licensed as described in the file COPYING, which
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * you should have received as part of this distribution. The terms
@@ -41,7 +41,7 @@
    its behavior is altered by the current locale.
    its behavior is altered by the current locale.
    See https://tools.ietf.org/html/rfc3986#section-2.3
    See https://tools.ietf.org/html/rfc3986#section-2.3
 */
 */
-static bool Curl_isunreserved(unsigned char in)
+bool Curl_isunreserved(unsigned char in)
 {
 {
   switch(in) {
   switch(in) {
     case '0': case '1': case '2': case '3': case '4':
     case '0': case '1': case '2': case '3': case '4':
@@ -141,6 +141,8 @@ char *curl_easy_escape(struct Curl_easy *data, const char *string,
  * Returns a pointer to a malloced string in *ostring with length given in
  * Returns a pointer to a malloced string in *ostring with length given in
  * *olen. If length == 0, the length is assumed to be strlen(string).
  * *olen. If length == 0, the length is assumed to be strlen(string).
  *
  *
+ * 'data' can be set to NULL but then this function can't convert network
+ * data to host for non-ascii.
  */
  */
 CURLcode Curl_urldecode(struct Curl_easy *data,
 CURLcode Curl_urldecode(struct Curl_easy *data,
                         const char *string, size_t length,
                         const char *string, size_t length,
@@ -151,7 +153,7 @@ CURLcode Curl_urldecode(struct Curl_easy *data,
   char *ns = malloc(alloc);
   char *ns = malloc(alloc);
   size_t strindex = 0;
   size_t strindex = 0;
   unsigned long hex;
   unsigned long hex;
-  CURLcode result;
+  CURLcode result = CURLE_OK;
 
 
   if(!ns)
   if(!ns)
     return CURLE_OUT_OF_MEMORY;
     return CURLE_OUT_OF_MEMORY;
@@ -171,11 +173,13 @@ CURLcode Curl_urldecode(struct Curl_easy *data,
 
 
       in = curlx_ultouc(hex); /* this long is never bigger than 255 anyway */
       in = curlx_ultouc(hex); /* this long is never bigger than 255 anyway */
 
 
-      result = Curl_convert_from_network(data, (char *)&in, 1);
-      if(result) {
-        /* Curl_convert_from_network calls failf if unsuccessful */
-        free(ns);
-        return result;
+      if(data) {
+        result = Curl_convert_from_network(data, (char *)&in, 1);
+        if(result) {
+          /* Curl_convert_from_network calls failf if unsuccessful */
+          free(ns);
+          return result;
+        }
       }
       }
 
 
       string += 2;
       string += 2;

+ 2 - 2
lib/escape.h

@@ -7,7 +7,7 @@
  *                            | (__| |_| |  _ <| |___
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *                             \___|\___/|_| \_\_____|
  *
  *
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <[email protected]>, et al.
  *
  *
  * This software is licensed as described in the file COPYING, which
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * you should have received as part of this distribution. The terms
@@ -24,10 +24,10 @@
 /* Escape and unescape URL encoding in strings. The functions return a new
 /* Escape and unescape URL encoding in strings. The functions return a new
  * allocated string or NULL if an error occurred.  */
  * allocated string or NULL if an error occurred.  */
 
 
+bool Curl_isunreserved(unsigned char in);
 CURLcode Curl_urldecode(struct Curl_easy *data,
 CURLcode Curl_urldecode(struct Curl_easy *data,
                         const char *string, size_t length,
                         const char *string, size_t length,
                         char **ostring, size_t *olen,
                         char **ostring, size_t *olen,
                         bool reject_crlf);
                         bool reject_crlf);
 
 
 #endif /* HEADER_CURL_ESCAPE_H */
 #endif /* HEADER_CURL_ESCAPE_H */
-

+ 16 - 17
lib/file.c

@@ -143,7 +143,7 @@ static CURLcode file_connect(struct connectdata *conn, bool *done)
 #endif
 #endif
   size_t real_path_len;
   size_t real_path_len;
 
 
-  CURLcode result = Curl_urldecode(data, data->state.path, 0, &real_path,
+  CURLcode result = Curl_urldecode(data, data->state.up.path, 0, &real_path,
                                    &real_path_len, FALSE);
                                    &real_path_len, FALSE);
   if(result)
   if(result)
     return result;
     return result;
@@ -197,7 +197,7 @@ static CURLcode file_connect(struct connectdata *conn, bool *done)
 
 
   file->fd = fd;
   file->fd = fd;
   if(!data->set.upload && (fd == -1)) {
   if(!data->set.upload && (fd == -1)) {
-    failf(data, "Couldn't open file %s", data->state.path);
+    failf(data, "Couldn't open file %s", data->state.up.path);
     file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE);
     file_done(conn, CURLE_FILE_COULDNT_READ_FILE, FALSE);
     return CURLE_FILE_COULDNT_READ_FILE;
     return CURLE_FILE_COULDNT_READ_FILE;
   }
   }
@@ -307,7 +307,7 @@ static CURLcode file_upload(struct connectdata *conn)
     size_t nread;
     size_t nread;
     size_t nwrite;
     size_t nwrite;
     size_t readcount;
     size_t readcount;
-    result = Curl_fillreadbuffer(conn, (int)data->set.buffer_size, &readcount);
+    result = Curl_fillreadbuffer(conn, data->set.buffer_size, &readcount);
     if(result)
     if(result)
       break;
       break;
 
 
@@ -386,7 +386,6 @@ static CURLcode file_do(struct connectdata *conn, bool *done)
 
 
   *done = TRUE; /* unconditionally */
   *done = TRUE; /* unconditionally */
 
 
-  Curl_initinfo(data);
   Curl_pgrsStartNow(data);
   Curl_pgrsStartNow(data);
 
 
   if(data->set.upload)
   if(data->set.upload)
@@ -413,21 +412,18 @@ static CURLcode file_do(struct connectdata *conn, bool *done)
     }
     }
   }
   }
 
 
-  /* If we have selected NOBODY and HEADER, it means that we only want file
-     information. Which for FILE can't be much more than the file size and
-     date. */
-  if(data->set.opt_no_body && data->set.include_header && fstated) {
+  if(fstated) {
     time_t filetime;
     time_t filetime;
     struct tm buffer;
     struct tm buffer;
     const struct tm *tm = &buffer;
     const struct tm *tm = &buffer;
     char header[80];
     char header[80];
     snprintf(header, sizeof(header),
     snprintf(header, sizeof(header),
              "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", expected_size);
              "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", expected_size);
-    result = Curl_client_write(conn, CLIENTWRITE_BOTH, header, 0);
+    result = Curl_client_write(conn, CLIENTWRITE_HEADER, header, 0);
     if(result)
     if(result)
       return result;
       return result;
 
 
-    result = Curl_client_write(conn, CLIENTWRITE_BOTH,
+    result = Curl_client_write(conn, CLIENTWRITE_HEADER,
                                (char *)"Accept-ranges: bytes\r\n", 0);
                                (char *)"Accept-ranges: bytes\r\n", 0);
     if(result)
     if(result)
       return result;
       return result;
@@ -439,19 +435,22 @@ static CURLcode file_do(struct connectdata *conn, bool *done)
 
 
     /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
     /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
     snprintf(header, sizeof(header),
     snprintf(header, sizeof(header),
-             "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
+             "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n%s",
              Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
              Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
              tm->tm_mday,
              tm->tm_mday,
              Curl_month[tm->tm_mon],
              Curl_month[tm->tm_mon],
              tm->tm_year + 1900,
              tm->tm_year + 1900,
              tm->tm_hour,
              tm->tm_hour,
              tm->tm_min,
              tm->tm_min,
-             tm->tm_sec);
-    result = Curl_client_write(conn, CLIENTWRITE_BOTH, header, 0);
-    if(!result)
-      /* set the file size to make it available post transfer */
-      Curl_pgrsSetDownloadSize(data, expected_size);
-    return result;
+             tm->tm_sec,
+             data->set.opt_no_body ? "": "\r\n");
+    result = Curl_client_write(conn, CLIENTWRITE_HEADER, header, 0);
+    if(result)
+      return result;
+    /* set the file size to make it available post transfer */
+    Curl_pgrsSetDownloadSize(data, expected_size);
+    if(data->set.opt_no_body)
+      return result;
   }
   }
 
 
   /* Check whether file range has been specified */
   /* Check whether file range has been specified */

+ 0 - 1
lib/file.h

@@ -38,4 +38,3 @@ extern const struct Curl_handler Curl_handler_file;
 #endif
 #endif
 
 
 #endif /* HEADER_CURL_FILE_H */
 #endif /* HEADER_CURL_FILE_H */
-

+ 26 - 20
lib/ftp.c

@@ -1444,6 +1444,7 @@ static CURLcode ftp_state_list(struct connectdata *conn)
 {
 {
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
   struct Curl_easy *data = conn->data;
   struct Curl_easy *data = conn->data;
+  struct FTP *ftp = data->req.protop;
 
 
   /* If this output is to be machine-parsed, the NLST command might be better
   /* If this output is to be machine-parsed, the NLST command might be better
      to use, since the LIST command output is not specified or standard in any
      to use, since the LIST command output is not specified or standard in any
@@ -1460,7 +1461,7 @@ static CURLcode ftp_state_list(struct connectdata *conn)
      then just do LIST (in that case: nothing to do here)
      then just do LIST (in that case: nothing to do here)
   */
   */
   char *cmd, *lstArg, *slashPos;
   char *cmd, *lstArg, *slashPos;
-  const char *inpath = data->state.path;
+  const char *inpath = ftp->path;
 
 
   lstArg = NULL;
   lstArg = NULL;
   if((data->set.ftp_filemethod == FTPFILE_NOCWD) &&
   if((data->set.ftp_filemethod == FTPFILE_NOCWD) &&
@@ -3141,7 +3142,6 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
   int ftpcode;
   int ftpcode;
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
   char *path = NULL;
   char *path = NULL;
-  const char *path_to_use = data->state.path;
 
 
   if(!ftp)
   if(!ftp)
     return CURLE_OK;
     return CURLE_OK;
@@ -3193,7 +3193,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
 
 
   if(!result)
   if(!result)
     /* get the "raw" path */
     /* get the "raw" path */
-    result = Curl_urldecode(data, path_to_use, 0, &path, NULL, TRUE);
+    result = Curl_urldecode(data, ftp->path, 0, &path, NULL, TRUE);
   if(result) {
   if(result) {
     /* We can limp along anyway (and should try to since we may already be in
     /* We can limp along anyway (and should try to since we may already be in
      * the error path) */
      * the error path) */
@@ -3213,9 +3213,11 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
           ftpc->prevpath[dlen] = 0; /* terminate */
           ftpc->prevpath[dlen] = 0; /* terminate */
       }
       }
       else {
       else {
+        free(path);
         /* we never changed dir */
         /* we never changed dir */
         ftpc->prevpath = strdup("");
         ftpc->prevpath = strdup("");
-        free(path);
+        if(!ftpc->prevpath)
+          return CURLE_OUT_OF_MEMORY;
       }
       }
       if(ftpc->prevpath)
       if(ftpc->prevpath)
         infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath);
         infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath);
@@ -3346,7 +3348,7 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
   /* Send any post-transfer QUOTE strings? */
   /* Send any post-transfer QUOTE strings? */
   if(!status && !result && !premature && data->set.postquote)
   if(!status && !result && !premature && data->set.postquote)
     result = ftp_sendquote(conn, data->set.postquote);
     result = ftp_sendquote(conn, data->set.postquote);
-
+  Curl_safefree(ftp->pathalloc);
   return result;
   return result;
 }
 }
 
 
@@ -3695,12 +3697,13 @@ static void wc_data_dtor(void *ptr)
 static CURLcode init_wc_data(struct connectdata *conn)
 static CURLcode init_wc_data(struct connectdata *conn)
 {
 {
   char *last_slash;
   char *last_slash;
-  char *path = conn->data->state.path;
+  struct FTP *ftp = conn->data->req.protop;
+  char *path = ftp->path;
   struct WildcardData *wildcard = &(conn->data->wildcard);
   struct WildcardData *wildcard = &(conn->data->wildcard);
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
   struct ftp_wc *ftpwc = NULL;
   struct ftp_wc *ftpwc = NULL;
 
 
-  last_slash = strrchr(conn->data->state.path, '/');
+  last_slash = strrchr(ftp->path, '/');
   if(last_slash) {
   if(last_slash) {
     last_slash++;
     last_slash++;
     if(last_slash[0] == '\0') {
     if(last_slash[0] == '\0') {
@@ -3757,7 +3760,7 @@ static CURLcode init_wc_data(struct connectdata *conn)
     goto fail;
     goto fail;
   }
   }
 
 
-  wildcard->path = strdup(conn->data->state.path);
+  wildcard->path = strdup(ftp->path);
   if(!wildcard->path) {
   if(!wildcard->path) {
     result = CURLE_OUT_OF_MEMORY;
     result = CURLE_OUT_OF_MEMORY;
     goto fail;
     goto fail;
@@ -3828,16 +3831,15 @@ static CURLcode wc_statemach(struct connectdata *conn)
     /* filelist has at least one file, lets get first one */
     /* filelist has at least one file, lets get first one */
     struct ftp_conn *ftpc = &conn->proto.ftpc;
     struct ftp_conn *ftpc = &conn->proto.ftpc;
     struct curl_fileinfo *finfo = wildcard->filelist.head->ptr;
     struct curl_fileinfo *finfo = wildcard->filelist.head->ptr;
+    struct FTP *ftp = conn->data->req.protop;
 
 
     char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
     char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
     if(!tmp_path)
     if(!tmp_path)
       return CURLE_OUT_OF_MEMORY;
       return CURLE_OUT_OF_MEMORY;
 
 
-    /* switch default "state.pathbuffer" and tmp_path, good to see
-       ftp_parse_url_path function to understand this trick */
-    Curl_safefree(conn->data->state.pathbuffer);
-    conn->data->state.pathbuffer = tmp_path;
-    conn->data->state.path = tmp_path;
+    /* switch default ftp->path and tmp_path */
+    free(ftp->pathalloc);
+    ftp->pathalloc = ftp->path = tmp_path;
 
 
     infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
     infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
     if(conn->data->set.chunk_bgn) {
     if(conn->data->set.chunk_bgn) {
@@ -3963,10 +3965,14 @@ CURLcode Curl_ftpsend(struct connectdata *conn, const char *cmd)
   enum protection_level data_sec = conn->data_prot;
   enum protection_level data_sec = conn->data_prot;
 #endif
 #endif
 
 
+  if(!cmd)
+    return CURLE_BAD_FUNCTION_ARGUMENT;
+
   write_len = strlen(cmd);
   write_len = strlen(cmd);
-  if(write_len > (sizeof(s) -3))
+  if(!write_len || write_len > (sizeof(s) -3))
     return CURLE_BAD_FUNCTION_ARGUMENT;
     return CURLE_BAD_FUNCTION_ARGUMENT;
 
 
+  memcpy(&s, cmd, write_len);
   strcpy(&s[write_len], "\r\n"); /* append a trailing CRLF */
   strcpy(&s[write_len], "\r\n"); /* append a trailing CRLF */
   write_len += 2;
   write_len += 2;
   bytes_written = 0;
   bytes_written = 0;
@@ -4101,7 +4107,7 @@ CURLcode ftp_parse_url_path(struct connectdata *conn)
   struct FTP *ftp = data->req.protop;
   struct FTP *ftp = data->req.protop;
   struct ftp_conn *ftpc = &conn->proto.ftpc;
   struct ftp_conn *ftpc = &conn->proto.ftpc;
   const char *slash_pos;  /* position of the first '/' char in curpos */
   const char *slash_pos;  /* position of the first '/' char in curpos */
-  const char *path_to_use = data->state.path;
+  const char *path_to_use = ftp->path;
   const char *cur_pos;
   const char *cur_pos;
   const char *filename = NULL;
   const char *filename = NULL;
 
 
@@ -4187,7 +4193,7 @@ CURLcode ftp_parse_url_path(struct connectdata *conn)
       /* parse the URL path into separate path components */
       /* parse the URL path into separate path components */
       while((slash_pos = strchr(cur_pos, '/')) != NULL) {
       while((slash_pos = strchr(cur_pos, '/')) != NULL) {
         /* 1 or 0 pointer offset to indicate absolute directory */
         /* 1 or 0 pointer offset to indicate absolute directory */
-        ssize_t absolute_dir = ((cur_pos - data->state.path > 0) &&
+        ssize_t absolute_dir = ((cur_pos - ftp->path > 0) &&
                                 (ftpc->dirdepth == 0))?1:0;
                                 (ftpc->dirdepth == 0))?1:0;
 
 
         /* seek out the next path component */
         /* seek out the next path component */
@@ -4264,7 +4270,7 @@ CURLcode ftp_parse_url_path(struct connectdata *conn)
     size_t dlen;
     size_t dlen;
     char *path;
     char *path;
     CURLcode result =
     CURLcode result =
-      Curl_urldecode(conn->data, data->state.path, 0, &path, &dlen, TRUE);
+      Curl_urldecode(conn->data, ftp->path, 0, &path, &dlen, TRUE);
     if(result) {
     if(result) {
       freedirs(ftpc);
       freedirs(ftpc);
       return result;
       return result;
@@ -4384,16 +4390,16 @@ static CURLcode ftp_setup_connection(struct connectdata *conn)
   char *type;
   char *type;
   struct FTP *ftp;
   struct FTP *ftp;
 
 
-  conn->data->req.protop = ftp = malloc(sizeof(struct FTP));
+  conn->data->req.protop = ftp = calloc(sizeof(struct FTP), 1);
   if(NULL == ftp)
   if(NULL == ftp)
     return CURLE_OUT_OF_MEMORY;
     return CURLE_OUT_OF_MEMORY;
 
 
-  data->state.path++;   /* don't include the initial slash */
+  ftp->path = &data->state.up.path[1]; /* don't include the initial slash */
   data->state.slash_removed = TRUE; /* we've skipped the slash */
   data->state.slash_removed = TRUE; /* we've skipped the slash */
 
 
   /* FTP URLs support an extension like ";type=<typecode>" that
   /* FTP URLs support an extension like ";type=<typecode>" that
    * we'll try to get now! */
    * we'll try to get now! */
-  type = strstr(data->state.path, ";type=");
+  type = strstr(ftp->path, ";type=");
 
 
   if(!type)
   if(!type)
     type = strstr(conn->host.rawalloc, ";type=");
     type = strstr(conn->host.rawalloc, ";type=");

+ 2 - 0
lib/ftp.h

@@ -105,6 +105,8 @@ struct FTP {
   curl_off_t *bytecountp;
   curl_off_t *bytecountp;
   char *user;    /* user name string */
   char *user;    /* user name string */
   char *passwd;  /* password string */
   char *passwd;  /* password string */
+  char *path;    /* points to the urlpieces struct field */
+  char *pathalloc; /* if non-NULL a pointer to an allocated path */
 
 
   /* transfer a file/body or not, done as a typedefed enum just to make
   /* transfer a file/body or not, done as a typedefed enum just to make
      debuggers display the full symbol and not just the numerical value */
      debuggers display the full symbol and not just the numerical value */

+ 0 - 1
lib/getinfo.c

@@ -85,7 +85,6 @@ CURLcode Curl_initinfo(struct Curl_easy *data)
 #ifdef USE_SSL
 #ifdef USE_SSL
   Curl_ssl_free_certinfo(data);
   Curl_ssl_free_certinfo(data);
 #endif
 #endif
-
   return CURLE_OK;
   return CURLE_OK;
 }
 }
 
 

+ 1 - 1
lib/gopher.c

@@ -78,7 +78,7 @@ static CURLcode gopher_do(struct connectdata *conn, bool *done)
   curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
   curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
 
 
   curl_off_t *bytecount = &data->req.bytecount;
   curl_off_t *bytecount = &data->req.bytecount;
-  char *path = data->state.path;
+  char *path = data->state.up.path;
   char *sel = NULL;
   char *sel = NULL;
   char *sel_org = NULL;
   char *sel_org = NULL;
   ssize_t amount, k;
   ssize_t amount, k;

+ 0 - 25
lib/hostasyn.c

@@ -111,31 +111,6 @@ CURLcode Curl_addrinfo_callback(struct connectdata *conn,
   return result;
   return result;
 }
 }
 
 
-/* Call this function after Curl_connect() has returned async=TRUE and
-   then a successful name resolve has been received.
-
-   Note: this function disconnects and frees the conn data in case of
-   resolve failure */
-CURLcode Curl_async_resolved(struct connectdata *conn,
-                             bool *protocol_done)
-{
-  CURLcode result;
-
-  if(conn->async.dns) {
-    conn->dns_entry = conn->async.dns;
-    conn->async.dns = NULL;
-  }
-
-  result = Curl_setup_conn(conn, protocol_done);
-
-  if(result)
-    /* We're not allowed to return failure with memory left allocated
-       in the connectdata struct, free those here */
-    Curl_disconnect(conn->data, conn, TRUE); /* close the connection */
-
-  return result;
-}
-
 /*
 /*
  * Curl_getaddrinfo() is the generic low-level name resolve API within this
  * Curl_getaddrinfo() is the generic low-level name resolve API within this
  * source file. There are several versions of this function - for different
  * source file. There are several versions of this function - for different

+ 0 - 1
lib/hostcheck.h

@@ -29,4 +29,3 @@
 int Curl_cert_hostcheck(const char *match_pattern, const char *hostname);
 int Curl_cert_hostcheck(const char *match_pattern, const char *hostname);
 
 
 #endif /* HEADER_CURL_HOSTCHECK_H */
 #endif /* HEADER_CURL_HOSTCHECK_H */
-

+ 67 - 11
lib/hostip.c

@@ -60,6 +60,7 @@
 #include "url.h"
 #include "url.h"
 #include "inet_ntop.h"
 #include "inet_ntop.h"
 #include "multiif.h"
 #include "multiif.h"
+#include "doh.h"
 #include "warnless.h"
 #include "warnless.h"
 /* The last 3 #include files should be in this order */
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_printf.h"
@@ -454,7 +455,7 @@ Curl_cache_addr(struct Curl_easy *data,
   /* shuffle addresses if requested */
   /* shuffle addresses if requested */
   if(data->set.dns_shuffle_addresses) {
   if(data->set.dns_shuffle_addresses) {
     CURLcode result = Curl_shuffle_addr(data, &addr);
     CURLcode result = Curl_shuffle_addr(data, &addr);
-    if(!result)
+    if(result)
       return NULL;
       return NULL;
   }
   }
 
 
@@ -565,23 +566,27 @@ int Curl_resolv(struct connectdata *conn,
         return CURLRESOLV_ERROR;
         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 */
-    addr = Curl_getaddrinfo(conn,
+    if(data->set.doh) {
+      addr = Curl_doh(conn, hostname, port, &respwait);
+    }
+    else {
+      /* 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 */
+      addr = Curl_getaddrinfo(conn,
 #ifdef DEBUGBUILD
 #ifdef DEBUGBUILD
-                            (data->set.str[STRING_DEVICE]
-                             && !strcmp(data->set.str[STRING_DEVICE],
-                                        "LocalHost"))?"localhost":
+                              (data->set.str[STRING_DEVICE]
+                               && !strcmp(data->set.str[STRING_DEVICE],
+                                          "LocalHost"))?"localhost":
 #endif
 #endif
-                            hostname, port, &respwait);
-
+                              hostname, port, &respwait);
+    }
     if(!addr) {
     if(!addr) {
       if(respwait) {
       if(respwait) {
         /* the response to our resolve call will come asynchronously at
         /* the response to our resolve call will come asynchronously at
            a later time, good or bad */
            a later time, good or bad */
         /* First, check that we haven't received the info by now */
         /* First, check that we haven't received the info by now */
-        result = Curl_resolver_is_resolved(conn, &dns);
+        result = Curl_resolv_check(conn, &dns);
         if(result) /* error detected */
         if(result) /* error detected */
           return CURLRESOLV_ERROR;
           return CURLRESOLV_ERROR;
         if(dns)
         if(dns)
@@ -1053,3 +1058,54 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
 
 
   return CURLE_OK;
   return CURLE_OK;
 }
 }
+
+CURLcode Curl_resolv_check(struct connectdata *conn,
+                           struct Curl_dns_entry **dns)
+{
+  if(conn->data->set.doh)
+    return Curl_doh_is_resolved(conn, dns);
+  return Curl_resolver_is_resolved(conn, dns);
+}
+
+int Curl_resolv_getsock(struct connectdata *conn,
+                        curl_socket_t *socks,
+                        int numsocks)
+{
+#ifdef CURLRES_ASYNCH
+  if(conn->data->set.doh)
+    /* nothing to wait for during DOH resolve, those handles have their own
+       sockets */
+    return GETSOCK_BLANK;
+  return Curl_resolver_getsock(conn, socks, numsocks);
+#else
+  (void)conn;
+  (void)socks;
+  (void)numsocks;
+  return GETSOCK_BLANK;
+#endif
+}
+
+/* Call this function after Curl_connect() has returned async=TRUE and
+   then a successful name resolve has been received.
+
+   Note: this function disconnects and frees the conn data in case of
+   resolve failure */
+CURLcode Curl_once_resolved(struct connectdata *conn,
+                            bool *protocol_done)
+{
+  CURLcode result;
+
+  if(conn->async.dns) {
+    conn->dns_entry = conn->async.dns;
+    conn->async.dns = NULL;
+  }
+
+  result = Curl_setup_conn(conn, protocol_done);
+
+  if(result)
+    /* We're not allowed to return failure with memory left allocated
+       in the connectdata struct, free those here */
+    Curl_disconnect(conn->data, conn, TRUE); /* close the connection */
+
+  return result;
+}

+ 7 - 6
lib/hostip.h

@@ -145,12 +145,7 @@ int curl_dogetnameinfo(GETNAMEINFO_QUAL_ARG1 GETNAMEINFO_TYPE_ARG1 sa,
 /* IPv4 threadsafe resolve function used for synch and asynch builds */
 /* IPv4 threadsafe resolve function used for synch and asynch builds */
 Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port);
 Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port);
 
 
-CURLcode Curl_async_resolved(struct connectdata *conn,
-                             bool *protocol_connect);
-
-#ifndef CURLRES_ASYNCH
-#define Curl_async_resolved(x,y) CURLE_OK
-#endif
+CURLcode Curl_once_resolved(struct connectdata *conn, bool *protocol_connect);
 
 
 /*
 /*
  * Curl_addrinfo_callback() is used when we build with any asynch specialty.
  * Curl_addrinfo_callback() is used when we build with any asynch specialty.
@@ -258,4 +253,10 @@ void Curl_hostcache_destroy(struct Curl_easy *data);
  */
  */
 CURLcode Curl_loadhostpairs(struct Curl_easy *data);
 CURLcode Curl_loadhostpairs(struct Curl_easy *data);
 
 
+CURLcode Curl_resolv_check(struct connectdata *conn,
+                           struct Curl_dns_entry **dns);
+int Curl_resolv_getsock(struct connectdata *conn,
+                        curl_socket_t *socks,
+                        int numsocks);
+
 #endif /* HEADER_CURL_HOSTIP_H */
 #endif /* HEADER_CURL_HOSTIP_H */

+ 138 - 107
lib/http.c

@@ -169,7 +169,7 @@ CURLcode Curl_http_setup_conn(struct connectdata *conn)
   data->req.protop = http;
   data->req.protop = http;
 
 
   if(!CONN_INUSE(conn))
   if(!CONN_INUSE(conn))
-    /* if not alredy multi-using, setup connection details */
+    /* if not already multi-using, setup connection details */
     Curl_http2_setup_conn(conn);
     Curl_http2_setup_conn(conn);
   Curl_http2_setup_req(data);
   Curl_http2_setup_req(data);
   return CURLE_OK;
   return CURLE_OK;
@@ -537,14 +537,6 @@ CURLcode Curl_http_auth_act(struct connectdata *conn)
   }
   }
 
 
   if(pickhost || pickproxy) {
   if(pickhost || pickproxy) {
-    /* In case this is GSS auth, the newurl field is already allocated so
-       we must make sure to free it before allocating a new one. As figured
-       out in bug #2284386 */
-    Curl_safefree(data->req.newurl);
-    data->req.newurl = strdup(data->change.url); /* clone URL */
-    if(!data->req.newurl)
-      return CURLE_OUT_OF_MEMORY;
-
     if((data->set.httpreq != HTTPREQ_GET) &&
     if((data->set.httpreq != HTTPREQ_GET) &&
        (data->set.httpreq != HTTPREQ_HEAD) &&
        (data->set.httpreq != HTTPREQ_HEAD) &&
        !conn->bits.rewindaftersend) {
        !conn->bits.rewindaftersend) {
@@ -552,6 +544,13 @@ CURLcode Curl_http_auth_act(struct connectdata *conn)
       if(result)
       if(result)
         return result;
         return result;
     }
     }
+    /* In case this is GSS auth, the newurl field is already allocated so
+       we must make sure to free it before allocating a new one. As figured
+       out in bug #2284386 */
+    Curl_safefree(data->req.newurl);
+    data->req.newurl = strdup(data->change.url); /* clone URL */
+    if(!data->req.newurl)
+      return CURLE_OUT_OF_MEMORY;
   }
   }
   else if((data->req.httpcode < 300) &&
   else if((data->req.httpcode < 300) &&
           (!data->state.authhost.done) &&
           (!data->state.authhost.done) &&
@@ -1094,11 +1093,13 @@ Curl_send_buffer *Curl_add_buffer_init(void)
 /*
 /*
  * Curl_add_buffer_free() frees all associated resources.
  * Curl_add_buffer_free() frees all associated resources.
  */
  */
-void Curl_add_buffer_free(Curl_send_buffer *buff)
+void Curl_add_buffer_free(Curl_send_buffer **inp)
 {
 {
-  if(buff) /* deal with NULL input */
-    free(buff->buffer);
-  free(buff);
+  Curl_send_buffer *in = *inp;
+  if(in) /* deal with NULL input */
+    free(in->buffer);
+  free(in);
+  *inp = NULL;
 }
 }
 
 
 /*
 /*
@@ -1107,7 +1108,7 @@ void Curl_add_buffer_free(Curl_send_buffer *buff)
  *
  *
  * Returns CURLcode
  * Returns CURLcode
  */
  */
-CURLcode Curl_add_buffer_send(Curl_send_buffer *in,
+CURLcode Curl_add_buffer_send(Curl_send_buffer **inp,
                               struct connectdata *conn,
                               struct connectdata *conn,
 
 
                                /* add the number of sent bytes to this
                                /* add the number of sent bytes to this
@@ -1128,6 +1129,7 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in,
   size_t sendsize;
   size_t sendsize;
   curl_socket_t sockfd;
   curl_socket_t sockfd;
   size_t headersize;
   size_t headersize;
+  Curl_send_buffer *in = *inp;
 
 
   DEBUGASSERT(socketindex <= SECONDARYSOCKET);
   DEBUGASSERT(socketindex <= SECONDARYSOCKET);
 
 
@@ -1148,7 +1150,7 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in,
   /* Curl_convert_to_network calls failf if unsuccessful */
   /* Curl_convert_to_network calls failf if unsuccessful */
   if(result) {
   if(result) {
     /* conversion failed, free memory and return to the caller */
     /* conversion failed, free memory and return to the caller */
-    Curl_add_buffer_free(in);
+    Curl_add_buffer_free(inp);
     return result;
     return result;
   }
   }
 
 
@@ -1172,7 +1174,7 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in,
     result = Curl_get_upload_buffer(data);
     result = Curl_get_upload_buffer(data);
     if(result) {
     if(result) {
       /* malloc failed, free memory and return to the caller */
       /* malloc failed, free memory and return to the caller */
-      Curl_add_buffer_free(in);
+      Curl_add_buffer_free(&in);
       return result;
       return result;
     }
     }
     memcpy(data->state.ulbuf, ptr, sendsize);
     memcpy(data->state.ulbuf, ptr, sendsize);
@@ -1256,7 +1258,7 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in,
       Curl_pipeline_leave_write(conn);
       Curl_pipeline_leave_write(conn);
     }
     }
   }
   }
-  Curl_add_buffer_free(in);
+  Curl_add_buffer_free(&in);
 
 
   return result;
   return result;
 }
 }
@@ -1265,31 +1267,35 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in,
 /*
 /*
  * add_bufferf() add the formatted input to the buffer.
  * add_bufferf() add the formatted input to the buffer.
  */
  */
-CURLcode Curl_add_bufferf(Curl_send_buffer *in, const char *fmt, ...)
+CURLcode Curl_add_bufferf(Curl_send_buffer **inp, const char *fmt, ...)
 {
 {
   char *s;
   char *s;
   va_list ap;
   va_list ap;
+  Curl_send_buffer *in = *inp;
   va_start(ap, fmt);
   va_start(ap, fmt);
   s = vaprintf(fmt, ap); /* this allocs a new string to append */
   s = vaprintf(fmt, ap); /* this allocs a new string to append */
   va_end(ap);
   va_end(ap);
 
 
   if(s) {
   if(s) {
-    CURLcode result = Curl_add_buffer(in, s, strlen(s));
+    CURLcode result = Curl_add_buffer(inp, s, strlen(s));
     free(s);
     free(s);
     return result;
     return result;
   }
   }
   /* If we failed, we cleanup the whole buffer and return error */
   /* If we failed, we cleanup the whole buffer and return error */
   free(in->buffer);
   free(in->buffer);
   free(in);
   free(in);
+  *inp = NULL;
   return CURLE_OUT_OF_MEMORY;
   return CURLE_OUT_OF_MEMORY;
 }
 }
 
 
 /*
 /*
- * add_buffer() appends a memory chunk to the existing buffer
+ * Curl_add_buffer() appends a memory chunk to the existing buffer
  */
  */
-CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size)
+CURLcode Curl_add_buffer(Curl_send_buffer **inp, const void *inptr,
+                         size_t size)
 {
 {
   char *new_rb;
   char *new_rb;
+  Curl_send_buffer *in = *inp;
 
 
   if(~size < in->size_used) {
   if(~size < in->size_used) {
     /* If resulting used size of send buffer would wrap size_t, cleanup
     /* If resulting used size of send buffer would wrap size_t, cleanup
@@ -1297,6 +1303,7 @@ CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size)
        size will fit into a single allocatable memory chunk */
        size will fit into a single allocatable memory chunk */
     Curl_safefree(in->buffer);
     Curl_safefree(in->buffer);
     free(in);
     free(in);
+    *inp = NULL;
     return CURLE_OUT_OF_MEMORY;
     return CURLE_OUT_OF_MEMORY;
   }
   }
 
 
@@ -1323,6 +1330,7 @@ CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size)
     if(!new_rb) {
     if(!new_rb) {
       /* If we failed, we cleanup the whole buffer and return error */
       /* If we failed, we cleanup the whole buffer and return error */
       free(in);
       free(in);
+      *inp = NULL;
       return CURLE_OUT_OF_MEMORY;
       return CURLE_OUT_OF_MEMORY;
     }
     }
 
 
@@ -1484,11 +1492,11 @@ static CURLcode add_haproxy_protocol_header(struct connectdata *conn)
   if(!req_buffer)
   if(!req_buffer)
     return CURLE_OUT_OF_MEMORY;
     return CURLE_OUT_OF_MEMORY;
 
 
-  result = Curl_add_bufferf(req_buffer, proxy_header);
+  result = Curl_add_bufferf(&req_buffer, proxy_header);
   if(result)
   if(result)
     return result;
     return result;
 
 
-  result = Curl_add_buffer_send(req_buffer,
+  result = Curl_add_buffer_send(&req_buffer,
                                 conn,
                                 conn,
                                 &conn->data->info.request_size,
                                 &conn->data->info.request_size,
                                 0,
                                 0,
@@ -1561,8 +1569,7 @@ CURLcode Curl_http_done(struct connectdata *conn,
     return CURLE_OK;
     return CURLE_OK;
 
 
   if(http->send_buffer) {
   if(http->send_buffer) {
-    Curl_add_buffer_free(http->send_buffer);
-    http->send_buffer = NULL; /* clear the pointer */
+    Curl_add_buffer_free(&http->send_buffer);
   }
   }
 
 
   Curl_http2_done(conn, premature);
   Curl_http2_done(conn, premature);
@@ -1653,8 +1660,8 @@ static CURLcode expect100(struct Curl_easy *data,
         Curl_compareheader(ptr, "Expect:", "100-continue");
         Curl_compareheader(ptr, "Expect:", "100-continue");
     }
     }
     else {
     else {
-      result = Curl_add_bufferf(req_buffer,
-                         "Expect: 100-continue\r\n");
+      result = Curl_add_bufferf(&req_buffer,
+                                "Expect: 100-continue\r\n");
       if(!result)
       if(!result)
         data->state.expect100header = TRUE;
         data->state.expect100header = TRUE;
     }
     }
@@ -1785,7 +1792,7 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
                    !strcasecompare(data->state.first_host, conn->host.name)))
                    !strcasecompare(data->state.first_host, conn->host.name)))
             ;
             ;
           else {
           else {
-            result = Curl_add_bufferf(req_buffer, "%s\r\n", headers->data);
+            result = Curl_add_bufferf(&req_buffer, "%s\r\n", headers->data);
           }
           }
           if(semicolonp)
           if(semicolonp)
             *semicolonp = ';'; /* put back the semicolon */
             *semicolonp = ';'; /* put back the semicolon */
@@ -1854,7 +1861,7 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data,
            tm->tm_min,
            tm->tm_min,
            tm->tm_sec);
            tm->tm_sec);
 
 
-  result = Curl_add_buffer(req_buffer, datestr, strlen(datestr));
+  result = Curl_add_buffer(&req_buffer, datestr, strlen(datestr));
 
 
   return result;
   return result;
 }
 }
@@ -1869,7 +1876,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
   struct Curl_easy *data = conn->data;
   struct Curl_easy *data = conn->data;
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
   struct HTTP *http;
   struct HTTP *http;
-  const char *ppath = data->state.path;
+  const char *path = data->state.up.path;
+  const char *query = data->state.up.query;
   bool paste_ftp_userpwd = FALSE;
   bool paste_ftp_userpwd = FALSE;
   char ftp_typecode[sizeof("/;type=?")] = "";
   char ftp_typecode[sizeof("/;type=?")] = "";
   const char *host = conn->host.name;
   const char *host = conn->host.name;
@@ -1987,7 +1995,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
   }
   }
 
 
   /* setup the authentication headers */
   /* setup the authentication headers */
-  result = Curl_http_output_auth(conn, request, ppath, FALSE);
+  result = Curl_http_output_auth(conn, request, path, FALSE);
   if(result)
   if(result)
     return result;
     return result;
 
 
@@ -2215,47 +2223,59 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
     /* The path sent to the proxy is in fact the entire URL. But if the remote
     /* The path sent to the proxy is in fact the entire URL. But if the remote
        host is a IDN-name, we must make sure that the request we produce only
        host is a IDN-name, we must make sure that the request we produce only
        uses the encoded host name! */
        uses the encoded host name! */
+
+    /* and no fragment part */
+    CURLUcode uc;
+    char *url;
+    CURLU *h = curl_url_dup(data->state.uh);
+    if(!h)
+      return CURLE_OUT_OF_MEMORY;
+
     if(conn->host.dispname != conn->host.name) {
     if(conn->host.dispname != conn->host.name) {
-      char *url = data->change.url;
-      ptr = strstr(url, conn->host.dispname);
-      if(ptr) {
-        /* This is where the display name starts in the URL, now replace this
-           part with the encoded name. TODO: This method of replacing the host
-           name is rather crude as I believe there's a slight risk that the
-           user has entered a user name or password that contain the host name
-           string. */
-        size_t currlen = strlen(conn->host.dispname);
-        size_t newlen = strlen(conn->host.name);
-        size_t urllen = strlen(url);
-
-        char *newurl;
-
-        newurl = malloc(urllen + newlen - currlen + 1);
-        if(newurl) {
-          /* copy the part before the host name */
-          memcpy(newurl, url, ptr - url);
-          /* append the new host name instead of the old */
-          memcpy(newurl + (ptr - url), conn->host.name, newlen);
-          /* append the piece after the host name */
-          memcpy(newurl + newlen + (ptr - url),
-                 ptr + currlen, /* copy the trailing zero byte too */
-                 urllen - (ptr-url) - currlen + 1);
-          if(data->change.url_alloc) {
-            Curl_safefree(data->change.url);
-            data->change.url_alloc = FALSE;
-          }
-          data->change.url = newurl;
-          data->change.url_alloc = TRUE;
-        }
-        else
-          return CURLE_OUT_OF_MEMORY;
+      uc = curl_url_set(h, CURLUPART_HOST, conn->host.name, 0);
+      if(uc) {
+        curl_url_cleanup(h);
+        return CURLE_OUT_OF_MEMORY;
+      }
+    }
+    uc = curl_url_set(h, CURLUPART_FRAGMENT, NULL, 0);
+    if(uc) {
+      curl_url_cleanup(h);
+      return CURLE_OUT_OF_MEMORY;
+    }
+
+    if(strcasecompare("http", data->state.up.scheme)) {
+      /* when getting HTTP, we don't want the userinfo the URL */
+      uc = curl_url_set(h, CURLUPART_USER, NULL, 0);
+      if(uc) {
+        curl_url_cleanup(h);
+        return CURLE_OUT_OF_MEMORY;
+      }
+      uc = curl_url_set(h, CURLUPART_PASSWORD, NULL, 0);
+      if(uc) {
+        curl_url_cleanup(h);
+        return CURLE_OUT_OF_MEMORY;
       }
       }
     }
     }
-    ppath = data->change.url;
-    if(checkprefix("ftp://", ppath)) {
+    /* now extract the new version of the URL */
+    uc = curl_url_get(h, CURLUPART_URL, &url, 0);
+    if(uc) {
+      curl_url_cleanup(h);
+      return CURLE_OUT_OF_MEMORY;
+    }
+
+    if(data->change.url_alloc)
+      free(data->change.url);
+
+    data->change.url = url;
+    data->change.url_alloc = TRUE;
+
+    curl_url_cleanup(h);
+
+    if(strcasecompare("ftp", data->state.up.scheme)) {
       if(data->set.proxy_transfer_mode) {
       if(data->set.proxy_transfer_mode) {
         /* when doing ftp, append ;type=<a|i> if not present */
         /* when doing ftp, append ;type=<a|i> if not present */
-        char *type = strstr(ppath, ";type=");
+        char *type = strstr(path, ";type=");
         if(type && type[6] && type[7] == 0) {
         if(type && type[6] && type[7] == 0) {
           switch(Curl_raw_toupper(type[6])) {
           switch(Curl_raw_toupper(type[6])) {
           case 'A':
           case 'A':
@@ -2270,7 +2290,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
           char *p = ftp_typecode;
           char *p = ftp_typecode;
           /* avoid sending invalid URLs like ftp://example.com;type=i if the
           /* avoid sending invalid URLs like ftp://example.com;type=i if the
            * user specified ftp://example.com without the slash */
            * user specified ftp://example.com without the slash */
-          if(!*data->state.path && ppath[strlen(ppath) - 1] != '/') {
+          if(!*data->state.up.path && path[strlen(path) - 1] != '/') {
             *p++ = '/';
             *p++ = '/';
           }
           }
           snprintf(p, sizeof(ftp_typecode) - 1, ";type=%c",
           snprintf(p, sizeof(ftp_typecode) - 1, ";type=%c",
@@ -2419,25 +2439,36 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
 
 
   /* add the main request stuff */
   /* add the main request stuff */
   /* GET/HEAD/POST/PUT */
   /* GET/HEAD/POST/PUT */
-  result = Curl_add_bufferf(req_buffer, "%s ", request);
+  result = Curl_add_bufferf(&req_buffer, "%s ", request);
   if(result)
   if(result)
     return result;
     return result;
 
 
-  if(data->set.str[STRING_TARGET])
-    ppath = data->set.str[STRING_TARGET];
+  if(data->set.str[STRING_TARGET]) {
+    path = data->set.str[STRING_TARGET];
+    query = NULL;
+  }
 
 
   /* url */
   /* url */
-  if(paste_ftp_userpwd)
-    result = Curl_add_bufferf(req_buffer, "ftp://%s:%s@%s",
+  if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
+    char *url = data->change.url;
+    result = Curl_add_buffer(&req_buffer, url, strlen(url));
+  }
+  else if(paste_ftp_userpwd)
+    result = Curl_add_bufferf(&req_buffer, "ftp://%s:%s@%s",
                               conn->user, conn->passwd,
                               conn->user, conn->passwd,
-                              ppath + sizeof("ftp://") - 1);
-  else
-    result = Curl_add_buffer(req_buffer, ppath, strlen(ppath));
+                              path + sizeof("ftp://") - 1);
+  else {
+    result = Curl_add_buffer(&req_buffer, path, strlen(path));
+    if(result)
+      return result;
+    if(query)
+      result = Curl_add_bufferf(&req_buffer, "?%s", query);
+  }
   if(result)
   if(result)
     return result;
     return result;
 
 
   result =
   result =
-    Curl_add_bufferf(req_buffer,
+    Curl_add_bufferf(&req_buffer,
                      "%s" /* ftp typecode (;type=x) */
                      "%s" /* ftp typecode (;type=x) */
                      " HTTP/%s\r\n" /* HTTP version */
                      " HTTP/%s\r\n" /* HTTP version */
                      "%s" /* host */
                      "%s" /* host */
@@ -2507,7 +2538,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
       co = Curl_cookie_getlist(data->cookies,
       co = Curl_cookie_getlist(data->cookies,
                                conn->allocptr.cookiehost?
                                conn->allocptr.cookiehost?
                                conn->allocptr.cookiehost:host,
                                conn->allocptr.cookiehost:host,
-                               data->state.path,
+                               data->state.up.path,
                                (conn->handler->protocol&CURLPROTO_HTTPS)?
                                (conn->handler->protocol&CURLPROTO_HTTPS)?
                                TRUE:FALSE);
                                TRUE:FALSE);
       Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
       Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
@@ -2518,11 +2549,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
       while(co) {
       while(co) {
         if(co->value) {
         if(co->value) {
           if(0 == count) {
           if(0 == count) {
-            result = Curl_add_bufferf(req_buffer, "Cookie: ");
+            result = Curl_add_bufferf(&req_buffer, "Cookie: ");
             if(result)
             if(result)
               break;
               break;
           }
           }
-          result = Curl_add_bufferf(req_buffer,
+          result = Curl_add_bufferf(&req_buffer,
                                     "%s%s=%s", count?"; ":"",
                                     "%s%s=%s", count?"; ":"",
                                     co->name, co->value);
                                     co->name, co->value);
           if(result)
           if(result)
@@ -2535,15 +2566,15 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
     }
     }
     if(addcookies && !result) {
     if(addcookies && !result) {
       if(!count)
       if(!count)
-        result = Curl_add_bufferf(req_buffer, "Cookie: ");
+        result = Curl_add_bufferf(&req_buffer, "Cookie: ");
       if(!result) {
       if(!result) {
-        result = Curl_add_bufferf(req_buffer, "%s%s", count?"; ":"",
+        result = Curl_add_bufferf(&req_buffer, "%s%s", count?"; ":"",
                                   addcookies);
                                   addcookies);
         count++;
         count++;
       }
       }
     }
     }
     if(count && !result)
     if(count && !result)
-      result = Curl_add_buffer(req_buffer, "\r\n", 2);
+      result = Curl_add_buffer(&req_buffer, "\r\n", 2);
 
 
     if(result)
     if(result)
       return result;
       return result;
@@ -2577,7 +2608,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
     if((postsize != -1) && !data->req.upload_chunky &&
     if((postsize != -1) && !data->req.upload_chunky &&
        (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length"))) {
        (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length"))) {
       /* only add Content-Length if not uploading chunked */
       /* only add Content-Length if not uploading chunked */
-      result = Curl_add_bufferf(req_buffer,
+      result = Curl_add_bufferf(&req_buffer,
                                 "Content-Length: %" CURL_FORMAT_CURL_OFF_T
                                 "Content-Length: %" CURL_FORMAT_CURL_OFF_T
                                 "\r\n", postsize);
                                 "\r\n", postsize);
       if(result)
       if(result)
@@ -2590,7 +2621,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
         return result;
         return result;
     }
     }
 
 
-    result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers */
+    result = Curl_add_buffer(&req_buffer, "\r\n", 2); /* end of headers */
     if(result)
     if(result)
       return result;
       return result;
 
 
@@ -2598,7 +2629,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
     Curl_pgrsSetUploadSize(data, postsize);
     Curl_pgrsSetUploadSize(data, postsize);
 
 
     /* this sends the buffer and frees all the buffer resources */
     /* this sends the buffer and frees all the buffer resources */
-    result = Curl_add_buffer_send(req_buffer, conn,
+    result = Curl_add_buffer_send(&req_buffer, conn,
                                   &data->info.request_size, 0, FIRSTSOCKET);
                                   &data->info.request_size, 0, FIRSTSOCKET);
     if(result)
     if(result)
       failf(data, "Failed sending PUT request");
       failf(data, "Failed sending PUT request");
@@ -2616,11 +2647,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
     /* This is form posting using mime data. */
     /* This is form posting using mime data. */
     if(conn->bits.authneg) {
     if(conn->bits.authneg) {
       /* nothing to post! */
       /* nothing to post! */
-      result = Curl_add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
+      result = Curl_add_bufferf(&req_buffer, "Content-Length: 0\r\n\r\n");
       if(result)
       if(result)
         return result;
         return result;
 
 
-      result = Curl_add_buffer_send(req_buffer, conn,
+      result = Curl_add_buffer_send(&req_buffer, conn,
                                     &data->info.request_size, 0, FIRSTSOCKET);
                                     &data->info.request_size, 0, FIRSTSOCKET);
       if(result)
       if(result)
         failf(data, "Failed sending POST request");
         failf(data, "Failed sending POST request");
@@ -2640,7 +2671,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
        (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length"))) {
        (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length"))) {
       /* we allow replacing this header if not during auth negotiation,
       /* we allow replacing this header if not during auth negotiation,
          although it isn't very wise to actually set your own */
          although it isn't very wise to actually set your own */
-      result = Curl_add_bufferf(req_buffer,
+      result = Curl_add_bufferf(&req_buffer,
                                 "Content-Length: %" CURL_FORMAT_CURL_OFF_T
                                 "Content-Length: %" CURL_FORMAT_CURL_OFF_T
                                 "\r\n", postsize);
                                 "\r\n", postsize);
       if(result)
       if(result)
@@ -2652,7 +2683,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
       struct curl_slist *hdr;
       struct curl_slist *hdr;
 
 
       for(hdr = http->sendit->curlheaders; hdr; hdr = hdr->next) {
       for(hdr = http->sendit->curlheaders; hdr; hdr = hdr->next) {
-        result = Curl_add_bufferf(req_buffer, "%s\r\n", hdr->data);
+        result = Curl_add_bufferf(&req_buffer, "%s\r\n", hdr->data);
         if(result)
         if(result)
           return result;
           return result;
       }
       }
@@ -2676,7 +2707,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
       data->state.expect100header = FALSE;
       data->state.expect100header = FALSE;
 
 
     /* make the request end in a true CRLF */
     /* make the request end in a true CRLF */
-    result = Curl_add_buffer(req_buffer, "\r\n", 2);
+    result = Curl_add_buffer(&req_buffer, "\r\n", 2);
     if(result)
     if(result)
       return result;
       return result;
 
 
@@ -2689,7 +2720,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
     http->sending = HTTPSEND_BODY;
     http->sending = HTTPSEND_BODY;
 
 
     /* this sends the buffer and frees all the buffer resources */
     /* this sends the buffer and frees all the buffer resources */
-    result = Curl_add_buffer_send(req_buffer, conn,
+    result = Curl_add_buffer_send(&req_buffer, conn,
                                   &data->info.request_size, 0, FIRSTSOCKET);
                                   &data->info.request_size, 0, FIRSTSOCKET);
     if(result)
     if(result)
       failf(data, "Failed sending POST request");
       failf(data, "Failed sending POST request");
@@ -2719,7 +2750,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
        (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length"))) {
        (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length"))) {
       /* we allow replacing this header if not during auth negotiation,
       /* we allow replacing this header if not during auth negotiation,
          although it isn't very wise to actually set your own */
          although it isn't very wise to actually set your own */
-      result = Curl_add_bufferf(req_buffer,
+      result = Curl_add_bufferf(&req_buffer,
                                 "Content-Length: %" CURL_FORMAT_CURL_OFF_T
                                 "Content-Length: %" CURL_FORMAT_CURL_OFF_T
                                 "\r\n", postsize);
                                 "\r\n", postsize);
       if(result)
       if(result)
@@ -2727,7 +2758,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
     }
     }
 
 
     if(!Curl_checkheaders(conn, "Content-Type")) {
     if(!Curl_checkheaders(conn, "Content-Type")) {
-      result = Curl_add_bufferf(req_buffer,
+      result = Curl_add_bufferf(&req_buffer,
                                 "Content-Type: application/"
                                 "Content-Type: application/"
                                 "x-www-form-urlencoded\r\n");
                                 "x-www-form-urlencoded\r\n");
       if(result)
       if(result)
@@ -2765,31 +2796,31 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
            is no magic limit but only set to prevent really huge POSTs to
            is no magic limit but only set to prevent really huge POSTs to
            get the data duplicated with malloc() and family. */
            get the data duplicated with malloc() and family. */
 
 
-        result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
+        result = Curl_add_buffer(&req_buffer, "\r\n", 2); /* end of headers! */
         if(result)
         if(result)
           return result;
           return result;
 
 
         if(!data->req.upload_chunky) {
         if(!data->req.upload_chunky) {
           /* We're not sending it 'chunked', append it to the request
           /* We're not sending it 'chunked', append it to the request
              already now to reduce the number if send() calls */
              already now to reduce the number if send() calls */
-          result = Curl_add_buffer(req_buffer, data->set.postfields,
+          result = Curl_add_buffer(&req_buffer, data->set.postfields,
                                    (size_t)postsize);
                                    (size_t)postsize);
           included_body = postsize;
           included_body = postsize;
         }
         }
         else {
         else {
           if(postsize) {
           if(postsize) {
             /* Append the POST data chunky-style */
             /* Append the POST data chunky-style */
-            result = Curl_add_bufferf(req_buffer, "%x\r\n", (int)postsize);
+            result = Curl_add_bufferf(&req_buffer, "%x\r\n", (int)postsize);
             if(!result) {
             if(!result) {
-              result = Curl_add_buffer(req_buffer, data->set.postfields,
+              result = Curl_add_buffer(&req_buffer, data->set.postfields,
                                        (size_t)postsize);
                                        (size_t)postsize);
               if(!result)
               if(!result)
-                result = Curl_add_buffer(req_buffer, "\r\n", 2);
+                result = Curl_add_buffer(&req_buffer, "\r\n", 2);
               included_body = postsize + 2;
               included_body = postsize + 2;
             }
             }
           }
           }
           if(!result)
           if(!result)
-            result = Curl_add_buffer(req_buffer, "\x30\x0d\x0a\x0d\x0a", 5);
+            result = Curl_add_buffer(&req_buffer, "\x30\x0d\x0a\x0d\x0a", 5);
           /* 0  CR  LF  CR  LF */
           /* 0  CR  LF  CR  LF */
           included_body += 5;
           included_body += 5;
         }
         }
@@ -2811,20 +2842,20 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
         /* set the upload size to the progress meter */
         /* set the upload size to the progress meter */
         Curl_pgrsSetUploadSize(data, http->postsize);
         Curl_pgrsSetUploadSize(data, http->postsize);
 
 
-        result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
+        result = Curl_add_buffer(&req_buffer, "\r\n", 2); /* end of headers! */
         if(result)
         if(result)
           return result;
           return result;
       }
       }
     }
     }
     else {
     else {
-      result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
+      result = Curl_add_buffer(&req_buffer, "\r\n", 2); /* end of headers! */
       if(result)
       if(result)
         return result;
         return result;
 
 
       if(data->req.upload_chunky && conn->bits.authneg) {
       if(data->req.upload_chunky && conn->bits.authneg) {
         /* Chunky upload is selected and we're negotiating auth still, send
         /* Chunky upload is selected and we're negotiating auth still, send
            end-of-data only */
            end-of-data only */
-        result = Curl_add_buffer(req_buffer,
+        result = Curl_add_buffer(&req_buffer,
                                  "\x30\x0d\x0a\x0d\x0a", 5);
                                  "\x30\x0d\x0a\x0d\x0a", 5);
         /* 0  CR  LF  CR  LF */
         /* 0  CR  LF  CR  LF */
         if(result)
         if(result)
@@ -2845,7 +2876,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
       }
       }
     }
     }
     /* issue the request */
     /* issue the request */
-    result = Curl_add_buffer_send(req_buffer, conn, &data->info.request_size,
+    result = Curl_add_buffer_send(&req_buffer, conn, &data->info.request_size,
                                   (size_t)included_body, FIRSTSOCKET);
                                   (size_t)included_body, FIRSTSOCKET);
 
 
     if(result)
     if(result)
@@ -2857,12 +2888,12 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
     break;
     break;
 
 
   default:
   default:
-    result = Curl_add_buffer(req_buffer, "\r\n", 2);
+    result = Curl_add_buffer(&req_buffer, "\r\n", 2);
     if(result)
     if(result)
       return result;
       return result;
 
 
     /* issue the request */
     /* issue the request */
-    result = Curl_add_buffer_send(req_buffer, conn,
+    result = Curl_add_buffer_send(&req_buffer, conn,
                                   &data->info.request_size, 0, FIRSTSOCKET);
                                   &data->info.request_size, 0, FIRSTSOCKET);
 
 
     if(result)
     if(result)
@@ -3828,7 +3859,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
                          here, or else use real peer host name. */
                          here, or else use real peer host name. */
                       conn->allocptr.cookiehost?
                       conn->allocptr.cookiehost?
                       conn->allocptr.cookiehost:conn->host.name,
                       conn->allocptr.cookiehost:conn->host.name,
-                      data->state.path);
+                      data->state.up.path);
       Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
       Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
     }
     }
 #endif
 #endif

+ 11 - 8
lib/http.h

@@ -58,10 +58,12 @@ struct Curl_send_buffer {
 typedef struct Curl_send_buffer Curl_send_buffer;
 typedef struct Curl_send_buffer Curl_send_buffer;
 
 
 Curl_send_buffer *Curl_add_buffer_init(void);
 Curl_send_buffer *Curl_add_buffer_init(void);
-void Curl_add_buffer_free(Curl_send_buffer *buff);
-CURLcode Curl_add_bufferf(Curl_send_buffer *in, const char *fmt, ...);
-CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size);
-CURLcode Curl_add_buffer_send(Curl_send_buffer *in,
+void Curl_add_buffer_free(Curl_send_buffer **inp);
+CURLcode Curl_add_bufferf(Curl_send_buffer **inp, const char *fmt, ...)
+  WARN_UNUSED_RESULT;
+CURLcode Curl_add_buffer(Curl_send_buffer **inp, const void *inptr,
+                         size_t size) WARN_UNUSED_RESULT;
+CURLcode Curl_add_buffer_send(Curl_send_buffer **inp,
                               struct connectdata *conn,
                               struct connectdata *conn,
                               long *bytes_written,
                               long *bytes_written,
                               size_t included_body_bytes,
                               size_t included_body_bytes,
@@ -154,9 +156,11 @@ struct HTTP {
     HTTPSEND_LAST     /* never use this */
     HTTPSEND_LAST     /* never use this */
   } sending;
   } sending;
 
 
-  void *send_buffer; /* used if the request couldn't be sent in one chunk,
-                        points to an allocated send_buffer struct */
-
+#ifndef CURL_DISABLE_HTTP
+  Curl_send_buffer *send_buffer; /* used if the request couldn't be sent in
+                                    one chunk, points to an allocated
+                                    send_buffer struct */
+#endif
 #ifdef USE_NGHTTP2
 #ifdef USE_NGHTTP2
   /*********** for HTTP/2 we store stream-local data here *************/
   /*********** for HTTP/2 we store stream-local data here *************/
   int32_t stream_id; /* stream we are interested in */
   int32_t stream_id; /* stream we are interested in */
@@ -253,4 +257,3 @@ Curl_http_output_auth(struct connectdata *conn,
                                             up the proxy tunnel */
                                             up the proxy tunnel */
 
 
 #endif /* HEADER_CURL_HTTP_H */
 #endif /* HEADER_CURL_HTTP_H */
-

+ 101 - 34
lib/http2.c

@@ -141,10 +141,8 @@ static int http2_getsock(struct connectdata *conn,
 static void http2_stream_free(struct HTTP *http)
 static void http2_stream_free(struct HTTP *http)
 {
 {
   if(http) {
   if(http) {
-    Curl_add_buffer_free(http->header_recvbuf);
-    http->header_recvbuf = NULL; /* clear the pointer */
-    Curl_add_buffer_free(http->trailer_recvbuf);
-    http->trailer_recvbuf = NULL; /* clear the pointer */
+    Curl_add_buffer_free(&http->header_recvbuf);
+    Curl_add_buffer_free(&http->trailer_recvbuf);
     for(; http->push_headers_used > 0; --http->push_headers_used) {
     for(; http->push_headers_used > 0; --http->push_headers_used) {
       free(http->push_headers[http->push_headers_used - 1]);
       free(http->push_headers[http->push_headers_used - 1]);
     }
     }
@@ -203,7 +201,7 @@ static bool http2_connisdead(struct connectdata *conn)
     dead = !Curl_connalive(conn);
     dead = !Curl_connalive(conn);
     if(!dead) {
     if(!dead) {
       /* This happens before we've sent off a request and the connection is
       /* This happens before we've sent off a request and the connection is
-         not in use by any other thransfer, there shouldn't be any data here,
+         not in use by any other transfer, there shouldn't be any data here,
          only "protocol frames" */
          only "protocol frames" */
       CURLcode result;
       CURLcode result;
       struct http_conn *httpc = &conn->proto.httpc;
       struct http_conn *httpc = &conn->proto.httpc;
@@ -233,12 +231,43 @@ static unsigned int http2_conncheck(struct connectdata *check,
                                     unsigned int checks_to_perform)
                                     unsigned int checks_to_perform)
 {
 {
   unsigned int ret_val = CONNRESULT_NONE;
   unsigned int ret_val = CONNRESULT_NONE;
+  struct http_conn *c = &check->proto.httpc;
+  int rc;
+  bool send_frames = false;
 
 
   if(checks_to_perform & CONNCHECK_ISDEAD) {
   if(checks_to_perform & CONNCHECK_ISDEAD) {
     if(http2_connisdead(check))
     if(http2_connisdead(check))
       ret_val |= CONNRESULT_DEAD;
       ret_val |= CONNRESULT_DEAD;
   }
   }
 
 
+  if(checks_to_perform & CONNCHECK_KEEPALIVE) {
+    struct curltime now = Curl_now();
+    time_t elapsed = Curl_timediff(now, check->keepalive);
+
+    if(elapsed > check->upkeep_interval_ms) {
+      /* Perform an HTTP/2 PING */
+      rc = nghttp2_submit_ping(c->h2, 0, ZERO_NULL);
+      if(!rc) {
+        /* Successfully added a PING frame to the session. Need to flag this
+           so the frame is sent. */
+        send_frames = true;
+      }
+      else {
+       failf(check->data, "nghttp2_submit_ping() failed: %s(%d)",
+             nghttp2_strerror(rc), rc);
+      }
+
+      check->keepalive = now;
+    }
+  }
+
+  if(send_frames) {
+    rc = nghttp2_session_send(c->h2);
+    if(rc)
+      failf(check->data, "nghttp2_session_send() failed: %s(%d)",
+            nghttp2_strerror(rc), rc);
+  }
+
   return ret_val;
   return ret_val;
 }
 }
 
 
@@ -599,6 +628,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
   int rv;
   int rv;
   size_t left, ncopy;
   size_t left, ncopy;
   int32_t stream_id = frame->hd.stream_id;
   int32_t stream_id = frame->hd.stream_id;
+  CURLcode result;
 
 
   if(!stream_id) {
   if(!stream_id) {
     /* stream ID zero is for connection-oriented stuff */
     /* stream ID zero is for connection-oriented stuff */
@@ -674,7 +704,9 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
       stream->status_code = -1;
       stream->status_code = -1;
     }
     }
 
 
-    Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
+    result = Curl_add_buffer(&stream->header_recvbuf, "\r\n", 2);
+    if(result)
+      return NGHTTP2_ERR_CALLBACK_FAILURE;
 
 
     left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
     left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
     ncopy = CURLMIN(stream->len, left);
     ncopy = CURLMIN(stream->len, left);
@@ -898,6 +930,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
   struct Curl_easy *data_s;
   struct Curl_easy *data_s;
   int32_t stream_id = frame->hd.stream_id;
   int32_t stream_id = frame->hd.stream_id;
   struct connectdata *conn = (struct connectdata *)userp;
   struct connectdata *conn = (struct connectdata *)userp;
+  CURLcode result;
   (void)flags;
   (void)flags;
 
 
   DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
   DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
@@ -924,6 +957,8 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
       stream->push_headers_alloc = 10;
       stream->push_headers_alloc = 10;
       stream->push_headers = malloc(stream->push_headers_alloc *
       stream->push_headers = malloc(stream->push_headers_alloc *
                                     sizeof(char *));
                                     sizeof(char *));
+      if(!stream->push_headers)
+        return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE;
       stream->push_headers_used = 0;
       stream->push_headers_used = 0;
     }
     }
     else if(stream->push_headers_used ==
     else if(stream->push_headers_used ==
@@ -952,11 +987,21 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
     H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen,
     H2BUGF(infof(data_s, "h2 trailer: %.*s: %.*s\n", namelen, name, valuelen,
                  value));
                  value));
 
 
-    Curl_add_buffer(stream->trailer_recvbuf, &n, sizeof(n));
-    Curl_add_buffer(stream->trailer_recvbuf, name, namelen);
-    Curl_add_buffer(stream->trailer_recvbuf, ": ", 2);
-    Curl_add_buffer(stream->trailer_recvbuf, value, valuelen);
-    Curl_add_buffer(stream->trailer_recvbuf, "\r\n\0", 3);
+    result = Curl_add_buffer(&stream->trailer_recvbuf, &n, sizeof(n));
+    if(result)
+      return NGHTTP2_ERR_CALLBACK_FAILURE;
+    result = Curl_add_buffer(&stream->trailer_recvbuf, name, namelen);
+    if(result)
+      return NGHTTP2_ERR_CALLBACK_FAILURE;
+    result = Curl_add_buffer(&stream->trailer_recvbuf, ": ", 2);
+    if(result)
+      return NGHTTP2_ERR_CALLBACK_FAILURE;
+    result = Curl_add_buffer(&stream->trailer_recvbuf, value, valuelen);
+    if(result)
+      return NGHTTP2_ERR_CALLBACK_FAILURE;
+    result = Curl_add_buffer(&stream->trailer_recvbuf, "\r\n\0", 3);
+    if(result)
+      return NGHTTP2_ERR_CALLBACK_FAILURE;
 
 
     return 0;
     return 0;
   }
   }
@@ -969,10 +1014,16 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
     stream->status_code = decode_status_code(value, valuelen);
     stream->status_code = decode_status_code(value, valuelen);
     DEBUGASSERT(stream->status_code != -1);
     DEBUGASSERT(stream->status_code != -1);
 
 
-    Curl_add_buffer(stream->header_recvbuf, "HTTP/2 ", 7);
-    Curl_add_buffer(stream->header_recvbuf, value, valuelen);
+    result = Curl_add_buffer(&stream->header_recvbuf, "HTTP/2 ", 7);
+    if(result)
+      return NGHTTP2_ERR_CALLBACK_FAILURE;
+    result = Curl_add_buffer(&stream->header_recvbuf, value, valuelen);
+    if(result)
+      return NGHTTP2_ERR_CALLBACK_FAILURE;
     /* the space character after the status code is mandatory */
     /* the space character after the status code is mandatory */
-    Curl_add_buffer(stream->header_recvbuf, " \r\n", 3);
+    result = Curl_add_buffer(&stream->header_recvbuf, " \r\n", 3);
+    if(result)
+      return NGHTTP2_ERR_CALLBACK_FAILURE;
     /* if we receive data for another handle, wake that up */
     /* if we receive data for another handle, wake that up */
     if(conn->data != data_s)
     if(conn->data != data_s)
       Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
       Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
@@ -985,10 +1036,18 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
   /* nghttp2 guarantees that namelen > 0, and :status was already
   /* nghttp2 guarantees that namelen > 0, and :status was already
      received, and this is not pseudo-header field . */
      received, and this is not pseudo-header field . */
   /* convert to a HTTP1-style header */
   /* convert to a HTTP1-style header */
-  Curl_add_buffer(stream->header_recvbuf, name, namelen);
-  Curl_add_buffer(stream->header_recvbuf, ": ", 2);
-  Curl_add_buffer(stream->header_recvbuf, value, valuelen);
-  Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);
+  result = Curl_add_buffer(&stream->header_recvbuf, name, namelen);
+  if(result)
+    return NGHTTP2_ERR_CALLBACK_FAILURE;
+  result = Curl_add_buffer(&stream->header_recvbuf, ": ", 2);
+  if(result)
+    return NGHTTP2_ERR_CALLBACK_FAILURE;
+  result = Curl_add_buffer(&stream->header_recvbuf, value, valuelen);
+  if(result)
+    return NGHTTP2_ERR_CALLBACK_FAILURE;
+  result = Curl_add_buffer(&stream->header_recvbuf, "\r\n", 2);
+  if(result)
+    return NGHTTP2_ERR_CALLBACK_FAILURE;
   /* if we receive data for another handle, wake that up */
   /* if we receive data for another handle, wake that up */
   if(conn->data != data_s)
   if(conn->data != data_s)
     Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
     Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
@@ -1049,7 +1108,8 @@ static ssize_t data_source_read_callback(nghttp2_session *session,
   return nread;
   return nread;
 }
 }
 
 
-#ifdef NGHTTP2_HAS_ERROR_CALLBACK
+#if defined(NGHTTP2_HAS_ERROR_CALLBACK) &&      \
+  !defined(CURL_DISABLE_VERBOSE_STRINGS)
 static int error_callback(nghttp2_session *session,
 static int error_callback(nghttp2_session *session,
                           const char *msg,
                           const char *msg,
                           size_t len,
                           size_t len,
@@ -1085,17 +1145,11 @@ void Curl_http2_done(struct connectdata *conn, bool premature)
   struct HTTP *http = data->req.protop;
   struct HTTP *http = data->req.protop;
   struct http_conn *httpc = &conn->proto.httpc;
   struct http_conn *httpc = &conn->proto.httpc;
 
 
-  if(!httpc->h2) /* not HTTP/2 ? */
-    return;
-
-  if(data->state.drain)
-    drained_transfer(data, httpc);
-
+  /* there might be allocated resources done before this got the 'h2' pointer
+     setup */
   if(http->header_recvbuf) {
   if(http->header_recvbuf) {
-    Curl_add_buffer_free(http->header_recvbuf);
-    http->header_recvbuf = NULL; /* clear the pointer */
-    Curl_add_buffer_free(http->trailer_recvbuf);
-    http->trailer_recvbuf = NULL; /* clear the pointer */
+    Curl_add_buffer_free(&http->header_recvbuf);
+    Curl_add_buffer_free(&http->trailer_recvbuf);
     if(http->push_headers) {
     if(http->push_headers) {
       /* if they weren't used and then freed before */
       /* if they weren't used and then freed before */
       for(; http->push_headers_used > 0; --http->push_headers_used) {
       for(; http->push_headers_used > 0; --http->push_headers_used) {
@@ -1106,6 +1160,12 @@ void Curl_http2_done(struct connectdata *conn, bool premature)
     }
     }
   }
   }
 
 
+  if(!httpc->h2) /* not HTTP/2 ? */
+    return;
+
+  if(data->state.drain)
+    drained_transfer(data, httpc);
+
   if(premature) {
   if(premature) {
     /* RST_STREAM */
     /* RST_STREAM */
     if(!nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE,
     if(!nghttp2_submit_rst_stream(httpc->h2, NGHTTP2_FLAG_NONE,
@@ -1167,7 +1227,9 @@ CURLcode Curl_http2_init(struct connectdata *conn)
     /* nghttp2_on_header_callback */
     /* nghttp2_on_header_callback */
     nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
     nghttp2_session_callbacks_set_on_header_callback(callbacks, on_header);
 
 
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
     nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
     nghttp2_session_callbacks_set_error_callback(callbacks, error_callback);
+#endif
 
 
     /* The nghttp2 session is not yet setup, do it */
     /* The nghttp2 session is not yet setup, do it */
     rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
     rc = nghttp2_session_client_new(&conn->proto.httpc.h2, callbacks, conn);
@@ -1204,7 +1266,7 @@ CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
                                          httpc->local_settings_num);
                                          httpc->local_settings_num);
   if(!binlen) {
   if(!binlen) {
     failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
     failf(conn->data, "nghttp2 unexpectedly failed on pack_settings_payload");
-    Curl_add_buffer_free(req);
+    Curl_add_buffer_free(&req);
     return CURLE_FAILED_INIT;
     return CURLE_FAILED_INIT;
   }
   }
   conn->proto.httpc.binlen = binlen;
   conn->proto.httpc.binlen = binlen;
@@ -1212,11 +1274,11 @@ CURLcode Curl_http2_request_upgrade(Curl_send_buffer *req,
   result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen,
   result = Curl_base64url_encode(conn->data, (const char *)binsettings, binlen,
                                  &base64, &blen);
                                  &base64, &blen);
   if(result) {
   if(result) {
-    Curl_add_buffer_free(req);
+    Curl_add_buffer_free(&req);
     return result;
     return result;
   }
   }
 
 
-  result = Curl_add_bufferf(req,
+  result = Curl_add_bufferf(&req,
                             "Connection: Upgrade, HTTP2-Settings\r\n"
                             "Connection: Upgrade, HTTP2-Settings\r\n"
                             "Upgrade: %s\r\n"
                             "Upgrade: %s\r\n"
                             "HTTP2-Settings: %s\r\n",
                             "HTTP2-Settings: %s\r\n",
@@ -2060,8 +2122,11 @@ CURLcode Curl_http2_setup(struct connectdata *conn)
 
 
   stream->stream_id = -1;
   stream->stream_id = -1;
 
 
-  if(!stream->header_recvbuf)
+  if(!stream->header_recvbuf) {
     stream->header_recvbuf = Curl_add_buffer_init();
     stream->header_recvbuf = Curl_add_buffer_init();
+    if(!stream->header_recvbuf)
+      return CURLE_OUT_OF_MEMORY;
+  }
 
 
   if((conn->handler == &Curl_handler_http2_ssl) ||
   if((conn->handler == &Curl_handler_http2_ssl) ||
      (conn->handler == &Curl_handler_http2))
      (conn->handler == &Curl_handler_http2))
@@ -2073,8 +2138,10 @@ CURLcode Curl_http2_setup(struct connectdata *conn)
     conn->handler = &Curl_handler_http2;
     conn->handler = &Curl_handler_http2;
 
 
   result = Curl_http2_init(conn);
   result = Curl_http2_init(conn);
-  if(result)
+  if(result) {
+    Curl_add_buffer_free(&stream->header_recvbuf);
     return result;
     return result;
+  }
 
 
   infof(conn->data, "Using HTTP2, server supports multi-use\n");
   infof(conn->data, "Using HTTP2, server supports multi-use\n");
   stream->upload_left = 0;
   stream->upload_left = 0;

+ 0 - 1
lib/http2.h

@@ -77,4 +77,3 @@ void Curl_http2_cleanup_dependencies(struct Curl_easy *data);
 #endif
 #endif
 
 
 #endif /* HEADER_CURL_HTTP2_H */
 #endif /* HEADER_CURL_HTTP2_H */
-

+ 0 - 1
lib/http_chunks.h

@@ -88,4 +88,3 @@ struct Curl_chunker {
 };
 };
 
 
 #endif /* HEADER_CURL_HTTP_CHUNKS_H */
 #endif /* HEADER_CURL_HTTP_CHUNKS_H */
-

+ 7 - 7
lib/http_proxy.c

@@ -222,7 +222,7 @@ static CURLcode CONNECT(struct connectdata *conn,
 
 
       host_port = aprintf("%s:%d", hostname, remote_port);
       host_port = aprintf("%s:%d", hostname, remote_port);
       if(!host_port) {
       if(!host_port) {
-        Curl_add_buffer_free(req_buffer);
+        Curl_add_buffer_free(&req_buffer);
         return CURLE_OUT_OF_MEMORY;
         return CURLE_OUT_OF_MEMORY;
       }
       }
 
 
@@ -247,7 +247,7 @@ static CURLcode CONNECT(struct connectdata *conn,
           aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
           aprintf("%s%s%s:%d", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
                   remote_port);
                   remote_port);
         if(!hostheader) {
         if(!hostheader) {
-          Curl_add_buffer_free(req_buffer);
+          Curl_add_buffer_free(&req_buffer);
           return CURLE_OUT_OF_MEMORY;
           return CURLE_OUT_OF_MEMORY;
         }
         }
 
 
@@ -255,7 +255,7 @@ static CURLcode CONNECT(struct connectdata *conn,
           host = aprintf("Host: %s\r\n", hostheader);
           host = aprintf("Host: %s\r\n", hostheader);
           if(!host) {
           if(!host) {
             free(hostheader);
             free(hostheader);
-            Curl_add_buffer_free(req_buffer);
+            Curl_add_buffer_free(&req_buffer);
             return CURLE_OUT_OF_MEMORY;
             return CURLE_OUT_OF_MEMORY;
           }
           }
         }
         }
@@ -267,7 +267,7 @@ static CURLcode CONNECT(struct connectdata *conn,
           useragent = conn->allocptr.uagent;
           useragent = conn->allocptr.uagent;
 
 
         result =
         result =
-          Curl_add_bufferf(req_buffer,
+          Curl_add_bufferf(&req_buffer,
                            "CONNECT %s HTTP/%s\r\n"
                            "CONNECT %s HTTP/%s\r\n"
                            "%s"  /* Host: */
                            "%s"  /* Host: */
                            "%s"  /* Proxy-Authorization */
                            "%s"  /* Proxy-Authorization */
@@ -290,13 +290,13 @@ static CURLcode CONNECT(struct connectdata *conn,
 
 
         if(!result)
         if(!result)
           /* CRLF terminate the request */
           /* CRLF terminate the request */
-          result = Curl_add_bufferf(req_buffer, "\r\n");
+          result = Curl_add_bufferf(&req_buffer, "\r\n");
 
 
         if(!result) {
         if(!result) {
           /* Send the connect request to the proxy */
           /* Send the connect request to the proxy */
           /* BLOCKING */
           /* BLOCKING */
           result =
           result =
-            Curl_add_buffer_send(req_buffer, conn,
+            Curl_add_buffer_send(&req_buffer, conn,
                                  &data->info.request_size, 0, sockindex);
                                  &data->info.request_size, 0, sockindex);
         }
         }
         req_buffer = NULL;
         req_buffer = NULL;
@@ -304,7 +304,7 @@ static CURLcode CONNECT(struct connectdata *conn,
           failf(data, "Failed sending CONNECT to proxy");
           failf(data, "Failed sending CONNECT to proxy");
       }
       }
 
 
-      Curl_add_buffer_free(req_buffer);
+      Curl_add_buffer_free(&req_buffer);
       if(result)
       if(result)
         return result;
         return result;
 
 

+ 48 - 36
lib/imap.c

@@ -159,7 +159,8 @@ const struct Curl_handler Curl_handler_imaps = {
   ZERO_NULL,                        /* connection_check */
   ZERO_NULL,                        /* connection_check */
   PORT_IMAPS,                       /* defport */
   PORT_IMAPS,                       /* defport */
   CURLPROTO_IMAPS,                  /* protocol */
   CURLPROTO_IMAPS,                  /* protocol */
-  PROTOPT_CLOSEACTION | PROTOPT_SSL /* flags */
+  PROTOPT_CLOSEACTION | PROTOPT_SSL | /* flags */
+  PROTOPT_URLOPTIONS
 };
 };
 #endif
 #endif
 
 
@@ -421,7 +422,6 @@ static CURLcode imap_perform_capability(struct connectdata *conn)
 {
 {
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
   struct imap_conn *imapc = &conn->proto.imapc;
   struct imap_conn *imapc = &conn->proto.imapc;
-
   imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
   imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
   imapc->sasl.authused = SASL_AUTH_NONE;  /* Clear the auth. mechanism used */
   imapc->sasl.authused = SASL_AUTH_NONE;  /* Clear the auth. mechanism used */
   imapc->tls_supported = FALSE;           /* Clear the TLS capability */
   imapc->tls_supported = FALSE;           /* Clear the TLS capability */
@@ -683,24 +683,37 @@ static CURLcode imap_perform_fetch(struct connectdata *conn)
 {
 {
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
   struct IMAP *imap = conn->data->req.protop;
   struct IMAP *imap = conn->data->req.protop;
-
   /* Check we have a UID */
   /* Check we have a UID */
-  if(!imap->uid) {
-    failf(conn->data, "Cannot FETCH without a UID.");
-    return CURLE_URL_MALFORMAT;
+  if(imap->uid) {
+
+    /* Send the FETCH command */
+    if(imap->partial)
+      result = imap_sendf(conn, "UID FETCH %s BODY[%s]<%s>",
+                            imap->uid,
+                            imap->section ? imap->section : "",
+                            imap->partial);
+    else
+      result = imap_sendf(conn, "UID FETCH %s BODY[%s]",
+                            imap->uid,
+                            imap->section ? imap->section : "");
+  }
+  else if(imap->mindex) {
+
+    /* Send the FETCH command */
+    if(imap->partial)
+      result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>",
+                            imap->mindex,
+                            imap->section ? imap->section : "",
+                            imap->partial);
+    else
+      result = imap_sendf(conn, "FETCH %s BODY[%s]",
+                            imap->mindex,
+                            imap->section ? imap->section : "");
+  }
+  else {
+        failf(conn->data, "Cannot FETCH without a UID.");
+        return CURLE_URL_MALFORMAT;
   }
   }
-
-  /* Send the FETCH command */
-  if(imap->partial)
-    result = imap_sendf(conn, "FETCH %s BODY[%s]<%s>",
-                        imap->uid,
-                        imap->section ? imap->section : "",
-                        imap->partial);
-  else
-    result = imap_sendf(conn, "FETCH %s BODY[%s]",
-                        imap->uid,
-                        imap->section ? imap->section : "");
-
   if(!result)
   if(!result)
     state(conn, IMAP_FETCH);
     state(conn, IMAP_FETCH);
 
 
@@ -1464,9 +1477,10 @@ static CURLcode imap_done(struct connectdata *conn, CURLcode status,
     result = status;         /* use the already set error code */
     result = status;         /* use the already set error code */
   }
   }
   else if(!data->set.connect_only && !imap->custom &&
   else if(!data->set.connect_only && !imap->custom &&
-          (imap->uid || data->set.upload ||
+          (imap->uid || imap->mindex || data->set.upload ||
           data->set.mimepost.kind != MIMEKIND_NONE)) {
           data->set.mimepost.kind != MIMEKIND_NONE)) {
     /* Handle responses after FETCH or APPEND transfer has finished */
     /* Handle responses after FETCH or APPEND transfer has finished */
+
     if(!data->set.upload && data->set.mimepost.kind == MIMEKIND_NONE)
     if(!data->set.upload && data->set.mimepost.kind == MIMEKIND_NONE)
       state(conn, IMAP_FETCH_FINAL);
       state(conn, IMAP_FETCH_FINAL);
     else {
     else {
@@ -1490,6 +1504,7 @@ static CURLcode imap_done(struct connectdata *conn, CURLcode status,
   Curl_safefree(imap->mailbox);
   Curl_safefree(imap->mailbox);
   Curl_safefree(imap->uidvalidity);
   Curl_safefree(imap->uidvalidity);
   Curl_safefree(imap->uid);
   Curl_safefree(imap->uid);
+  Curl_safefree(imap->mindex);
   Curl_safefree(imap->section);
   Curl_safefree(imap->section);
   Curl_safefree(imap->partial);
   Curl_safefree(imap->partial);
   Curl_safefree(imap->query);
   Curl_safefree(imap->query);
@@ -1543,14 +1558,14 @@ static CURLcode imap_perform(struct connectdata *conn, bool *connected,
   else if(imap->custom && (selected || !imap->mailbox))
   else if(imap->custom && (selected || !imap->mailbox))
     /* Custom command using the same mailbox or no mailbox */
     /* Custom command using the same mailbox or no mailbox */
     result = imap_perform_list(conn);
     result = imap_perform_list(conn);
-  else if(!imap->custom && selected && imap->uid)
+  else if(!imap->custom && selected && (imap->uid || imap->mindex))
     /* FETCH from the same mailbox */
     /* FETCH from the same mailbox */
     result = imap_perform_fetch(conn);
     result = imap_perform_fetch(conn);
   else if(!imap->custom && selected && imap->query)
   else if(!imap->custom && selected && imap->query)
     /* SEARCH the current mailbox */
     /* SEARCH the current mailbox */
     result = imap_perform_search(conn);
     result = imap_perform_search(conn);
   else if(imap->mailbox && !selected &&
   else if(imap->mailbox && !selected &&
-         (imap->custom || imap->uid || imap->query))
+         (imap->custom || imap->uid || imap->mindex || imap->query))
     /* SELECT the mailbox */
     /* SELECT the mailbox */
     result = imap_perform_select(conn);
     result = imap_perform_select(conn);
   else
   else
@@ -1702,8 +1717,6 @@ static CURLcode imap_regular_transfer(struct connectdata *conn,
 
 
 static CURLcode imap_setup_connection(struct connectdata *conn)
 static CURLcode imap_setup_connection(struct connectdata *conn)
 {
 {
-  struct Curl_easy *data = conn->data;
-
   /* Initialise the IMAP layer */
   /* Initialise the IMAP layer */
   CURLcode result = imap_init(conn);
   CURLcode result = imap_init(conn);
   if(result)
   if(result)
@@ -1711,7 +1724,6 @@ static CURLcode imap_setup_connection(struct connectdata *conn)
 
 
   /* Clear the TLS upgraded flag */
   /* Clear the TLS upgraded flag */
   conn->tls_upgraded = FALSE;
   conn->tls_upgraded = FALSE;
-  data->state.path++;   /* don't include the initial slash */
 
 
   return CURLE_OK;
   return CURLE_OK;
 }
 }
@@ -1944,7 +1956,7 @@ static CURLcode imap_parse_url_path(struct connectdata *conn)
   CURLcode result = CURLE_OK;
   CURLcode result = CURLE_OK;
   struct Curl_easy *data = conn->data;
   struct Curl_easy *data = conn->data;
   struct IMAP *imap = data->req.protop;
   struct IMAP *imap = data->req.protop;
-  const char *begin = data->state.path;
+  const char *begin = &data->state.up.path[1]; /* skip leading slash */
   const char *ptr = begin;
   const char *ptr = begin;
 
 
   /* See how much of the URL is a valid path and decode it */
   /* See how much of the URL is a valid path and decode it */
@@ -2016,6 +2028,13 @@ static CURLcode imap_parse_url_path(struct connectdata *conn)
       imap->uid = value;
       imap->uid = value;
       value = NULL;
       value = NULL;
     }
     }
+    else if(strcasecompare(name, "MAILINDEX") && !imap->mindex) {
+      if(valuelen > 0 && value[valuelen - 1] == '/')
+        value[valuelen - 1] = '\0';
+
+      imap->mindex = value;
+      value = NULL;
+    }
     else if(strcasecompare(name, "SECTION") && !imap->section) {
     else if(strcasecompare(name, "SECTION") && !imap->section) {
       if(valuelen > 0 && value[valuelen - 1] == '/')
       if(valuelen > 0 && value[valuelen - 1] == '/')
         value[valuelen - 1] = '\0';
         value[valuelen - 1] = '\0';
@@ -2043,17 +2062,10 @@ static CURLcode imap_parse_url_path(struct connectdata *conn)
 
 
   /* Does the URL contain a query parameter? Only valid when we have a mailbox
   /* Does the URL contain a query parameter? Only valid when we have a mailbox
      and no UID as per RFC-5092 */
      and no UID as per RFC-5092 */
-  if(imap->mailbox && !imap->uid && *ptr == '?') {
-    /* Find the length of the query parameter */
-    begin = ++ptr;
-    while(imap_is_bchar(*ptr))
-      ptr++;
-
-    /* Decode the query parameter */
-    result = Curl_urldecode(data, begin, ptr - begin, &imap->query, NULL,
-                            TRUE);
-    if(result)
-      return result;
+  if(imap->mailbox && !imap->uid && !imap->mindex) {
+    /* Get the query parameter, URL decoded */
+    (void)curl_url_get(data->state.uh, CURLUPART_QUERY, &imap->query,
+                       CURLU_URLDECODE);
   }
   }
 
 
   /* Any extra stuff at the end of the URL is an error */
   /* Any extra stuff at the end of the URL is an error */

+ 1 - 0
lib/imap.h

@@ -58,6 +58,7 @@ struct IMAP {
   char *mailbox;          /* Mailbox to select */
   char *mailbox;          /* Mailbox to select */
   char *uidvalidity;      /* UIDVALIDITY to check in select */
   char *uidvalidity;      /* UIDVALIDITY to check in select */
   char *uid;              /* Message UID to fetch */
   char *uid;              /* Message UID to fetch */
+  char *mindex;           /* Index in mail box of mail to fetch */
   char *section;          /* Message SECTION to fetch */
   char *section;          /* Message SECTION to fetch */
   char *partial;          /* Message PARTIAL to fetch */
   char *partial;          /* Message PARTIAL to fetch */
   char *query;            /* Query to search for */
   char *query;            /* Query to search for */

+ 0 - 1
lib/inet_ntop.h

@@ -35,4 +35,3 @@ char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t size);
 #endif
 #endif
 
 
 #endif /* HEADER_CURL_INET_NTOP_H */
 #endif /* HEADER_CURL_INET_NTOP_H */
-

+ 0 - 1
lib/inet_pton.h

@@ -37,4 +37,3 @@ int Curl_inet_pton(int, const char *, void *);
 #endif
 #endif
 
 
 #endif /* HEADER_CURL_INET_PTON_H */
 #endif /* HEADER_CURL_INET_PTON_H */
-

+ 3 - 3
lib/krb5.c

@@ -206,7 +206,7 @@ krb5_auth(void *app_data, struct connectdata *conn)
     if(maj != GSS_S_COMPLETE) {
     if(maj != GSS_S_COMPLETE) {
       gss_release_name(&min, &gssname);
       gss_release_name(&min, &gssname);
       if(service == srv_host) {
       if(service == srv_host) {
-        Curl_failf(data, "Error importing service name %s@%s", service, host);
+        failf(data, "Error importing service name %s@%s", service, host);
         return AUTH_ERROR;
         return AUTH_ERROR;
       }
       }
       service = srv_host;
       service = srv_host;
@@ -265,6 +265,7 @@ krb5_auth(void *app_data, struct connectdata *conn)
           result = CURLE_OUT_OF_MEMORY;
           result = CURLE_OUT_OF_MEMORY;
 
 
         free(p);
         free(p);
+        free(cmd);
 
 
         if(result) {
         if(result) {
           ret = -2;
           ret = -2;
@@ -290,8 +291,7 @@ krb5_auth(void *app_data, struct connectdata *conn)
                                       (unsigned char **)&_gssresp.value,
                                       (unsigned char **)&_gssresp.value,
                                       &_gssresp.length);
                                       &_gssresp.length);
           if(result) {
           if(result) {
-            Curl_failf(data, "base64-decoding: %s",
-                       curl_easy_strerror(result));
+            failf(data, "base64-decoding: %s", curl_easy_strerror(result));
             ret = AUTH_CONTINUE;
             ret = AUTH_CONTINUE;
             break;
             break;
           }
           }

+ 12 - 6
lib/ldap.c

@@ -5,7 +5,7 @@
  *                | (__| |_| |  _ <| |___
  *                | (__| |_| |  _ <| |___
  *                 \___|\___/|_| \_\_____|
  *                 \___|\___/|_| \_\_____|
  *
  *
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <[email protected]>, et al.
  *
  *
  * This software is licensed as described in the file COPYING, which
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * you should have received as part of this distribution. The terms
@@ -474,7 +474,13 @@ static CURLcode Curl_ldap(struct connectdata *conn, bool *done)
 #endif
 #endif
   }
   }
   if(rc != 0) {
   if(rc != 0) {
-    failf(data, "LDAP local: ldap_simple_bind_s %s", ldap_err2string(rc));
+#ifdef USE_WIN32_LDAP
+    failf(data, "LDAP local: bind via ldap_win_bind %s",
+          ldap_err2string(rc));
+#else
+    failf(data, "LDAP local: bind via ldap_simple_bind_s %s",
+          ldap_err2string(rc));
+#endif
     result = CURLE_LDAP_CANNOT_BIND;
     result = CURLE_LDAP_CANNOT_BIND;
     goto quit;
     goto quit;
   }
   }
@@ -838,9 +844,9 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp)
   size_t i;
   size_t i;
 
 
   if(!conn->data ||
   if(!conn->data ||
-     !conn->data->state.path ||
-     conn->data->state.path[0] != '/' ||
-     !checkprefix("LDAP", conn->data->change.url))
+     !conn->data->state.up.path ||
+     conn->data->state.up.path[0] != '/' ||
+     !strcasecompare("LDAP", conn->data->state.up.scheme))
     return LDAP_INVALID_SYNTAX;
     return LDAP_INVALID_SYNTAX;
 
 
   ludp->lud_scope = LDAP_SCOPE_BASE;
   ludp->lud_scope = LDAP_SCOPE_BASE;
@@ -848,7 +854,7 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp)
   ludp->lud_host  = conn->host.name;
   ludp->lud_host  = conn->host.name;
 
 
   /* Duplicate the path */
   /* Duplicate the path */
-  p = path = strdup(conn->data->state.path + 1);
+  p = path = strdup(conn->data->state.up.path + 1);
   if(!path)
   if(!path)
     return LDAP_NO_MEMORY;
     return LDAP_NO_MEMORY;
 
 

+ 0 - 1
lib/llist.h

@@ -51,4 +51,3 @@ void Curl_llist_move(struct curl_llist *, struct curl_llist_element *,
                      struct curl_llist *, struct curl_llist_element *);
                      struct curl_llist *, struct curl_llist_element *);
 
 
 #endif /* HEADER_CURL_LLIST_H */
 #endif /* HEADER_CURL_LLIST_H */
-

+ 1 - 1
lib/md4.c

@@ -3,7 +3,7 @@
  * MD4 Message-Digest Algorithm (RFC 1320).
  * MD4 Message-Digest Algorithm (RFC 1320).
  *
  *
  * Homepage:
  * Homepage:
- http://openwall.info/wiki/people/solar/software/public-domain-source-code/md4
+ https://openwall.info/wiki/people/solar/software/public-domain-source-code/md4
  *
  *
  * Author:
  * Author:
  * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
  * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>

+ 1 - 1
lib/md5.c

@@ -177,7 +177,7 @@ static void MD5_Final(unsigned char digest[16], MD5_CTX *ctx)
  * MD5 Message-Digest Algorithm (RFC 1321).
  * MD5 Message-Digest Algorithm (RFC 1321).
  *
  *
  * Homepage:
  * Homepage:
- http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
+ https://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
  *
  *
  * Author:
  * Author:
  * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
  * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>

+ 42 - 35
lib/multi.c

@@ -347,6 +347,7 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
   Curl_llist_init(&multi->pending, multi_freeamsg);
   Curl_llist_init(&multi->pending, multi_freeamsg);
 
 
   multi->max_pipeline_length = 5;
   multi->max_pipeline_length = 5;
+  multi->pipelining = CURLPIPE_MULTIPLEX;
 
 
   /* -1 means it not set by user, use the default value */
   /* -1 means it not set by user, use the default value */
   multi->maxconnects = -1;
   multi->maxconnects = -1;
@@ -491,6 +492,8 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
   data->state.conn_cache->closure_handle->set.timeout = data->set.timeout;
   data->state.conn_cache->closure_handle->set.timeout = data->set.timeout;
   data->state.conn_cache->closure_handle->set.server_response_timeout =
   data->state.conn_cache->closure_handle->set.server_response_timeout =
     data->set.server_response_timeout;
     data->set.server_response_timeout;
+  data->state.conn_cache->closure_handle->set.no_signal =
+    data->set.no_signal;
 
 
   update_timer(multi);
   update_timer(multi);
   return CURLM_OK;
   return CURLM_OK;
@@ -541,10 +544,8 @@ static CURLcode multi_done(struct connectdata **connp,
   Curl_getoff_all_pipelines(data, conn);
   Curl_getoff_all_pipelines(data, conn);
 
 
   /* Cleanup possible redirect junk */
   /* Cleanup possible redirect junk */
-  free(data->req.newurl);
-  data->req.newurl = NULL;
-  free(data->req.location);
-  data->req.location = NULL;
+  Curl_safefree(data->req.newurl);
+  Curl_safefree(data->req.location);
 
 
   switch(status) {
   switch(status) {
   case CURLE_ABORTED_BY_CALLBACK:
   case CURLE_ABORTED_BY_CALLBACK:
@@ -656,7 +657,6 @@ static CURLcode multi_done(struct connectdata **connp,
                     cache here, and therefore cannot be used from this point on
                     cache here, and therefore cannot be used from this point on
                  */
                  */
   Curl_free_request_state(data);
   Curl_free_request_state(data);
-
   return result;
   return result;
 }
 }
 
 
@@ -905,7 +905,7 @@ static int multi_getsock(struct Curl_easy *data,
     return 0;
     return 0;
 
 
   case CURLM_STATE_WAITRESOLVE:
   case CURLM_STATE_WAITRESOLVE:
-    return Curl_resolver_getsock(data->easy_conn, socks, numsocks);
+    return Curl_resolv_getsock(data->easy_conn, socks, numsocks);
 
 
   case CURLM_STATE_PROTOCONNECT:
   case CURLM_STATE_PROTOCONNECT:
   case CURLM_STATE_SENDPROTOCONNECT:
   case CURLM_STATE_SENDPROTOCONNECT:
@@ -1009,13 +1009,6 @@ CURLMcode curl_multi_wait(struct Curl_multi *multi,
   if(multi->in_callback)
   if(multi->in_callback)
     return CURLM_RECURSIVE_API_CALL;
     return CURLM_RECURSIVE_API_CALL;
 
 
-  /* If the internally desired timeout is actually shorter than requested from
-     the outside, then use the shorter time! But only if the internal timer
-     is actually larger than -1! */
-  (void)multi_timeout(multi, &timeout_internal);
-  if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms))
-    timeout_ms = (int)timeout_internal;
-
   /* Count up how many fds we have from the multi handle */
   /* Count up how many fds we have from the multi handle */
   data = multi->easyp;
   data = multi->easyp;
   while(data) {
   while(data) {
@@ -1040,6 +1033,13 @@ CURLMcode curl_multi_wait(struct Curl_multi *multi,
     data = data->next; /* check next handle */
     data = data->next; /* check next handle */
   }
   }
 
 
+  /* If the internally desired timeout is actually shorter than requested from
+     the outside, then use the shorter time! But only if the internal timer
+     is actually larger than -1! */
+  (void)multi_timeout(multi, &timeout_internal);
+  if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms))
+    timeout_ms = (int)timeout_internal;
+
   curlfds = nfds; /* number of internal file descriptors */
   curlfds = nfds; /* number of internal file descriptors */
   nfds += extra_nfds; /* add the externally provided ones */
   nfds += extra_nfds; /* add the externally provided ones */
 
 
@@ -1235,7 +1235,7 @@ static CURLcode multi_reconnect_request(struct connectdata **connp)
           return result;
           return result;
 
 
         /* Resolved, continue with the connection */
         /* Resolved, continue with the connection */
-        result = Curl_async_resolved(conn, &protocol_done);
+        result = Curl_once_resolved(conn, &protocol_done);
         if(result)
         if(result)
           return result;
           return result;
       }
       }
@@ -1511,7 +1511,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       }
       }
 
 
       if(!dns)
       if(!dns)
-        result = Curl_resolver_is_resolved(data->easy_conn, &dns);
+        result = Curl_resolv_check(data->easy_conn, &dns);
 
 
       /* Update sockets here, because the socket(s) may have been
       /* Update sockets here, because the socket(s) may have been
          closed and the application thus needs to be told, even if it
          closed and the application thus needs to be told, even if it
@@ -1524,10 +1524,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
       if(dns) {
       if(dns) {
         /* Perform the next step in the connection phase, and then move on
         /* Perform the next step in the connection phase, and then move on
            to the WAITCONNECT state */
            to the WAITCONNECT state */
-        result = Curl_async_resolved(data->easy_conn, &protocol_connect);
+        result = Curl_once_resolved(data->easy_conn, &protocol_connect);
 
 
         if(result)
         if(result)
-          /* if Curl_async_resolved() returns failure, the connection struct
+          /* if Curl_once_resolved() returns failure, the connection struct
              is already freed and gone */
              is already freed and gone */
           data->easy_conn = NULL;           /* no more connection */
           data->easy_conn = NULL;           /* no more connection */
         else {
         else {
@@ -1608,7 +1608,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
 
 
     case CURLM_STATE_SENDPROTOCONNECT:
     case CURLM_STATE_SENDPROTOCONNECT:
       result = Curl_protocol_connect(data->easy_conn, &protocol_connect);
       result = Curl_protocol_connect(data->easy_conn, &protocol_connect);
-      if(!protocol_connect)
+      if(!result && !protocol_connect)
         /* switch to waiting state */
         /* switch to waiting state */
         multistate(data, CURLM_STATE_PROTOCONNECT);
         multistate(data, CURLM_STATE_PROTOCONNECT);
       else if(!result) {
       else if(!result) {
@@ -1707,7 +1707,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
           char *newurl = NULL;
           char *newurl = NULL;
           followtype follow = FOLLOW_NONE;
           followtype follow = FOLLOW_NONE;
           CURLcode drc;
           CURLcode drc;
-          bool retry = FALSE;
 
 
           drc = Curl_retry_request(data->easy_conn, &newurl);
           drc = Curl_retry_request(data->easy_conn, &newurl);
           if(drc) {
           if(drc) {
@@ -1715,15 +1714,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
             result = drc;
             result = drc;
             stream_error = TRUE;
             stream_error = TRUE;
           }
           }
-          else
-            retry = (newurl)?TRUE:FALSE;
 
 
           Curl_posttransfer(data);
           Curl_posttransfer(data);
           drc = multi_done(&data->easy_conn, result, FALSE);
           drc = multi_done(&data->easy_conn, result, FALSE);
 
 
           /* When set to retry the connection, we must to go back to
           /* When set to retry the connection, we must to go back to
            * the CONNECT state */
            * the CONNECT state */
-          if(retry) {
+          if(newurl) {
             if(!drc || (drc == CURLE_SEND_ERROR)) {
             if(!drc || (drc == CURLE_SEND_ERROR)) {
               follow = FOLLOW_RETRY;
               follow = FOLLOW_RETRY;
               drc = Curl_follow(data, newurl, follow);
               drc = Curl_follow(data, newurl, follow);
@@ -1993,6 +1990,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
               rc = CURLM_CALL_MULTI_PERFORM;
               rc = CURLM_CALL_MULTI_PERFORM;
             }
             }
           }
           }
+          free(newurl);
         }
         }
         else {
         else {
           /* after the transfer is done, go DONE */
           /* after the transfer is done, go DONE */
@@ -2004,18 +2002,21 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
             newurl = data->req.location;
             newurl = data->req.location;
             data->req.location = NULL;
             data->req.location = NULL;
             result = Curl_follow(data, newurl, FOLLOW_FAKE);
             result = Curl_follow(data, newurl, FOLLOW_FAKE);
-            if(result)
+            free(newurl);
+            if(result) {
               stream_error = TRUE;
               stream_error = TRUE;
+              result = multi_done(&data->easy_conn, result, TRUE);
+            }
           }
           }
 
 
-          multistate(data, CURLM_STATE_DONE);
-          rc = CURLM_CALL_MULTI_PERFORM;
+          if(!result) {
+            multistate(data, CURLM_STATE_DONE);
+            rc = CURLM_CALL_MULTI_PERFORM;
+          }
         }
         }
       }
       }
       else if(comeback)
       else if(comeback)
         rc = CURLM_CALL_MULTI_PERFORM;
         rc = CURLM_CALL_MULTI_PERFORM;
-
-      free(newurl);
       break;
       break;
     }
     }
 
 
@@ -2131,15 +2132,21 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
     }
     }
 
 
     if(CURLM_STATE_COMPLETED == data->mstate) {
     if(CURLM_STATE_COMPLETED == data->mstate) {
-      /* now fill in the Curl_message with this info */
-      msg = &data->msg;
+      if(data->set.fmultidone) {
+        /* signal via callback instead */
+        data->set.fmultidone(data, result);
+      }
+      else {
+        /* now fill in the Curl_message with this info */
+        msg = &data->msg;
 
 
-      msg->extmsg.msg = CURLMSG_DONE;
-      msg->extmsg.easy_handle = data;
-      msg->extmsg.data.result = result;
+        msg->extmsg.msg = CURLMSG_DONE;
+        msg->extmsg.easy_handle = data;
+        msg->extmsg.data.result = result;
 
 
-      rc = multi_addmsg(multi, msg);
-      DEBUGASSERT(!data->easy_conn);
+        rc = multi_addmsg(multi, msg);
+        DEBUGASSERT(!data->easy_conn);
+      }
       multistate(data, CURLM_STATE_MSGSENT);
       multistate(data, CURLM_STATE_MSGSENT);
     }
     }
   } while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE));
   } while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE));
@@ -2705,7 +2712,7 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi,
     multi->push_userp = va_arg(param, void *);
     multi->push_userp = va_arg(param, void *);
     break;
     break;
   case CURLMOPT_PIPELINING:
   case CURLMOPT_PIPELINING:
-    multi->pipelining = va_arg(param, long);
+    multi->pipelining = va_arg(param, long) & CURLPIPE_MULTIPLEX;
     break;
     break;
   case CURLMOPT_TIMERFUNCTION:
   case CURLMOPT_TIMERFUNCTION:
     multi->timer_cb = va_arg(param, curl_multi_timer_callback);
     multi->timer_cb = va_arg(param, curl_multi_timer_callback);

+ 39 - 9
lib/netrc.c

@@ -57,7 +57,11 @@ int Curl_parsenetrc(const char *host,
 {
 {
   FILE *file;
   FILE *file;
   int retcode = 1;
   int retcode = 1;
-  int specific_login = (*loginp && **loginp != 0);
+  char *login = *loginp;
+  char *password = *passwordp;
+  bool specific_login = (login && *login != 0);
+  bool login_alloc = FALSE;
+  bool password_alloc = FALSE;
   bool netrc_alloc = FALSE;
   bool netrc_alloc = FALSE;
   enum host_lookup_state state = NOTHING;
   enum host_lookup_state state = NOTHING;
 
 
@@ -125,7 +129,7 @@ int Curl_parsenetrc(const char *host,
         continue;
         continue;
       while(!done && tok) {
       while(!done && tok) {
 
 
-        if((*loginp && **loginp) && (*passwordp && **passwordp)) {
+        if((login && *login) && (password && *password)) {
           done = TRUE;
           done = TRUE;
           break;
           break;
         }
         }
@@ -158,26 +162,34 @@ int Curl_parsenetrc(const char *host,
           /* we are now parsing sub-keywords concerning "our" host */
           /* we are now parsing sub-keywords concerning "our" host */
           if(state_login) {
           if(state_login) {
             if(specific_login) {
             if(specific_login) {
-              state_our_login = strcasecompare(*loginp, tok);
+              state_our_login = strcasecompare(login, tok);
             }
             }
             else {
             else {
-              free(*loginp);
-              *loginp = strdup(tok);
-              if(!*loginp) {
+              if(login_alloc) {
+                free(login);
+                login_alloc = FALSE;
+              }
+              login = strdup(tok);
+              if(!login) {
                 retcode = -1; /* allocation failed */
                 retcode = -1; /* allocation failed */
                 goto out;
                 goto out;
               }
               }
+              login_alloc = TRUE;
             }
             }
             state_login = 0;
             state_login = 0;
           }
           }
           else if(state_password) {
           else if(state_password) {
             if(state_our_login || !specific_login) {
             if(state_our_login || !specific_login) {
-              free(*passwordp);
-              *passwordp = strdup(tok);
-              if(!*passwordp) {
+              if(password_alloc) {
+                free(password);
+                password_alloc = FALSE;
+              }
+              password = strdup(tok);
+              if(!password) {
                 retcode = -1; /* allocation failed */
                 retcode = -1; /* allocation failed */
                 goto out;
                 goto out;
               }
               }
+              password_alloc = TRUE;
             }
             }
             state_password = 0;
             state_password = 0;
           }
           }
@@ -198,6 +210,24 @@ int Curl_parsenetrc(const char *host,
     } /* while fgets() */
     } /* while fgets() */
 
 
     out:
     out:
+    if(!retcode) {
+      if(login_alloc) {
+        if(*loginp)
+          free(*loginp);
+        *loginp = login;
+      }
+      if(password_alloc) {
+        if(*passwordp)
+          free(*passwordp);
+        *passwordp = password;
+      }
+    }
+    else {
+      if(login_alloc)
+        free(login);
+      if(password_alloc)
+        free(password);
+    }
     fclose(file);
     fclose(file);
   }
   }
 
 

+ 2 - 1
lib/nonblock.c

@@ -48,7 +48,8 @@ int curlx_nonblock(curl_socket_t sockfd,    /* operate on this */
                    int nonblock   /* TRUE or FALSE */)
                    int nonblock   /* TRUE or FALSE */)
 {
 {
 #if defined(USE_BLOCKING_SOCKETS)
 #if defined(USE_BLOCKING_SOCKETS)
-
+  (void)sockfd;
+  (void)nonblock;
   return 0; /* returns success */
   return 0; /* returns success */
 
 
 #elif defined(HAVE_FCNTL_O_NONBLOCK)
 #elif defined(HAVE_FCNTL_O_NONBLOCK)

+ 0 - 1
lib/nonblock.h

@@ -28,4 +28,3 @@ int curlx_nonblock(curl_socket_t sockfd,    /* operate on this */
                    int nonblock   /* TRUE or FALSE */);
                    int nonblock   /* TRUE or FALSE */);
 
 
 #endif /* HEADER_CURL_NONBLOCK_H */
 #endif /* HEADER_CURL_NONBLOCK_H */
-

+ 5 - 2
lib/nwlib.c

@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *                             \___|\___/|_| \_\_____|
  *
  *
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <[email protected]>, et al.
  *
  *
  * This software is licensed as described in the file COPYING, which
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * you should have received as part of this distribution. The terms
@@ -195,7 +195,7 @@ int GetOrSetUpData(int id, libdata_t **appData,
         if(!app_data->tenbytes || !app_data->lock) {
         if(!app_data->tenbytes || !app_data->lock) {
           if(app_data->lock)
           if(app_data->lock)
             NXMutexFree(app_data->lock);
             NXMutexFree(app_data->lock);
-
+          free(app_data->tenbytes);
           free(app_data);
           free(app_data);
           app_data = (libdata_t *) NULL;
           app_data = (libdata_t *) NULL;
           err      = ENOMEM;
           err      = ENOMEM;
@@ -213,6 +213,9 @@ int GetOrSetUpData(int id, libdata_t **appData,
           err = set_app_data(gLibId, app_data);
           err = set_app_data(gLibId, app_data);
 
 
           if(err) {
           if(err) {
+            if(app_data->lock)
+              NXMutexFree(app_data->lock);
+            free(app_data->tenbytes);
             free(app_data);
             free(app_data);
             app_data = (libdata_t *) NULL;
             app_data = (libdata_t *) NULL;
             err      = ENOMEM;
             err      = ENOMEM;

+ 0 - 1
lib/parsedate.h

@@ -28,4 +28,3 @@ extern const char * const Curl_month[12];
 CURLcode Curl_gmtime(time_t intime, struct tm *store);
 CURLcode Curl_gmtime(time_t intime, struct tm *store);
 
 
 #endif /* HEADER_CURL_PARSEDATE_H */
 #endif /* HEADER_CURL_PARSEDATE_H */
-

+ 1 - 4
lib/pop3.c

@@ -1303,8 +1303,6 @@ static CURLcode pop3_regular_transfer(struct connectdata *conn,
 
 
 static CURLcode pop3_setup_connection(struct connectdata *conn)
 static CURLcode pop3_setup_connection(struct connectdata *conn)
 {
 {
-  struct Curl_easy *data = conn->data;
-
   /* Initialise the POP3 layer */
   /* Initialise the POP3 layer */
   CURLcode result = pop3_init(conn);
   CURLcode result = pop3_init(conn);
   if(result)
   if(result)
@@ -1312,7 +1310,6 @@ static CURLcode pop3_setup_connection(struct connectdata *conn)
 
 
   /* Clear the TLS upgraded flag */
   /* Clear the TLS upgraded flag */
   conn->tls_upgraded = FALSE;
   conn->tls_upgraded = FALSE;
-  data->state.path++;   /* don't include the initial slash */
 
 
   return CURLE_OK;
   return CURLE_OK;
 }
 }
@@ -1387,7 +1384,7 @@ static CURLcode pop3_parse_url_path(struct connectdata *conn)
   /* The POP3 struct is already initialised in pop3_connect() */
   /* The POP3 struct is already initialised in pop3_connect() */
   struct Curl_easy *data = conn->data;
   struct Curl_easy *data = conn->data;
   struct POP3 *pop3 = data->req.protop;
   struct POP3 *pop3 = data->req.protop;
-  const char *path = data->state.path;
+  const char *path = &data->state.up.path[1]; /* skip leading path */
 
 
   /* URL decode the path for the message ID */
   /* URL decode the path for the message ID */
   return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);
   return Curl_urldecode(data, path, 0, &pop3->id, NULL, TRUE);

+ 0 - 1
lib/progress.h

@@ -62,4 +62,3 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize,
 #define PGRS_HEADERS_OUT (1<<7) /* set when the headers have been written */
 #define PGRS_HEADERS_OUT (1<<7) /* set when the headers have been written */
 
 
 #endif /* HEADER_CURL_PROGRESS_H */
 #endif /* HEADER_CURL_PROGRESS_H */
-

+ 3 - 1
lib/rand.c

@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *                             \___|\___/|_| \_\_____|
  *
  *
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <[email protected]>, et al.
  *
  *
  * This software is licensed as described in the file COPYING, which
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * you should have received as part of this distribution. The terms
@@ -174,6 +174,8 @@ CURLcode Curl_rand_hex(struct Curl_easy *data, unsigned char *rnd,
     return result;
     return result;
 
 
   while(num) {
   while(num) {
+    /* clang-tidy warns on this line without this comment: */
+    /* NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) */
     *rnd++ = hex[(*bufp & 0xF0)>>4];
     *rnd++ = hex[(*bufp & 0xF0)>>4];
     *rnd++ = hex[*bufp & 0x0F];
     *rnd++ = hex[*bufp & 0x0F];
     bufp++;
     bufp++;

+ 14 - 13
lib/rtsp.c

@@ -462,7 +462,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
     return CURLE_OUT_OF_MEMORY;
     return CURLE_OUT_OF_MEMORY;
 
 
   result =
   result =
-    Curl_add_bufferf(req_buffer,
+    Curl_add_bufferf(&req_buffer,
                      "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
                      "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
                      "CSeq: %ld\r\n", /* CSeq */
                      "CSeq: %ld\r\n", /* CSeq */
                      p_request, p_stream_uri, rtsp->CSeq_sent);
                      p_request, p_stream_uri, rtsp->CSeq_sent);
@@ -474,7 +474,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
    * to make comparison easier
    * to make comparison easier
    */
    */
   if(p_session_id) {
   if(p_session_id) {
-    result = Curl_add_bufferf(req_buffer, "Session: %s\r\n", p_session_id);
+    result = Curl_add_bufferf(&req_buffer, "Session: %s\r\n", p_session_id);
     if(result)
     if(result)
       return result;
       return result;
   }
   }
@@ -482,7 +482,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
   /*
   /*
    * Shared HTTP-like options
    * Shared HTTP-like options
    */
    */
-  result = Curl_add_bufferf(req_buffer,
+  result = Curl_add_bufferf(&req_buffer,
                             "%s" /* transport */
                             "%s" /* transport */
                             "%s" /* accept */
                             "%s" /* accept */
                             "%s" /* accept-encoding */
                             "%s" /* accept-encoding */
@@ -541,9 +541,10 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
       /* As stated in the http comments, it is probably not wise to
       /* As stated in the http comments, it is probably not wise to
        * actually set a custom Content-Length in the headers */
        * actually set a custom Content-Length in the headers */
       if(!Curl_checkheaders(conn, "Content-Length")) {
       if(!Curl_checkheaders(conn, "Content-Length")) {
-        result = Curl_add_bufferf(req_buffer,
-            "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n",
-            (data->set.upload ? putsize : postsize));
+        result =
+          Curl_add_bufferf(&req_buffer,
+                           "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n",
+                           (data->set.upload ? putsize : postsize));
         if(result)
         if(result)
           return result;
           return result;
       }
       }
@@ -551,8 +552,8 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
       if(rtspreq == RTSPREQ_SET_PARAMETER ||
       if(rtspreq == RTSPREQ_SET_PARAMETER ||
          rtspreq == RTSPREQ_GET_PARAMETER) {
          rtspreq == RTSPREQ_GET_PARAMETER) {
         if(!Curl_checkheaders(conn, "Content-Type")) {
         if(!Curl_checkheaders(conn, "Content-Type")) {
-          result = Curl_add_bufferf(req_buffer,
-              "Content-Type: text/parameters\r\n");
+          result = Curl_add_bufferf(&req_buffer,
+                                    "Content-Type: text/parameters\r\n");
           if(result)
           if(result)
             return result;
             return result;
         }
         }
@@ -560,8 +561,8 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
 
 
       if(rtspreq == RTSPREQ_ANNOUNCE) {
       if(rtspreq == RTSPREQ_ANNOUNCE) {
         if(!Curl_checkheaders(conn, "Content-Type")) {
         if(!Curl_checkheaders(conn, "Content-Type")) {
-          result = Curl_add_bufferf(req_buffer,
-              "Content-Type: application/sdp\r\n");
+          result = Curl_add_bufferf(&req_buffer,
+                                    "Content-Type: application/sdp\r\n");
           if(result)
           if(result)
             return result;
             return result;
         }
         }
@@ -579,19 +580,19 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
   /* RTSP never allows chunked transfer */
   /* RTSP never allows chunked transfer */
   data->req.forbidchunk = TRUE;
   data->req.forbidchunk = TRUE;
   /* Finish the request buffer */
   /* Finish the request buffer */
-  result = Curl_add_buffer(req_buffer, "\r\n", 2);
+  result = Curl_add_buffer(&req_buffer, "\r\n", 2);
   if(result)
   if(result)
     return result;
     return result;
 
 
   if(postsize > 0) {
   if(postsize > 0) {
-    result = Curl_add_buffer(req_buffer, data->set.postfields,
+    result = Curl_add_buffer(&req_buffer, data->set.postfields,
                              (size_t)postsize);
                              (size_t)postsize);
     if(result)
     if(result)
       return result;
       return result;
   }
   }
 
 
   /* issue the request */
   /* issue the request */
-  result = Curl_add_buffer_send(req_buffer, conn,
+  result = Curl_add_buffer_send(&req_buffer, conn,
                                 &data->info.request_size, 0, FIRSTSOCKET);
                                 &data->info.request_size, 0, FIRSTSOCKET);
   if(result) {
   if(result) {
     failf(data, "Failed sending RTSP request");
     failf(data, "Failed sending RTSP request");

+ 0 - 1
lib/rtsp.h

@@ -64,4 +64,3 @@ struct RTSP {
 
 
 
 
 #endif /* HEADER_CURL_RTSP_H */
 #endif /* HEADER_CURL_RTSP_H */
-

+ 4 - 2
lib/security.c

@@ -61,7 +61,9 @@
 #include "strcase.h"
 #include "strcase.h"
 #include "warnless.h"
 #include "warnless.h"
 #include "strdup.h"
 #include "strdup.h"
-/* The last #include file should be: */
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
 #include "memdebug.h"
 #include "memdebug.h"
 
 
 static const struct {
 static const struct {
@@ -422,7 +424,7 @@ static int sec_set_protection_level(struct connectdata *conn)
 
 
   if(!conn->sec_complete) {
   if(!conn->sec_complete) {
     infof(conn->data, "Trying to change the protection level after the"
     infof(conn->data, "Trying to change the protection level after the"
-                      "completion of the data exchange.\n");
+                      " completion of the data exchange.\n");
     return -1;
     return -1;
   }
   }
 
 

+ 0 - 1
lib/select.h

@@ -113,4 +113,3 @@ int tpf_select_libcurl(int maxfds, fd_set* reads, fd_set* writes,
 #endif
 #endif
 
 
 #endif /* HEADER_CURL_SELECT_H */
 #endif /* HEADER_CURL_SELECT_H */
-

+ 1 - 1
lib/sendf.h

@@ -36,7 +36,7 @@ void Curl_failf(struct Curl_easy *, const char *fmt, ...);
 #elif defined(HAVE_VARIADIC_MACROS_GCC)
 #elif defined(HAVE_VARIADIC_MACROS_GCC)
 #define infof(x...)  Curl_nop_stmt
 #define infof(x...)  Curl_nop_stmt
 #else
 #else
-#define infof (void)
+#error "missing VARIADIC macro define, fix and rebuild!"
 #endif
 #endif
 
 
 #else /* CURL_DISABLE_VERBOSE_STRINGS */
 #else /* CURL_DISABLE_VERBOSE_STRINGS */

+ 31 - 0
lib/setopt.c

@@ -127,9 +127,11 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
     data->set.dns_cache_timeout = arg;
     data->set.dns_cache_timeout = arg;
     break;
     break;
   case CURLOPT_DNS_USE_GLOBAL_CACHE:
   case CURLOPT_DNS_USE_GLOBAL_CACHE:
+#if 0 /* deprecated */
     /* remember we want this enabled */
     /* remember we want this enabled */
     arg = va_arg(param, long);
     arg = va_arg(param, long);
     data->set.global_dns_cache = (0 != arg) ? TRUE : FALSE;
     data->set.global_dns_cache = (0 != arg) ? TRUE : FALSE;
+#endif
     break;
     break;
   case CURLOPT_SSL_CIPHER_LIST:
   case CURLOPT_SSL_CIPHER_LIST:
     /* set a list of cipher we want to use in the SSL connection */
     /* set a list of cipher we want to use in the SSL connection */
@@ -841,6 +843,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
 #else
 #else
     if(arg > CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE)
     if(arg > CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE)
       return CURLE_UNSUPPORTED_PROTOCOL;
       return CURLE_UNSUPPORTED_PROTOCOL;
+    if(arg == CURL_HTTP_VERSION_NONE)
+      arg = CURL_HTTP_VERSION_2TLS;
 #endif
 #endif
     data->set.httpversion = arg;
     data->set.httpversion = arg;
     break;
     break;
@@ -1936,6 +1940,22 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
 
 
     break;
     break;
 
 
+  case CURLOPT_UPLOAD_BUFFERSIZE:
+    /*
+     * The application kindly asks for a differently sized upload buffer.
+     * Cap it to sensible.
+     */
+    arg = va_arg(param, long);
+
+    if(arg > UPLOADBUFFER_MAX)
+      arg = UPLOADBUFFER_MAX;
+    else if(arg < UPLOADBUFFER_MIN)
+      arg = UPLOADBUFFER_MIN;
+
+    data->set.upload_buffer_size = arg;
+    Curl_safefree(data->state.ulbuf); /* force a realloc next opportunity */
+    break;
+
   case CURLOPT_NOSIGNAL:
   case CURLOPT_NOSIGNAL:
     /*
     /*
      * The application asks not to set any signal() or alarm() handlers,
      * The application asks not to set any signal() or alarm() handlers,
@@ -2599,6 +2619,17 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
     data->set.disallow_username_in_url =
     data->set.disallow_username_in_url =
       (0 != va_arg(param, long)) ? TRUE : FALSE;
       (0 != va_arg(param, long)) ? TRUE : FALSE;
     break;
     break;
+  case CURLOPT_DOH_URL:
+    result = Curl_setstropt(&data->set.str[STRING_DOH],
+                            va_arg(param, char *));
+    data->set.doh = data->set.str[STRING_DOH]?TRUE:FALSE;
+    break;
+  case CURLOPT_UPKEEP_INTERVAL_MS:
+    arg = va_arg(param, long);
+    if(arg < 0)
+      return CURLE_BAD_FUNCTION_ARGUMENT;
+    data->set.upkeep_interval_ms = arg;
+    break;
   default:
   default:
     /* unknown tag and its companion, just ignore: */
     /* unknown tag and its companion, just ignore: */
     result = CURLE_UNKNOWN_OPTION;
     result = CURLE_UNKNOWN_OPTION;

+ 0 - 1
lib/slist.c

@@ -142,4 +142,3 @@ void curl_slist_free_all(struct curl_slist *list)
     item = next;
     item = next;
   } while(next);
   } while(next);
 }
 }
-

+ 0 - 1
lib/slist.h

@@ -37,4 +37,3 @@ struct curl_slist *Curl_slist_append_nodup(struct curl_slist *list,
                                            char *data);
                                            char *data);
 
 
 #endif /* HEADER_CURL_SLIST_H */
 #endif /* HEADER_CURL_SLIST_H */
-

+ 3 - 2
lib/smb.c

@@ -610,7 +610,8 @@ static CURLcode smb_send_and_recv(struct connectdata *conn, void **msg)
 
 
   /* Check if there is data in the transfer buffer */
   /* Check if there is data in the transfer buffer */
   if(!smbc->send_size && smbc->upload_size) {
   if(!smbc->send_size && smbc->upload_size) {
-    size_t nread = smbc->upload_size > UPLOAD_BUFSIZE ? UPLOAD_BUFSIZE :
+    size_t nread = smbc->upload_size > conn->data->set.upload_buffer_size ?
+      conn->data->set.upload_buffer_size :
       smbc->upload_size;
       smbc->upload_size;
     conn->data->req.upload_fromhere = conn->data->state.ulbuf;
     conn->data->req.upload_fromhere = conn->data->state.ulbuf;
     result = Curl_fillreadbuffer(conn, nread, &nread);
     result = Curl_fillreadbuffer(conn, nread, &nread);
@@ -968,7 +969,7 @@ static CURLcode smb_parse_url_path(struct connectdata *conn)
   char *slash;
   char *slash;
 
 
   /* URL decode the path */
   /* URL decode the path */
-  result = Curl_urldecode(data, data->state.path, 0, &path, NULL, TRUE);
+  result = Curl_urldecode(data, data->state.up.path, 0, &path, NULL, TRUE);
   if(result)
   if(result)
     return result;
     return result;
 
 

+ 3 - 6
lib/smtp.c

@@ -1441,7 +1441,6 @@ static CURLcode smtp_regular_transfer(struct connectdata *conn,
 
 
 static CURLcode smtp_setup_connection(struct connectdata *conn)
 static CURLcode smtp_setup_connection(struct connectdata *conn)
 {
 {
-  struct Curl_easy *data = conn->data;
   CURLcode result;
   CURLcode result;
 
 
   /* Clear the TLS upgraded flag */
   /* Clear the TLS upgraded flag */
@@ -1452,8 +1451,6 @@ static CURLcode smtp_setup_connection(struct connectdata *conn)
   if(result)
   if(result)
     return result;
     return result;
 
 
-  data->state.path++;   /* don't include the initial slash */
-
   return CURLE_OK;
   return CURLE_OK;
 }
 }
 
 
@@ -1507,7 +1504,7 @@ static CURLcode smtp_parse_url_path(struct connectdata *conn)
   /* The SMTP struct is already initialised in smtp_connect() */
   /* The SMTP struct is already initialised in smtp_connect() */
   struct Curl_easy *data = conn->data;
   struct Curl_easy *data = conn->data;
   struct smtp_conn *smtpc = &conn->proto.smtpc;
   struct smtp_conn *smtpc = &conn->proto.smtpc;
-  const char *path = data->state.path;
+  const char *path = &data->state.up.path[1]; /* skip leading path */
   char localhost[HOSTNAME_MAX + 1];
   char localhost[HOSTNAME_MAX + 1];
 
 
   /* Calculate the path if necessary */
   /* Calculate the path if necessary */
@@ -1563,14 +1560,14 @@ CURLcode Curl_smtp_escape_eob(struct connectdata *conn, const ssize_t nread)
   if(!scratch || data->set.crlf) {
   if(!scratch || data->set.crlf) {
     oldscratch = scratch;
     oldscratch = scratch;
 
 
-    scratch = newscratch = malloc(2 * UPLOAD_BUFSIZE);
+    scratch = newscratch = malloc(2 * data->set.upload_buffer_size);
     if(!newscratch) {
     if(!newscratch) {
       failf(data, "Failed to alloc scratch buffer!");
       failf(data, "Failed to alloc scratch buffer!");
 
 
       return CURLE_OUT_OF_MEMORY;
       return CURLE_OUT_OF_MEMORY;
     }
     }
   }
   }
-  DEBUGASSERT(UPLOAD_BUFSIZE >= nread);
+  DEBUGASSERT(data->set.upload_buffer_size >= (size_t)nread);
 
 
   /* Have we already sent part of the EOB? */
   /* Have we already sent part of the EOB? */
   eob_sent = smtp->eob;
   eob_sent = smtp->eob;

+ 0 - 1
lib/sockaddr.h

@@ -40,4 +40,3 @@ struct Curl_sockaddr_storage {
 };
 };
 
 
 #endif /* HEADER_CURL_SOCKADDR_H */
 #endif /* HEADER_CURL_SOCKADDR_H */
-

+ 1 - 2
lib/socks.c

@@ -98,7 +98,7 @@ int Curl_blockread_all(struct connectdata *conn, /* connection data */
 * destination server.
 * destination server.
 *
 *
 * Reference :
 * Reference :
-*   http://socks.permeo.com/protocol/socks4.protocol
+*   https://www.openssh.com/txt/socks4.protocol
 *
 *
 * Note :
 * Note :
 *   Set protocol4a=true for  "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
 *   Set protocol4a=true for  "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
@@ -789,4 +789,3 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
 }
 }
 
 
 #endif /* CURL_DISABLE_PROXY */
 #endif /* CURL_DISABLE_PROXY */
-

+ 0 - 1
lib/socks.h

@@ -73,4 +73,3 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(int sockindex,
 #endif /* CURL_DISABLE_PROXY */
 #endif /* CURL_DISABLE_PROXY */
 
 
 #endif  /* HEADER_CURL_SOCKS_H */
 #endif  /* HEADER_CURL_SOCKS_H */
-

+ 0 - 1
lib/splay.c

@@ -274,4 +274,3 @@ int Curl_splayremovebyaddr(struct Curl_tree *t,
 
 
   return 0;
   return 0;
 }
 }
-

+ 1 - 1
lib/ssh.c

@@ -2926,7 +2926,7 @@ static CURLcode ssh_connect(struct connectdata *conn, bool *done)
     int rc;
     int rc;
     ssh->kh = libssh2_knownhost_init(ssh->ssh_session);
     ssh->kh = libssh2_knownhost_init(ssh->ssh_session);
     if(!ssh->kh) {
     if(!ssh->kh) {
-      /* eeek. TODO: free the ssh_session! */
+      libssh2_session_free(ssh->ssh_session);
       return CURLE_FAILED_INIT;
       return CURLE_FAILED_INIT;
     }
     }
 
 

+ 1 - 1
lib/strdup.c

@@ -81,7 +81,7 @@ void *Curl_memdup(const void *src, size_t length)
  * Curl_saferealloc(ptr, size)
  * Curl_saferealloc(ptr, size)
  *
  *
  * Does a normal realloc(), but will free the data pointer if the realloc
  * Does a normal realloc(), but will free the data pointer if the realloc
- * fails. If 'size' is zero, it will free the data and return a failure.
+ * fails. If 'size' is non-zero, it will free the data and return a failure.
  *
  *
  * This convenience function is provided and used to help us avoid a common
  * This convenience function is provided and used to help us avoid a common
  * mistake pattern when we could pass in a zero, catch the NULL return and end
  * mistake pattern when we could pass in a zero, catch the NULL return and end

+ 3 - 6
lib/strerror.c

@@ -191,9 +191,6 @@ curl_easy_strerror(CURLcode error)
   case CURLE_TELNET_OPTION_SYNTAX :
   case CURLE_TELNET_OPTION_SYNTAX :
     return "Malformed telnet option";
     return "Malformed telnet option";
 
 
-  case CURLE_PEER_FAILED_VERIFICATION:
-    return "SSL peer certificate or SSH remote key was not OK";
-
   case CURLE_GOT_NOTHING:
   case CURLE_GOT_NOTHING:
     return "Server returned nothing (no headers, no data)";
     return "Server returned nothing (no headers, no data)";
 
 
@@ -218,9 +215,8 @@ curl_easy_strerror(CURLcode error)
   case CURLE_SSL_CIPHER:
   case CURLE_SSL_CIPHER:
     return "Couldn't use specified SSL cipher";
     return "Couldn't use specified SSL cipher";
 
 
-  case CURLE_SSL_CACERT:
-    return "Peer certificate cannot be authenticated with given CA "
-      "certificates";
+  case CURLE_PEER_FAILED_VERIFICATION:
+    return "SSL peer certificate or SSH remote key was not OK";
 
 
   case CURLE_SSL_CACERT_BADFILE:
   case CURLE_SSL_CACERT_BADFILE:
     return "Problem with the SSL CA cert (path? access rights?)";
     return "Problem with the SSL CA cert (path? access rights?)";
@@ -324,6 +320,7 @@ curl_easy_strerror(CURLcode error)
   case CURLE_OBSOLETE44:
   case CURLE_OBSOLETE44:
   case CURLE_OBSOLETE46:
   case CURLE_OBSOLETE46:
   case CURLE_OBSOLETE50:
   case CURLE_OBSOLETE50:
+  case CURLE_OBSOLETE51:
   case CURLE_OBSOLETE57:
   case CURLE_OBSOLETE57:
   case CURL_LAST:
   case CURL_LAST:
     break;
     break;

+ 0 - 1
lib/telnet.h

@@ -26,4 +26,3 @@ extern const struct Curl_handler Curl_handler_telnet;
 #endif
 #endif
 
 
 #endif /* HEADER_CURL_TELNET_H */
 #endif /* HEADER_CURL_TELNET_H */
-

+ 2 - 2
lib/tftp.c

@@ -485,7 +485,7 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
     /* As RFC3617 describes the separator slash is not actually part of the
     /* As RFC3617 describes the separator slash is not actually part of the
        file name so we skip the always-present first letter of the path
        file name so we skip the always-present first letter of the path
        string. */
        string. */
-    result = Curl_urldecode(data, &state->conn->data->state.path[1], 0,
+    result = Curl_urldecode(data, &state->conn->data->state.up.path[1], 0,
                             &filename, NULL, FALSE);
                             &filename, NULL, FALSE);
     if(result)
     if(result)
       return result;
       return result;
@@ -1374,7 +1374,7 @@ static CURLcode tftp_setup_connection(struct connectdata * conn)
 
 
   /* TFTP URLs support an extension like ";mode=<typecode>" that
   /* TFTP URLs support an extension like ";mode=<typecode>" that
    * we'll try to get now! */
    * we'll try to get now! */
-  type = strstr(data->state.path, ";mode=");
+  type = strstr(data->state.up.path, ";mode=");
 
 
   if(!type)
   if(!type)
     type = strstr(conn->host.rawalloc, ";mode=");
     type = strstr(conn->host.rawalloc, ";mode=");

+ 0 - 1
lib/tftp.h

@@ -26,4 +26,3 @@ extern const struct Curl_handler Curl_handler_tftp;
 #endif
 #endif
 
 
 #endif /* HEADER_CURL_TFTP_H */
 #endif /* HEADER_CURL_TFTP_H */
-

+ 20 - 3
lib/timeval.c

@@ -5,7 +5,7 @@
  *                            | (__| |_| |  _ <| |___
  *                            | (__| |_| |  _ <| |___
  *                             \___|\___/|_| \_\_____|
  *                             \___|\___/|_| \_\_____|
  *
  *
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <[email protected]>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <[email protected]>, et al.
  *
  *
  * This software is licensed as described in the file COPYING, which
  * This software is licensed as described in the file COPYING, which
  * you should have received as part of this distribution. The terms
  * you should have received as part of this distribution. The terms
@@ -33,7 +33,8 @@ struct curltime Curl_now(void)
   */
   */
   struct curltime now;
   struct curltime now;
 #if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \
 #if !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_VISTA) || \
-    (_WIN32_WINNT < _WIN32_WINNT_VISTA)
+    (_WIN32_WINNT < _WIN32_WINNT_VISTA) || \
+    (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
   DWORD milliseconds = GetTickCount();
   DWORD milliseconds = GetTickCount();
   now.tv_sec = milliseconds / 1000;
   now.tv_sec = milliseconds / 1000;
   now.tv_usec = (milliseconds % 1000) * 1000;
   now.tv_usec = (milliseconds % 1000) * 1000;
@@ -60,7 +61,23 @@ struct curltime Curl_now(void)
   struct timeval now;
   struct timeval now;
   struct curltime cnow;
   struct curltime cnow;
   struct timespec tsnow;
   struct timespec tsnow;
-  if(0 == clock_gettime(CLOCK_MONOTONIC, &tsnow)) {
+
+  /*
+  ** clock_gettime() may be defined by Apple's SDK as weak symbol thus
+  ** code compiles but fails during run-time if clock_gettime() is
+  ** called on unsupported OS version.
+  */
+#if defined(__APPLE__) && (HAVE_BUILTIN_AVAILABLE == 1)
+  bool have_clock_gettime = FALSE;
+  if(__builtin_available(macOS 10.12, iOS 10, tvOS 10, watchOS 3, *))
+    have_clock_gettime = TRUE;
+#endif
+
+  if(
+#if defined(__APPLE__) && (HAVE_BUILTIN_AVAILABLE == 1)
+    have_clock_gettime &&
+#endif
+    (0 == clock_gettime(CLOCK_MONOTONIC, &tsnow))) {
     cnow.tv_sec = tsnow.tv_sec;
     cnow.tv_sec = tsnow.tv_sec;
     cnow.tv_usec = (unsigned int)(tsnow.tv_nsec / 1000);
     cnow.tv_usec = (unsigned int)(tsnow.tv_nsec / 1000);
   }
   }

+ 28 - 340
lib/transfer.c

@@ -75,6 +75,7 @@
 #include "http2.h"
 #include "http2.h"
 #include "mime.h"
 #include "mime.h"
 #include "strcase.h"
 #include "strcase.h"
+#include "urlapi-int.h"
 
 
 /* The last 3 #include files should be in this order */
 /* The last 3 #include files should be in this order */
 #include "curl_printf.h"
 #include "curl_printf.h"
@@ -362,7 +363,7 @@ static int data_pending(const struct connectdata *conn)
   return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) ||
   return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) ||
 #if defined(USE_NGHTTP2)
 #if defined(USE_NGHTTP2)
     Curl_ssl_data_pending(conn, FIRSTSOCKET) ||
     Curl_ssl_data_pending(conn, FIRSTSOCKET) ||
-    /* For HTTP/2, we may read up everything including responde body
+    /* For HTTP/2, we may read up everything including response body
        with header fields in Curl_http_readwrite_headers. If no
        with header fields in Curl_http_readwrite_headers. If no
        content-length is provided, curl waits for the connection
        content-length is provided, curl waits for the connection
        close, which we emulate it using conn->proto.httpc.closed =
        close, which we emulate it using conn->proto.httpc.closed =
@@ -566,7 +567,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
             infof(data,
             infof(data,
                   "Rewinding stream by : %zd"
                   "Rewinding stream by : %zd"
                   " bytes on url %s (zero-length body)\n",
                   " bytes on url %s (zero-length body)\n",
-                  nread, data->state.path);
+                  nread, data->state.up.path);
             read_rewind(conn, (size_t)nread);
             read_rewind(conn, (size_t)nread);
           }
           }
           else {
           else {
@@ -574,7 +575,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
                   "Excess found in a non pipelined read:"
                   "Excess found in a non pipelined read:"
                   " excess = %zd"
                   " excess = %zd"
                   " url = %s (zero-length body)\n",
                   " url = %s (zero-length body)\n",
-                  nread, data->state.path);
+                  nread, data->state.up.path);
           }
           }
         }
         }
 
 
@@ -743,7 +744,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
                   " bytes on url %s (size = %" CURL_FORMAT_CURL_OFF_T
                   " bytes on url %s (size = %" CURL_FORMAT_CURL_OFF_T
                   ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
                   ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
                   ", bytecount = %" CURL_FORMAT_CURL_OFF_T ", nread = %zd)\n",
                   ", bytecount = %" CURL_FORMAT_CURL_OFF_T ", nread = %zd)\n",
-                  excess, data->state.path,
+                  excess, data->state.up.path,
                   k->size, k->maxdownload, k->bytecount, nread);
                   k->size, k->maxdownload, k->bytecount, nread);
             read_rewind(conn, excess);
             read_rewind(conn, excess);
           }
           }
@@ -878,7 +879,7 @@ static CURLcode done_sending(struct connectdata *conn,
   return CURLE_OK;
   return CURLE_OK;
 }
 }
 
 
-#ifdef WIN32
+#if defined(WIN32) && !defined(USE_LWIPSOCK)
 #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
 #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
 #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
 #define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
 #endif
 #endif
@@ -959,7 +960,8 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
             sending_http_headers = FALSE;
             sending_http_headers = FALSE;
         }
         }
 
 
-        result = Curl_fillreadbuffer(conn, UPLOAD_BUFSIZE, &fillcount);
+        result = Curl_fillreadbuffer(conn, data->set.upload_buffer_size,
+                                     &fillcount);
         if(result)
         if(result)
           return result;
           return result;
 
 
@@ -991,7 +993,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
          (data->set.crlf))) {
          (data->set.crlf))) {
         /* Do we need to allocate a scratch buffer? */
         /* Do we need to allocate a scratch buffer? */
         if(!data->state.scratch) {
         if(!data->state.scratch) {
-          data->state.scratch = malloc(2 * UPLOAD_BUFSIZE);
+          data->state.scratch = malloc(2 * data->set.upload_buffer_size);
           if(!data->state.scratch) {
           if(!data->state.scratch) {
             failf(data, "Failed to alloc scratch buffer!");
             failf(data, "Failed to alloc scratch buffer!");
 
 
@@ -1453,314 +1455,11 @@ CURLcode Curl_posttransfer(struct Curl_easy *data)
   return CURLE_OK;
   return CURLE_OK;
 }
 }
 
 
-#ifndef CURL_DISABLE_HTTP
-/*
- * Find the separator at the end of the host name, or the '?' in cases like
- * http://www.url.com?id=2380
- */
-static const char *find_host_sep(const char *url)
-{
-  const char *sep;
-  const char *query;
-
-  /* Find the start of the hostname */
-  sep = strstr(url, "//");
-  if(!sep)
-    sep = url;
-  else
-    sep += 2;
-
-  query = strchr(sep, '?');
-  sep = strchr(sep, '/');
-
-  if(!sep)
-    sep = url + strlen(url);
-
-  if(!query)
-    query = url + strlen(url);
-
-  return sep < query ? sep : query;
-}
-
-/*
- * Decide in an encoding-independent manner whether a character in an
- * URL must be escaped. The same criterion must be used in strlen_url()
- * and strcpy_url().
- */
-static bool urlchar_needs_escaping(int c)
-{
-    return !(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c));
-}
-
-/*
- * strlen_url() returns the length of the given URL if the spaces within the
- * URL were properly URL encoded.
- * URL encoding should be skipped for host names, otherwise IDN resolution
- * will fail.
- */
-static size_t strlen_url(const char *url, bool relative)
-{
-  const unsigned char *ptr;
-  size_t newlen = 0;
-  bool left = TRUE; /* left side of the ? */
-  const unsigned char *host_sep = (const unsigned char *) url;
-
-  if(!relative)
-    host_sep = (const unsigned char *) find_host_sep(url);
-
-  for(ptr = (unsigned char *)url; *ptr; ptr++) {
-
-    if(ptr < host_sep) {
-      ++newlen;
-      continue;
-    }
-
-    switch(*ptr) {
-    case '?':
-      left = FALSE;
-      /* FALLTHROUGH */
-    default:
-      if(urlchar_needs_escaping(*ptr))
-        newlen += 2;
-      newlen++;
-      break;
-    case ' ':
-      if(left)
-        newlen += 3;
-      else
-        newlen++;
-      break;
-    }
-  }
-  return newlen;
-}
-
-/* strcpy_url() copies a url to a output buffer and URL-encodes the spaces in
- * the source URL accordingly.
- * URL encoding should be skipped for host names, otherwise IDN resolution
- * will fail.
- */
-static void strcpy_url(char *output, const char *url, bool relative)
-{
-  /* we must add this with whitespace-replacing */
-  bool left = TRUE;
-  const unsigned char *iptr;
-  char *optr = output;
-  const unsigned char *host_sep = (const unsigned char *) url;
-
-  if(!relative)
-    host_sep = (const unsigned char *) find_host_sep(url);
-
-  for(iptr = (unsigned char *)url;    /* read from here */
-      *iptr;         /* until zero byte */
-      iptr++) {
-
-    if(iptr < host_sep) {
-      *optr++ = *iptr;
-      continue;
-    }
-
-    switch(*iptr) {
-    case '?':
-      left = FALSE;
-      /* FALLTHROUGH */
-    default:
-      if(urlchar_needs_escaping(*iptr)) {
-        snprintf(optr, 4, "%%%02x", *iptr);
-        optr += 3;
-      }
-      else
-        *optr++=*iptr;
-      break;
-    case ' ':
-      if(left) {
-        *optr++='%'; /* add a '%' */
-        *optr++='2'; /* add a '2' */
-        *optr++='0'; /* add a '0' */
-      }
-      else
-        *optr++='+'; /* add a '+' here */
-      break;
-    }
-  }
-  *optr = 0; /* zero terminate output buffer */
-
-}
-
-/*
- * Returns true if the given URL is absolute (as opposed to relative)
- */
-static bool is_absolute_url(const char *url)
-{
-  char prot[16]; /* URL protocol string storage */
-  char letter;   /* used for a silly sscanf */
-
-  return (2 == sscanf(url, "%15[^?&/:]://%c", prot, &letter)) ? TRUE : FALSE;
-}
-
-/*
- * Concatenate a relative URL to a base URL making it absolute.
- * URL-encodes any spaces.
- * The returned pointer must be freed by the caller unless NULL
- * (returns NULL on out of memory).
- */
-static char *concat_url(const char *base, const char *relurl)
-{
-  /***
-   TRY to append this new path to the old URL
-   to the right of the host part. Oh crap, this is doomed to cause
-   problems in the future...
-  */
-  char *newest;
-  char *protsep;
-  char *pathsep;
-  size_t newlen;
-  bool host_changed = FALSE;
-
-  const char *useurl = relurl;
-  size_t urllen;
-
-  /* we must make our own copy of the URL to play with, as it may
-     point to read-only data */
-  char *url_clone = strdup(base);
-
-  if(!url_clone)
-    return NULL; /* skip out of this NOW */
-
-  /* protsep points to the start of the host name */
-  protsep = strstr(url_clone, "//");
-  if(!protsep)
-    protsep = url_clone;
-  else
-    protsep += 2; /* pass the slashes */
-
-  if('/' != relurl[0]) {
-    int level = 0;
-
-    /* First we need to find out if there's a ?-letter in the URL,
-       and cut it and the right-side of that off */
-    pathsep = strchr(protsep, '?');
-    if(pathsep)
-      *pathsep = 0;
-
-    /* we have a relative path to append to the last slash if there's one
-       available, or if the new URL is just a query string (starts with a
-       '?')  we append the new one at the end of the entire currently worked
-       out URL */
-    if(useurl[0] != '?') {
-      pathsep = strrchr(protsep, '/');
-      if(pathsep)
-        *pathsep = 0;
-    }
-
-    /* Check if there's any slash after the host name, and if so, remember
-       that position instead */
-    pathsep = strchr(protsep, '/');
-    if(pathsep)
-      protsep = pathsep + 1;
-    else
-      protsep = NULL;
-
-    /* now deal with one "./" or any amount of "../" in the newurl
-       and act accordingly */
-
-    if((useurl[0] == '.') && (useurl[1] == '/'))
-      useurl += 2; /* just skip the "./" */
-
-    while((useurl[0] == '.') &&
-          (useurl[1] == '.') &&
-          (useurl[2] == '/')) {
-      level++;
-      useurl += 3; /* pass the "../" */
-    }
-
-    if(protsep) {
-      while(level--) {
-        /* cut off one more level from the right of the original URL */
-        pathsep = strrchr(protsep, '/');
-        if(pathsep)
-          *pathsep = 0;
-        else {
-          *protsep = 0;
-          break;
-        }
-      }
-    }
-  }
-  else {
-    /* We got a new absolute path for this server */
-
-    if((relurl[0] == '/') && (relurl[1] == '/')) {
-      /* the new URL starts with //, just keep the protocol part from the
-         original one */
-      *protsep = 0;
-      useurl = &relurl[2]; /* we keep the slashes from the original, so we
-                              skip the new ones */
-      host_changed = TRUE;
-    }
-    else {
-      /* cut off the original URL from the first slash, or deal with URLs
-         without slash */
-      pathsep = strchr(protsep, '/');
-      if(pathsep) {
-        /* When people use badly formatted URLs, such as
-           "http://www.url.com?dir=/home/daniel" we must not use the first
-           slash, if there's a ?-letter before it! */
-        char *sep = strchr(protsep, '?');
-        if(sep && (sep < pathsep))
-          pathsep = sep;
-        *pathsep = 0;
-      }
-      else {
-        /* There was no slash. Now, since we might be operating on a badly
-           formatted URL, such as "http://www.url.com?id=2380" which doesn't
-           use a slash separator as it is supposed to, we need to check for a
-           ?-letter as well! */
-        pathsep = strchr(protsep, '?');
-        if(pathsep)
-          *pathsep = 0;
-      }
-    }
-  }
-
-  /* If the new part contains a space, this is a mighty stupid redirect
-     but we still make an effort to do "right". To the left of a '?'
-     letter we replace each space with %20 while it is replaced with '+'
-     on the right side of the '?' letter.
-  */
-  newlen = strlen_url(useurl, !host_changed);
-
-  urllen = strlen(url_clone);
-
-  newest = malloc(urllen + 1 + /* possible slash */
-                  newlen + 1 /* zero byte */);
-
-  if(!newest) {
-    free(url_clone); /* don't leak this */
-    return NULL;
-  }
-
-  /* copy over the root url part */
-  memcpy(newest, url_clone, urllen);
-
-  /* check if we need to append a slash */
-  if(('/' == useurl[0]) || (protsep && !*protsep) || ('?' == useurl[0]))
-    ;
-  else
-    newest[urllen++]='/';
-
-  /* then append the new piece on the right side */
-  strcpy_url(&newest[urllen], useurl, !host_changed);
-
-  free(url_clone);
-
-  return newest;
-}
-#endif /* CURL_DISABLE_HTTP */
-
 /*
 /*
  * Curl_follow() handles the URL redirect magic. Pass in the 'newurl' string
  * Curl_follow() handles the URL redirect magic. Pass in the 'newurl' string
  * as given by the remote server and set up the new URL to request.
  * as given by the remote server and set up the new URL to request.
+ *
+ * This function DOES NOT FREE the given url.
  */
  */
 CURLcode Curl_follow(struct Curl_easy *data,
 CURLcode Curl_follow(struct Curl_easy *data,
                      char *newurl,    /* the Location: string */
                      char *newurl,    /* the Location: string */
@@ -1777,6 +1476,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
   /* Location: redirect */
   /* Location: redirect */
   bool disallowport = FALSE;
   bool disallowport = FALSE;
   bool reachedmax = FALSE;
   bool reachedmax = FALSE;
+  CURLUcode uc;
 
 
   if(type == FOLLOW_REDIR) {
   if(type == FOLLOW_REDIR) {
     if((data->set.maxredirs != -1) &&
     if((data->set.maxredirs != -1) &&
@@ -1809,33 +1509,18 @@ CURLcode Curl_follow(struct Curl_easy *data,
     }
     }
   }
   }
 
 
-  if(!is_absolute_url(newurl)) {
-    /***
-     *DANG* this is an RFC 2068 violation. The URL is supposed
-     to be absolute and this doesn't seem to be that!
-     */
-    char *absolute = concat_url(data->change.url, newurl);
-    if(!absolute)
-      return CURLE_OUT_OF_MEMORY;
-    newurl = absolute;
-  }
-  else {
-    /* The new URL MAY contain space or high byte values, that means a mighty
-       stupid redirect URL but we still make an effort to do "right". */
-    char *newest;
-    size_t newlen = strlen_url(newurl, FALSE);
-
+  if(Curl_is_absolute_url(newurl, NULL, MAX_SCHEME_LEN))
     /* This is an absolute URL, don't allow the custom port number */
     /* This is an absolute URL, don't allow the custom port number */
     disallowport = TRUE;
     disallowport = TRUE;
 
 
-    newest = malloc(newlen + 1); /* get memory for this */
-    if(!newest)
-      return CURLE_OUT_OF_MEMORY;
-
-    strcpy_url(newest, newurl, FALSE); /* create a space-free URL */
-    newurl = newest; /* use this instead now */
+  DEBUGASSERT(data->state.uh);
+  uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, 0);
+  if(uc)
+    return Curl_uc_to_curlcode(uc);
 
 
-  }
+  uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0);
+  if(uc)
+    return Curl_uc_to_curlcode(uc);
 
 
   if(type == FOLLOW_FAKE) {
   if(type == FOLLOW_FAKE) {
     /* we're only figuring out the new url if we would've followed locations
     /* we're only figuring out the new url if we would've followed locations
@@ -1852,10 +1537,8 @@ CURLcode Curl_follow(struct Curl_easy *data,
   if(disallowport)
   if(disallowport)
     data->state.allow_port = FALSE;
     data->state.allow_port = FALSE;
 
 
-  if(data->change.url_alloc) {
+  if(data->change.url_alloc)
     Curl_safefree(data->change.url);
     Curl_safefree(data->change.url);
-    data->change.url_alloc = FALSE;
-  }
 
 
   data->change.url = newurl;
   data->change.url = newurl;
   data->change.url_alloc = TRUE;
   data->change.url_alloc = TRUE;
@@ -2021,8 +1704,13 @@ CURLcode Curl_retry_request(struct connectdata *conn,
 
 
     if(conn->handler->protocol&PROTO_FAMILY_HTTP) {
     if(conn->handler->protocol&PROTO_FAMILY_HTTP) {
       struct HTTP *http = data->req.protop;
       struct HTTP *http = data->req.protop;
-      if(http->writebytecount)
-        return Curl_readrewind(conn);
+      if(http->writebytecount) {
+        CURLcode result = Curl_readrewind(conn);
+        if(result) {
+          Curl_safefree(*url);
+          return result;
+        }
+      }
     }
     }
   }
   }
   return CURLE_OK;
   return CURLE_OK;

+ 0 - 1
lib/transfer.h

@@ -71,4 +71,3 @@ Curl_setup_transfer (struct connectdata *data,
 );
 );
 
 
 #endif /* HEADER_CURL_TRANSFER_H */
 #endif /* HEADER_CURL_TRANSFER_H */
-

File diff suppressed because it is too large
+ 180 - 654
lib/url.c


+ 20 - 5
lib/url.h

@@ -27,6 +27,18 @@
 #define READBUFFER_MAX  CURL_MAX_READ_SIZE
 #define READBUFFER_MAX  CURL_MAX_READ_SIZE
 #define READBUFFER_MIN  1024
 #define READBUFFER_MIN  1024
 
 
+/* The default upload buffer size, should not be smaller than
+   CURL_MAX_WRITE_SIZE, as it needs to hold a full buffer as could be sent in
+   a write callback.
+
+   The size was 16KB for many years but was bumped to 64KB because it makes
+   libcurl able to do significantly faster uploads in some circumstances. Even
+   larger buffers can help further, but this is deemed a fair memory/speed
+   compromise. */
+#define UPLOADBUFFER_DEFAULT 65536
+#define UPLOADBUFFER_MAX (2*1024*1024)
+#define UPLOADBUFFER_MIN CURL_MAX_WRITE_SIZE
+
 /*
 /*
  * Prototypes for library-wide functions provided by url.c
  * Prototypes for library-wide functions provided by url.c
  */
  */
@@ -34,8 +46,11 @@
 CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn);
 CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn);
 CURLcode Curl_open(struct Curl_easy **curl);
 CURLcode Curl_open(struct Curl_easy **curl);
 CURLcode Curl_init_userdefined(struct Curl_easy *data);
 CURLcode Curl_init_userdefined(struct Curl_easy *data);
-CURLcode Curl_dupset(struct Curl_easy * dst, struct Curl_easy * src);
+
 void Curl_freeset(struct Curl_easy * data);
 void Curl_freeset(struct Curl_easy * data);
+/* free the URL pieces */
+void Curl_up_free(struct Curl_easy *data);
+CURLcode Curl_uc_to_curlcode(CURLUcode uc);
 CURLcode Curl_close(struct Curl_easy *data); /* opposite of curl_open() */
 CURLcode Curl_close(struct Curl_easy *data); /* opposite of curl_open() */
 CURLcode Curl_connect(struct Curl_easy *, struct connectdata **,
 CURLcode Curl_connect(struct Curl_easy *, struct connectdata **,
                       bool *async, bool *protocol_connect);
                       bool *async, bool *protocol_connect);
@@ -57,9 +72,7 @@ int Curl_doing_getsock(struct connectdata *conn,
 CURLcode Curl_parse_login_details(const char *login, const size_t len,
 CURLcode Curl_parse_login_details(const char *login, const size_t len,
                                   char **userptr, char **passwdptr,
                                   char **userptr, char **passwdptr,
                                   char **optionsptr);
                                   char **optionsptr);
-bool Curl_isPipeliningEnabled(const struct Curl_easy *handle);
-CURLcode Curl_addHandleToPipeline(struct Curl_easy *handle,
-                                  struct curl_llist *pipeline);
+
 int Curl_removeHandleFromPipeline(struct Curl_easy *handle,
 int Curl_removeHandleFromPipeline(struct Curl_easy *handle,
                                   struct curl_llist *pipeline);
                                   struct curl_llist *pipeline);
 /* remove the specified connection from all (possible) pipelines and related
 /* remove the specified connection from all (possible) pipelines and related
@@ -67,7 +80,9 @@ int Curl_removeHandleFromPipeline(struct Curl_easy *handle,
 void Curl_getoff_all_pipelines(struct Curl_easy *data,
 void Curl_getoff_all_pipelines(struct Curl_easy *data,
                                struct connectdata *conn);
                                struct connectdata *conn);
 
 
-void Curl_close_connections(struct Curl_easy *data);
+CURLcode Curl_upkeep(struct conncache *conn_cache, void *data);
+
+const struct Curl_handler *Curl_builtin_scheme(const char *scheme);
 
 
 #define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */
 #define CURL_DEFAULT_PROXY_PORT 1080 /* default proxy port unless specified */
 #define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless
 #define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless

+ 33 - 0
lib/urlapi-int.h

@@ -0,0 +1,33 @@
+#ifndef HEADER_CURL_URLAPI_INT_H
+#define HEADER_CURL_URLAPI_INT_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+/* scheme is not URL encoded, the longest libcurl supported ones are 6
+   letters */
+#define MAX_SCHEME_LEN 8
+
+bool Curl_is_absolute_url(const char *url, char *scheme, size_t buflen);
+char *Curl_concat_url(const char *base, const char *relurl);
+size_t Curl_strlen_url(const char *url, bool relative);
+void Curl_strcpy_url(char *output, const char *url, bool relative);
+#endif /* HEADER_CURL_URLAPI_INT_H */

+ 1340 - 0
lib/urlapi.c

@@ -0,0 +1,1340 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include "urldata.h"
+#include "urlapi-int.h"
+#include "strcase.h"
+#include "dotdot.h"
+#include "url.h"
+#include "escape.h"
+#include "curl_ctype.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+  /* MSDOS/Windows style drive prefix, eg c: in c:foo */
+#define STARTS_WITH_DRIVE_PREFIX(str) \
+  ((('a' <= str[0] && str[0] <= 'z') || \
+    ('A' <= str[0] && str[0] <= 'Z')) && \
+   (str[1] == ':'))
+
+  /* MSDOS/Windows style drive prefix, optionally with
+   * a '|' instead of ':', followed by a slash or NUL */
+#define STARTS_WITH_URL_DRIVE_PREFIX(str) \
+  ((('a' <= (str)[0] && (str)[0] <= 'z') || \
+    ('A' <= (str)[0] && (str)[0] <= 'Z')) && \
+   ((str)[1] == ':' || (str)[1] == '|') && \
+   ((str)[2] == '/' || (str)[2] == '\\' || (str)[2] == 0))
+
+/* Internal representation of CURLU. Point to URL-encoded strings. */
+struct Curl_URL {
+  char *scheme;
+  char *user;
+  char *password;
+  char *options; /* IMAP only? */
+  char *host;
+  char *port;
+  char *path;
+  char *query;
+  char *fragment;
+
+  char *scratch; /* temporary scratch area */
+  long portnum; /* the numerical version */
+};
+
+#define DEFAULT_SCHEME "https"
+
+static void free_urlhandle(struct Curl_URL *u)
+{
+  free(u->scheme);
+  free(u->user);
+  free(u->password);
+  free(u->options);
+  free(u->host);
+  free(u->port);
+  free(u->path);
+  free(u->query);
+  free(u->fragment);
+  free(u->scratch);
+}
+
+/* move the full contents of one handle onto another and
+   free the original */
+static void mv_urlhandle(struct Curl_URL *from,
+                         struct Curl_URL *to)
+{
+  free_urlhandle(to);
+  *to = *from;
+  free(from);
+}
+
+/*
+ * Find the separator at the end of the host name, or the '?' in cases like
+ * http://www.url.com?id=2380
+ */
+static const char *find_host_sep(const char *url)
+{
+  const char *sep;
+  const char *query;
+
+  /* Find the start of the hostname */
+  sep = strstr(url, "//");
+  if(!sep)
+    sep = url;
+  else
+    sep += 2;
+
+  query = strchr(sep, '?');
+  sep = strchr(sep, '/');
+
+  if(!sep)
+    sep = url + strlen(url);
+
+  if(!query)
+    query = url + strlen(url);
+
+  return sep < query ? sep : query;
+}
+
+/*
+ * Decide in an encoding-independent manner whether a character in an
+ * URL must be escaped. The same criterion must be used in strlen_url()
+ * and strcpy_url().
+ */
+static bool urlchar_needs_escaping(int c)
+{
+    return !(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c));
+}
+
+/*
+ * strlen_url() returns the length of the given URL if the spaces within the
+ * URL were properly URL encoded.
+ * URL encoding should be skipped for host names, otherwise IDN resolution
+ * will fail.
+ */
+size_t Curl_strlen_url(const char *url, bool relative)
+{
+  const unsigned char *ptr;
+  size_t newlen = 0;
+  bool left = TRUE; /* left side of the ? */
+  const unsigned char *host_sep = (const unsigned char *) url;
+
+  if(!relative)
+    host_sep = (const unsigned char *) find_host_sep(url);
+
+  for(ptr = (unsigned char *)url; *ptr; ptr++) {
+
+    if(ptr < host_sep) {
+      ++newlen;
+      continue;
+    }
+
+    switch(*ptr) {
+    case '?':
+      left = FALSE;
+      /* FALLTHROUGH */
+    default:
+      if(urlchar_needs_escaping(*ptr))
+        newlen += 2;
+      newlen++;
+      break;
+    case ' ':
+      if(left)
+        newlen += 3;
+      else
+        newlen++;
+      break;
+    }
+  }
+  return newlen;
+}
+
+/* strcpy_url() copies a url to a output buffer and URL-encodes the spaces in
+ * the source URL accordingly.
+ * URL encoding should be skipped for host names, otherwise IDN resolution
+ * will fail.
+ */
+void Curl_strcpy_url(char *output, const char *url, bool relative)
+{
+  /* we must add this with whitespace-replacing */
+  bool left = TRUE;
+  const unsigned char *iptr;
+  char *optr = output;
+  const unsigned char *host_sep = (const unsigned char *) url;
+
+  if(!relative)
+    host_sep = (const unsigned char *) find_host_sep(url);
+
+  for(iptr = (unsigned char *)url;    /* read from here */
+      *iptr;         /* until zero byte */
+      iptr++) {
+
+    if(iptr < host_sep) {
+      *optr++ = *iptr;
+      continue;
+    }
+
+    switch(*iptr) {
+    case '?':
+      left = FALSE;
+      /* FALLTHROUGH */
+    default:
+      if(urlchar_needs_escaping(*iptr)) {
+        snprintf(optr, 4, "%%%02x", *iptr);
+        optr += 3;
+      }
+      else
+        *optr++=*iptr;
+      break;
+    case ' ':
+      if(left) {
+        *optr++='%'; /* add a '%' */
+        *optr++='2'; /* add a '2' */
+        *optr++='0'; /* add a '0' */
+      }
+      else
+        *optr++='+'; /* add a '+' here */
+      break;
+    }
+  }
+  *optr = 0; /* zero terminate output buffer */
+
+}
+
+/*
+ * Returns true if the given URL is absolute (as opposed to relative) within
+ * the buffer size. Returns the scheme in the buffer if TRUE and 'buf' is
+ * non-NULL.
+ */
+bool Curl_is_absolute_url(const char *url, char *buf, size_t buflen)
+{
+  size_t i;
+#ifdef WIN32
+  if(STARTS_WITH_DRIVE_PREFIX(url))
+    return FALSE;
+#endif
+  for(i = 0; i < buflen && url[i]; ++i) {
+    char s = url[i];
+    if(s == ':') {
+      if(buf)
+        buf[i] = 0;
+      return TRUE;
+    }
+    /* RFC 3986 3.1 explains:
+      scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+    */
+    else if(ISALNUM(s) || (s == '+') || (s == '-') || (s == '.') ) {
+      if(buf)
+        buf[i] = (char)TOLOWER(s);
+    }
+    else
+      break;
+  }
+  return FALSE;
+}
+
+/*
+ * Concatenate a relative URL to a base URL making it absolute.
+ * URL-encodes any spaces.
+ * The returned pointer must be freed by the caller unless NULL
+ * (returns NULL on out of memory).
+ */
+char *Curl_concat_url(const char *base, const char *relurl)
+{
+  /***
+   TRY to append this new path to the old URL
+   to the right of the host part. Oh crap, this is doomed to cause
+   problems in the future...
+  */
+  char *newest;
+  char *protsep;
+  char *pathsep;
+  size_t newlen;
+  bool host_changed = FALSE;
+
+  const char *useurl = relurl;
+  size_t urllen;
+
+  /* we must make our own copy of the URL to play with, as it may
+     point to read-only data */
+  char *url_clone = strdup(base);
+
+  if(!url_clone)
+    return NULL; /* skip out of this NOW */
+
+  /* protsep points to the start of the host name */
+  protsep = strstr(url_clone, "//");
+  if(!protsep)
+    protsep = url_clone;
+  else
+    protsep += 2; /* pass the slashes */
+
+  if('/' != relurl[0]) {
+    int level = 0;
+
+    /* First we need to find out if there's a ?-letter in the URL,
+       and cut it and the right-side of that off */
+    pathsep = strchr(protsep, '?');
+    if(pathsep)
+      *pathsep = 0;
+
+    /* we have a relative path to append to the last slash if there's one
+       available, or if the new URL is just a query string (starts with a
+       '?')  we append the new one at the end of the entire currently worked
+       out URL */
+    if(useurl[0] != '?') {
+      pathsep = strrchr(protsep, '/');
+      if(pathsep)
+        *pathsep = 0;
+    }
+
+    /* Check if there's any slash after the host name, and if so, remember
+       that position instead */
+    pathsep = strchr(protsep, '/');
+    if(pathsep)
+      protsep = pathsep + 1;
+    else
+      protsep = NULL;
+
+    /* now deal with one "./" or any amount of "../" in the newurl
+       and act accordingly */
+
+    if((useurl[0] == '.') && (useurl[1] == '/'))
+      useurl += 2; /* just skip the "./" */
+
+    while((useurl[0] == '.') &&
+          (useurl[1] == '.') &&
+          (useurl[2] == '/')) {
+      level++;
+      useurl += 3; /* pass the "../" */
+    }
+
+    if(protsep) {
+      while(level--) {
+        /* cut off one more level from the right of the original URL */
+        pathsep = strrchr(protsep, '/');
+        if(pathsep)
+          *pathsep = 0;
+        else {
+          *protsep = 0;
+          break;
+        }
+      }
+    }
+  }
+  else {
+    /* We got a new absolute path for this server */
+
+    if((relurl[0] == '/') && (relurl[1] == '/')) {
+      /* the new URL starts with //, just keep the protocol part from the
+         original one */
+      *protsep = 0;
+      useurl = &relurl[2]; /* we keep the slashes from the original, so we
+                              skip the new ones */
+      host_changed = TRUE;
+    }
+    else {
+      /* cut off the original URL from the first slash, or deal with URLs
+         without slash */
+      pathsep = strchr(protsep, '/');
+      if(pathsep) {
+        /* When people use badly formatted URLs, such as
+           "http://www.url.com?dir=/home/daniel" we must not use the first
+           slash, if there's a ?-letter before it! */
+        char *sep = strchr(protsep, '?');
+        if(sep && (sep < pathsep))
+          pathsep = sep;
+        *pathsep = 0;
+      }
+      else {
+        /* There was no slash. Now, since we might be operating on a badly
+           formatted URL, such as "http://www.url.com?id=2380" which doesn't
+           use a slash separator as it is supposed to, we need to check for a
+           ?-letter as well! */
+        pathsep = strchr(protsep, '?');
+        if(pathsep)
+          *pathsep = 0;
+      }
+    }
+  }
+
+  /* If the new part contains a space, this is a mighty stupid redirect
+     but we still make an effort to do "right". To the left of a '?'
+     letter we replace each space with %20 while it is replaced with '+'
+     on the right side of the '?' letter.
+  */
+  newlen = Curl_strlen_url(useurl, !host_changed);
+
+  urllen = strlen(url_clone);
+
+  newest = malloc(urllen + 1 + /* possible slash */
+                  newlen + 1 /* zero byte */);
+
+  if(!newest) {
+    free(url_clone); /* don't leak this */
+    return NULL;
+  }
+
+  /* copy over the root url part */
+  memcpy(newest, url_clone, urllen);
+
+  /* check if we need to append a slash */
+  if(('/' == useurl[0]) || (protsep && !*protsep) || ('?' == useurl[0]))
+    ;
+  else
+    newest[urllen++]='/';
+
+  /* then append the new piece on the right side */
+  Curl_strcpy_url(&newest[urllen], useurl, !host_changed);
+
+  free(url_clone);
+
+  return newest;
+}
+
+/*
+ * parse_hostname_login()
+ *
+ * Parse the login details (user name, password and options) from the URL and
+ * strip them out of the host name
+ *
+ */
+static CURLUcode parse_hostname_login(struct Curl_URL *u,
+                                      const struct Curl_handler *h,
+                                      char **hostname,
+                                      unsigned int flags)
+{
+  CURLUcode result = CURLUE_OK;
+  CURLcode ccode;
+  char *userp = NULL;
+  char *passwdp = NULL;
+  char *optionsp = NULL;
+
+  /* At this point, we're hoping all the other special cases have
+   * been taken care of, so conn->host.name is at most
+   *    [user[:password][;options]]@]hostname
+   *
+   * We need somewhere to put the embedded details, so do that first.
+   */
+
+  char *ptr = strchr(*hostname, '@');
+  char *login = *hostname;
+
+  if(!ptr)
+    goto out;
+
+  /* We will now try to extract the
+   * possible login information in a string like:
+   * ftp://user:[email protected]:8021/README */
+  *hostname = ++ptr;
+
+  /* We could use the login information in the URL so extract it. Only parse
+     options if the handler says we should. Note that 'h' might be NULL! */
+  ccode = Curl_parse_login_details(login, ptr - login - 1,
+                                   &userp, &passwdp,
+                                   (h && (h->flags & PROTOPT_URLOPTIONS)) ?
+                                   &optionsp:NULL);
+  if(ccode) {
+    result = CURLUE_MALFORMED_INPUT;
+    goto out;
+  }
+
+  if(userp) {
+    if(flags & CURLU_DISALLOW_USER) {
+      /* Option DISALLOW_USER is set and url contains username. */
+      result = CURLUE_USER_NOT_ALLOWED;
+      goto out;
+    }
+
+    u->user = userp;
+  }
+
+  if(passwdp)
+    u->password = passwdp;
+
+  if(optionsp)
+    u->options = optionsp;
+
+  return CURLUE_OK;
+  out:
+
+  free(userp);
+  free(passwdp);
+  free(optionsp);
+
+  return result;
+}
+
+static CURLUcode parse_port(struct Curl_URL *u, char *hostname)
+{
+  char *portptr;
+  char endbracket;
+  int len;
+
+  if((1 == sscanf(hostname, "[%*45[0123456789abcdefABCDEF:.%%]%c%n",
+                  &endbracket, &len)) &&
+     (']' == endbracket)) {
+    /* this is a RFC2732-style specified IP-address */
+    portptr = &hostname[len];
+    if (*portptr != ':')
+      return CURLUE_MALFORMED_INPUT;
+  }
+  else
+    portptr = strchr(hostname, ':');
+
+  if(portptr) {
+    char *rest;
+    long port;
+    char portbuf[7];
+
+    if(!ISDIGIT(portptr[1]))
+      return CURLUE_BAD_PORT_NUMBER;
+
+    port = strtol(portptr + 1, &rest, 10);  /* Port number must be decimal */
+
+    if((port <= 0) || (port > 0xffff))
+      /* Single unix standard says port numbers are 16 bits long, but we don't
+         treat port zero as OK. */
+      return CURLUE_BAD_PORT_NUMBER;
+
+    if(rest[0])
+      return CURLUE_BAD_PORT_NUMBER;
+
+    if(rest != &portptr[1]) {
+      *portptr++ = '\0'; /* cut off the name there */
+      *rest = 0;
+      /* generate a new to get rid of leading zeroes etc */
+      snprintf(portbuf, sizeof(portbuf), "%ld", port);
+      u->portnum = port;
+      u->port = strdup(portbuf);
+      if(!u->port)
+        return CURLUE_OUT_OF_MEMORY;
+    }
+    else {
+      /* Browser behavior adaptation. If there's a colon with no digits after,
+         just cut off the name there which makes us ignore the colon and just
+         use the default port. Firefox and Chrome both do that. */
+      *portptr = '\0';
+    }
+  }
+
+  return CURLUE_OK;
+}
+
+/* scan for byte values < 31 or 127 */
+static CURLUcode junkscan(char *part)
+{
+  char badbytes[]={
+    /* */ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    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 /* zero terminate */
+  };
+  if(part) {
+    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 CURLUE_OK;
+}
+
+static CURLUcode hostname_check(char *hostname, unsigned int flags)
+{
+  const char *l = NULL; /* accepted characters */
+  size_t len;
+  size_t hlen = strlen(hostname);
+  (void)flags;
+
+  if(hostname[0] == '[') {
+    hostname++;
+    l = "0123456789abcdefABCDEF::.%";
+    hlen -= 2;
+  }
+
+  if(l) {
+    /* only valid letters are ok */
+    len = strspn(hostname, l);
+    if(hlen != len)
+      /* hostname with bad content */
+      return CURLUE_MALFORMED_INPUT;
+  }
+  else {
+    /* letters from the second string is not ok */
+    len = strcspn(hostname, " ");
+    if(hlen != len)
+      /* hostname with bad content */
+      return CURLUE_MALFORMED_INPUT;
+  }
+  return CURLUE_OK;
+}
+
+#define HOSTNAME_END(x) (((x) == '/') || ((x) == '?') || ((x) == '#'))
+
+static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
+{
+  char *path;
+  bool path_alloced = FALSE;
+  char *hostname;
+  char *query = NULL;
+  char *fragment = NULL;
+  CURLUcode result;
+  bool url_has_scheme = FALSE;
+  char schemebuf[MAX_SCHEME_LEN];
+  char *schemep = NULL;
+  size_t schemelen = 0;
+  size_t urllen;
+  const struct Curl_handler *h = NULL;
+
+  if(!url)
+    return CURLUE_MALFORMED_INPUT;
+
+  /*************************************************************
+   * Parse the URL.
+   ************************************************************/
+  /* allocate scratch area */
+  urllen = strlen(url);
+  path = u->scratch = malloc(urllen * 2 + 2);
+  if(!path)
+    return CURLUE_OUT_OF_MEMORY;
+
+  hostname = &path[urllen + 1];
+  hostname[0] = 0;
+
+  if(Curl_is_absolute_url(url, schemebuf, sizeof(schemebuf))) {
+    url_has_scheme = TRUE;
+    schemelen = strlen(schemebuf);
+  }
+
+  /* handle the file: scheme */
+  if(url_has_scheme && strcasecompare(schemebuf, "file")) {
+    /* path has been allocated large enough to hold this */
+    strcpy(path, &url[5]);
+
+    hostname = NULL; /* no host for file: URLs */
+    u->scheme = strdup("file");
+    if(!u->scheme)
+      return CURLUE_OUT_OF_MEMORY;
+
+    /* Extra handling URLs with an authority component (i.e. that start with
+     * "file://")
+     *
+     * We allow omitted hostname (e.g. file:/<path>) -- valid according to
+     * RFC 8089, but not the (current) WHAT-WG URL spec.
+     */
+    if(path[0] == '/' && path[1] == '/') {
+      /* swallow the two slashes */
+      char *ptr = &path[2];
+
+      /*
+       * According to RFC 8089, a file: URL can be reliably dereferenced if:
+       *
+       *  o it has no/blank hostname, or
+       *
+       *  o the hostname matches "localhost" (case-insensitively), or
+       *
+       *  o the hostname is a FQDN that resolves to this machine.
+       *
+       * For brevity, we only consider URLs with empty, "localhost", or
+       * "127.0.0.1" hostnames as local.
+       *
+       * Additionally, there is an exception for URLs with a Windows drive
+       * letter in the authority (which was accidentally omitted from RFC 8089
+       * Appendix E, but believe me, it was meant to be there. --MK)
+       */
+      if(ptr[0] != '/' && !STARTS_WITH_URL_DRIVE_PREFIX(ptr)) {
+        /* the URL includes a host name, it must match "localhost" or
+           "127.0.0.1" to be valid */
+        if(!checkprefix("localhost/", ptr) &&
+           !checkprefix("127.0.0.1/", ptr)) {
+          /* Invalid file://hostname/, expected localhost or 127.0.0.1 or
+             none */
+          return CURLUE_MALFORMED_INPUT;
+        }
+        ptr += 9; /* now points to the slash after the host */
+      }
+
+      path = ptr;
+    }
+
+#if !defined(MSDOS) && !defined(WIN32) && !defined(__CYGWIN__)
+    /* Don't allow Windows drive letters when not in Windows.
+     * This catches both "file:/c:" and "file:c:" */
+    if(('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) ||
+       STARTS_WITH_URL_DRIVE_PREFIX(path)) {
+      /* File drive letters are only accepted in MSDOS/Windows */
+      return CURLUE_MALFORMED_INPUT;
+    }
+#else
+    /* If the path starts with a slash and a drive letter, ditch the slash */
+    if('/' == path[0] && STARTS_WITH_URL_DRIVE_PREFIX(&path[1])) {
+      /* This cannot be done with strcpy, as the memory chunks overlap! */
+      memmove(path, &path[1], strlen(&path[1]) + 1);
+    }
+#endif
+
+  }
+  else {
+    /* clear path */
+    const char *p;
+    const char *hostp;
+    size_t len;
+    path[0] = 0;
+
+    if(url_has_scheme) {
+      int i = 0;
+      p = &url[schemelen + 1];
+      while(p && (*p == '/') && (i < 4)) {
+        p++;
+        i++;
+      }
+      if((i < 1) || (i>3))
+        /* less than one or more than three slashes */
+        return CURLUE_MALFORMED_INPUT;
+
+      schemep = schemebuf;
+      if(!Curl_builtin_scheme(schemep) &&
+         !(flags & CURLU_NON_SUPPORT_SCHEME))
+        return CURLUE_UNSUPPORTED_SCHEME;
+
+      if(junkscan(schemep))
+        return CURLUE_MALFORMED_INPUT;
+    }
+    else {
+      /* no scheme! */
+
+      if(!(flags & (CURLU_DEFAULT_SCHEME|CURLU_GUESS_SCHEME)))
+        return CURLUE_MALFORMED_INPUT;
+      if(flags & CURLU_DEFAULT_SCHEME)
+        schemep = (char *) DEFAULT_SCHEME;
+
+      /*
+       * The URL was badly formatted, let's try without scheme specified.
+       */
+      p = url;
+    }
+    hostp = p; /* host name starts here */
+
+    while(*p && !HOSTNAME_END(*p)) /* find end of host name */
+      p++;
+
+    len = p - hostp;
+    if(!len)
+      return CURLUE_MALFORMED_INPUT;
+
+    memcpy(hostname, hostp, len);
+    hostname[len] = 0;
+
+    if((flags & CURLU_GUESS_SCHEME) && !schemep) {
+      /* legacy curl-style guess based on host name */
+      if(checkprefix("ftp.", hostname))
+        schemep = (char *)"ftp";
+      else if(checkprefix("dict.", hostname))
+        schemep = (char *)"dict";
+      else if(checkprefix("ldap.", hostname))
+        schemep = (char *)"ldap";
+      else if(checkprefix("imap.", hostname))
+        schemep = (char *)"imap";
+      else if(checkprefix("smtp.", hostname))
+        schemep = (char *)"smtp";
+      else if(checkprefix("pop3.", hostname))
+        schemep = (char *)"pop3";
+      else
+        schemep = (char *)"http";
+    }
+
+    len = strlen(p);
+    memcpy(path, p, len);
+    path[len] = 0;
+
+    u->scheme = strdup(schemep);
+    if(!u->scheme)
+      return CURLUE_OUT_OF_MEMORY;
+  }
+
+  /* if this is a known scheme, get some details */
+  h = Curl_builtin_scheme(u->scheme);
+
+  if(junkscan(path))
+    return CURLUE_MALFORMED_INPUT;
+
+  query = strchr(path, '?');
+  if(query)
+    *query++ = 0;
+
+  fragment = strchr(query?query:path, '#');
+  if(fragment)
+    *fragment++ = 0;
+
+  if(!path[0])
+    /* if there's no path set, unset */
+    path = NULL;
+  else if(!(flags & CURLU_PATH_AS_IS)) {
+    /* sanitise paths and remove ../ and ./ sequences according to RFC3986 */
+    char *newp = Curl_dedotdotify(path);
+    if(!newp)
+      return CURLUE_OUT_OF_MEMORY;
+
+    if(strcmp(newp, path)) {
+      /* if we got a new version */
+      path = newp;
+      path_alloced = TRUE;
+    }
+    else
+      free(newp);
+  }
+  if(path) {
+    u->path = path_alloced?path:strdup(path);
+    if(!u->path)
+      return CURLUE_OUT_OF_MEMORY;
+  }
+
+  if(hostname) {
+    /*
+     * Parse the login details and strip them out of the host name.
+     */
+    if(junkscan(hostname))
+      return CURLUE_MALFORMED_INPUT;
+
+    result = parse_hostname_login(u, h, &hostname, flags);
+    if(result)
+      return result;
+
+    result = parse_port(u, hostname);
+    if(result)
+      return result;
+
+    result = hostname_check(hostname, flags);
+    if(result)
+      return result;
+
+    u->host = strdup(hostname);
+    if(!u->host)
+      return CURLUE_OUT_OF_MEMORY;
+  }
+
+  if(query && query[0]) {
+    u->query = strdup(query);
+    if(!u->query)
+      return CURLUE_OUT_OF_MEMORY;
+  }
+  if(fragment && fragment[0]) {
+    u->fragment = strdup(fragment);
+    if(!u->fragment)
+      return CURLUE_OUT_OF_MEMORY;
+  }
+
+  free(u->scratch);
+  u->scratch = NULL;
+
+  return CURLUE_OK;
+}
+
+/*
+ * Parse the URL and set the relevant members of the Curl_URL struct.
+ */
+static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
+{
+  CURLUcode result = seturl(url, u, flags);
+  if(result) {
+    free_urlhandle(u);
+    memset(u, 0, sizeof(struct Curl_URL));
+  }
+  return result;
+}
+
+/*
+ */
+CURLU *curl_url(void)
+{
+  return calloc(sizeof(struct Curl_URL), 1);
+}
+
+void curl_url_cleanup(CURLU *u)
+{
+  if(u) {
+    free_urlhandle(u);
+    free(u);
+  }
+}
+
+#define DUP(dest, src, name)         \
+  if(src->name) {                    \
+    dest->name = strdup(src->name);  \
+    if(!dest->name)                  \
+      goto fail;                     \
+  }
+
+CURLU *curl_url_dup(CURLU *in)
+{
+  struct Curl_URL *u = calloc(sizeof(struct Curl_URL), 1);
+  if(u) {
+    DUP(u, in, scheme);
+    DUP(u, in, user);
+    DUP(u, in, password);
+    DUP(u, in, options);
+    DUP(u, in, host);
+    DUP(u, in, port);
+    DUP(u, in, path);
+    DUP(u, in, query);
+    DUP(u, in, fragment);
+    u->portnum = in->portnum;
+  }
+  return u;
+  fail:
+  curl_url_cleanup(u);
+  return NULL;
+}
+
+CURLUcode curl_url_get(CURLU *u, CURLUPart what,
+                       char **part, unsigned int flags)
+{
+  char *ptr;
+  CURLUcode ifmissing = CURLUE_UNKNOWN_PART;
+  char portbuf[7];
+  bool urldecode = (flags & CURLU_URLDECODE)?1:0;
+  bool plusdecode = FALSE;
+  (void)flags;
+  if(!u)
+    return CURLUE_BAD_HANDLE;
+  if(!part)
+    return CURLUE_BAD_PARTPOINTER;
+  *part = NULL;
+
+  switch(what) {
+  case CURLUPART_SCHEME:
+    ptr = u->scheme;
+    ifmissing = CURLUE_NO_SCHEME;
+    urldecode = FALSE; /* never for schemes */
+    break;
+  case CURLUPART_USER:
+    ptr = u->user;
+    ifmissing = CURLUE_NO_USER;
+    break;
+  case CURLUPART_PASSWORD:
+    ptr = u->password;
+    ifmissing = CURLUE_NO_PASSWORD;
+    break;
+  case CURLUPART_OPTIONS:
+    ptr = u->options;
+    ifmissing = CURLUE_NO_OPTIONS;
+    break;
+  case CURLUPART_HOST:
+    ptr = u->host;
+    ifmissing = CURLUE_NO_HOST;
+    break;
+  case CURLUPART_PORT:
+    ptr = u->port;
+    ifmissing = CURLUE_NO_PORT;
+    urldecode = FALSE; /* never for port */
+    if(!ptr && (flags & CURLU_DEFAULT_PORT) && u->scheme) {
+      /* there's no stored port number, but asked to deliver
+         a default one for the scheme */
+      const struct Curl_handler *h =
+        Curl_builtin_scheme(u->scheme);
+      if(h) {
+        snprintf(portbuf, sizeof(portbuf), "%ld", h->defport);
+        ptr = portbuf;
+      }
+    }
+    else if(ptr && u->scheme) {
+      /* there is a stored port number, but ask to inhibit if
+         it matches the default one for the scheme */
+      const struct Curl_handler *h =
+        Curl_builtin_scheme(u->scheme);
+      if(h && (h->defport == u->portnum) &&
+         (flags & CURLU_NO_DEFAULT_PORT))
+        ptr = NULL;
+    }
+    break;
+  case CURLUPART_PATH:
+    ptr = u->path;
+    if(!ptr) {
+      ptr = u->path = strdup("/");
+      if(!u->path)
+        return CURLUE_OUT_OF_MEMORY;
+    }
+    break;
+  case CURLUPART_QUERY:
+    ptr = u->query;
+    ifmissing = CURLUE_NO_QUERY;
+    plusdecode = urldecode;
+    break;
+  case CURLUPART_FRAGMENT:
+    ptr = u->fragment;
+    ifmissing = CURLUE_NO_FRAGMENT;
+    break;
+  case CURLUPART_URL: {
+    char *url;
+    char *scheme;
+    char *options = u->options;
+    char *port = u->port;
+    if(u->scheme && strcasecompare("file", u->scheme)) {
+      url = aprintf("file://%s%s%s",
+                    u->path,
+                    u->fragment? "#": "",
+                    u->fragment? u->fragment : "");
+    }
+    else if(!u->host)
+      return CURLUE_NO_HOST;
+    else {
+      const struct Curl_handler *h = NULL;
+      if(u->scheme)
+        scheme = u->scheme;
+      else if(flags & CURLU_DEFAULT_SCHEME)
+        scheme = (char *) DEFAULT_SCHEME;
+      else
+        return CURLUE_NO_SCHEME;
+
+      if(scheme) {
+        h = Curl_builtin_scheme(scheme);
+        if(!port && (flags & CURLU_DEFAULT_PORT)) {
+          /* there's no stored port number, but asked to deliver
+             a default one for the scheme */
+          if(h) {
+            snprintf(portbuf, sizeof(portbuf), "%ld", h->defport);
+            port = portbuf;
+          }
+        }
+        else if(port) {
+          /* there is a stored port number, but asked to inhibit if it matches
+             the default one for the scheme */
+          if(h && (h->defport == u->portnum) &&
+             (flags & CURLU_NO_DEFAULT_PORT))
+            port = NULL;
+        }
+      }
+      if(h && !(h->flags & PROTOPT_URLOPTIONS))
+        options = NULL;
+
+      url = aprintf("%s://%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+                    scheme,
+                    u->user ? u->user : "",
+                    u->password ? ":": "",
+                    u->password ? u->password : "",
+                    options ? ";" : "",
+                    options ? options : "",
+                    (u->user || u->password || options) ? "@": "",
+                    u->host,
+                    port ? ":": "",
+                    port ? port : "",
+                    (u->path && (u->path[0] != '/')) ? "/": "",
+                    u->path ? u->path : "/",
+                    u->query? "?": "",
+                    u->query? u->query : "",
+                    u->fragment? "#": "",
+                    u->fragment? u->fragment : "");
+    }
+    if(!url)
+      return CURLUE_OUT_OF_MEMORY;
+    *part = url;
+    return CURLUE_OK;
+    break;
+  }
+  default:
+    ptr = NULL;
+  }
+  if(ptr) {
+    *part = strdup(ptr);
+    if(!*part)
+      return CURLUE_OUT_OF_MEMORY;
+    if(plusdecode) {
+      /* convert + to space */
+      char *plus;
+      for(plus = *part; *plus; ++plus) {
+        if(*plus == '+')
+          *plus = ' ';
+      }
+    }
+    if(urldecode) {
+      char *decoded;
+      size_t dlen;
+      CURLcode res = Curl_urldecode(NULL, *part, 0, &decoded, &dlen, TRUE);
+      free(*part);
+      if(res) {
+        *part = NULL;
+        return CURLUE_URLDECODE;
+      }
+      *part = decoded;
+    }
+    return CURLUE_OK;
+  }
+  else
+    return ifmissing;
+}
+
+CURLUcode curl_url_set(CURLU *u, CURLUPart what,
+                       const char *part, unsigned int flags)
+{
+  char **storep = NULL;
+  long port = 0;
+  bool urlencode = (flags & CURLU_URLENCODE)? 1 : 0;
+  bool plusencode = FALSE;
+  bool urlskipslash = FALSE;
+  bool appendquery = FALSE;
+
+  if(!u)
+    return CURLUE_BAD_HANDLE;
+  if(!part) {
+    /* setting a part to NULL clears it */
+    switch(what) {
+    case CURLUPART_URL:
+      break;
+    case CURLUPART_SCHEME:
+      storep = &u->scheme;
+      break;
+    case CURLUPART_USER:
+      storep = &u->user;
+      break;
+    case CURLUPART_PASSWORD:
+      storep = &u->password;
+      break;
+    case CURLUPART_OPTIONS:
+      storep = &u->options;
+      break;
+    case CURLUPART_HOST:
+      storep = &u->host;
+      break;
+    case CURLUPART_PORT:
+      storep = &u->port;
+      break;
+    case CURLUPART_PATH:
+      storep = &u->path;
+      break;
+    case CURLUPART_QUERY:
+      storep = &u->query;
+      break;
+    case CURLUPART_FRAGMENT:
+      storep = &u->fragment;
+      break;
+    default:
+      return CURLUE_UNKNOWN_PART;
+    }
+    if(storep && *storep) {
+      free(*storep);
+      *storep = NULL;
+    }
+    return CURLUE_OK;
+  }
+
+  switch(what) {
+  case CURLUPART_SCHEME:
+    if(!(flags & CURLU_NON_SUPPORT_SCHEME) &&
+       /* verify that it is a fine scheme */
+       !Curl_builtin_scheme(part))
+      return CURLUE_UNSUPPORTED_SCHEME;
+    storep = &u->scheme;
+    urlencode = FALSE; /* never */
+    break;
+  case CURLUPART_USER:
+    storep = &u->user;
+    break;
+  case CURLUPART_PASSWORD:
+    storep = &u->password;
+    break;
+  case CURLUPART_OPTIONS:
+    storep = &u->options;
+    break;
+  case CURLUPART_HOST:
+    storep = &u->host;
+    break;
+  case CURLUPART_PORT:
+    urlencode = FALSE; /* never */
+    port = strtol(part, NULL, 10);  /* Port number must be decimal */
+    if((port <= 0) || (port > 0xffff))
+      return CURLUE_BAD_PORT_NUMBER;
+    storep = &u->port;
+    break;
+  case CURLUPART_PATH:
+    urlskipslash = TRUE;
+    storep = &u->path;
+    break;
+  case CURLUPART_QUERY:
+    plusencode = urlencode;
+    appendquery = (flags & CURLU_APPENDQUERY)?1:0;
+    storep = &u->query;
+    break;
+  case CURLUPART_FRAGMENT:
+    storep = &u->fragment;
+    break;
+  case CURLUPART_URL: {
+    /*
+     * Allow a new URL to replace the existing (if any) contents.
+     *
+     * If the existing contents is enough for a URL, allow a relative URL to
+     * replace it.
+     */
+    CURLUcode result;
+    char *oldurl;
+    char *redired_url;
+    CURLU *handle2;
+
+    if(Curl_is_absolute_url(part, NULL, MAX_SCHEME_LEN)) {
+      handle2 = curl_url();
+      if(!handle2)
+        return CURLUE_OUT_OF_MEMORY;
+      result = parseurl(part, handle2, flags);
+      if(!result)
+        mv_urlhandle(handle2, u);
+      else
+        curl_url_cleanup(handle2);
+      return result;
+    }
+    /* extract the full "old" URL to do the redirect on */
+    result = curl_url_get(u, CURLUPART_URL, &oldurl, flags);
+    if(result) {
+      /* couldn't get the old URL, just use the new! */
+      handle2 = curl_url();
+      if(!handle2)
+        return CURLUE_OUT_OF_MEMORY;
+      result = parseurl(part, handle2, flags);
+      if(!result)
+        mv_urlhandle(handle2, u);
+      else
+        curl_url_cleanup(handle2);
+      return result;
+    }
+
+    /* apply the relative part to create a new URL */
+    redired_url = Curl_concat_url(oldurl, part);
+    free(oldurl);
+    if(!redired_url)
+      return CURLUE_OUT_OF_MEMORY;
+
+    /* now parse the new URL */
+    handle2 = curl_url();
+    if(!handle2) {
+      free(redired_url);
+      return CURLUE_OUT_OF_MEMORY;
+    }
+    result = parseurl(redired_url, handle2, flags);
+    free(redired_url);
+    if(!result)
+      mv_urlhandle(handle2, u);
+    else
+      curl_url_cleanup(handle2);
+    return result;
+  }
+  default:
+    return CURLUE_UNKNOWN_PART;
+  }
+  if(storep) {
+    const char *newp = part;
+    size_t nalloc = strlen(part);
+
+    if(urlencode) {
+      const char *i;
+      char *o;
+      bool free_part = FALSE;
+      char *enc = malloc(nalloc * 3 + 1); /* for worst case! */
+      if(!enc)
+        return CURLUE_OUT_OF_MEMORY;
+      if(plusencode) {
+        /* space to plus */
+        i = part;
+        for(o = enc; *i; ++o, ++i)
+          *o = (*i == ' ') ? '+' : *i;
+        *o = 0; /* zero terminate */
+        part = strdup(enc);
+        if(!part) {
+          free(enc);
+          return CURLUE_OUT_OF_MEMORY;
+        }
+        free_part = TRUE;
+      }
+      for(i = part, o = enc; *i; i++) {
+        if(Curl_isunreserved(*i) ||
+           ((*i == '/') && urlskipslash) ||
+           ((*i == '=') && appendquery) ||
+           ((*i == '+') && plusencode)) {
+          *o = *i;
+          o++;
+        }
+        else {
+          snprintf(o, 4, "%%%02x", *i);
+          o += 3;
+        }
+      }
+      *o = 0; /* zero terminate */
+      newp = enc;
+      if(free_part)
+        free((char *)part);
+    }
+    else {
+      char *p;
+      newp = strdup(part);
+      if(!newp)
+        return CURLUE_OUT_OF_MEMORY;
+      p = (char *)newp;
+      while(*p) {
+        /* make sure percent encoded are lower case */
+        if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) &&
+           (ISUPPER(p[1]) || ISUPPER(p[2]))) {
+          p[1] = (char)TOLOWER(p[1]);
+          p[2] = (char)TOLOWER(p[2]);
+          p += 3;
+        }
+        else
+          p++;
+      }
+    }
+
+    if(appendquery) {
+      /* Append the string onto the old query. Add a '&' separator if none is
+         present at the end of the exsting query already */
+      size_t querylen = u->query ? strlen(u->query) : 0;
+      bool addamperand = querylen && (u->query[querylen -1] != '&');
+      if(querylen) {
+        size_t newplen = strlen(newp);
+        char *p = malloc(querylen + addamperand + newplen + 1);
+        if(!p) {
+          free((char *)newp);
+          return CURLUE_OUT_OF_MEMORY;
+        }
+        strcpy(p, u->query); /* original query */
+        if(addamperand)
+          p[querylen] = '&'; /* ampersand */
+        strcpy(&p[querylen + addamperand], newp); /* new suffix */
+        free((char *)newp);
+        free(*storep);
+        *storep = p;
+        return CURLUE_OK;
+      }
+    }
+
+    free(*storep);
+    *storep = (char *)newp;
+  }
+  /* set after the string, to make it not assigned if the allocation above
+     fails */
+  if(port)
+    u->portnum = port;
+  return CURLUE_OK;
+}

+ 61 - 22
lib/urldata.h

@@ -142,14 +142,6 @@ typedef ssize_t (Curl_recv)(struct connectdata *conn, /* connection data */
 #include <libssh2_sftp.h>
 #include <libssh2_sftp.h>
 #endif /* HAVE_LIBSSH2_H */
 #endif /* HAVE_LIBSSH2_H */
 
 
-/* The upload buffer size, should not be smaller than CURL_MAX_WRITE_SIZE, as
-   it needs to hold a full buffer as could be sent in a write callback.
-
-   The size was 16KB for many years but was bumped to 64KB because it makes
-   libcurl able to do significantly faster uploads in some circumstances. Even
-   larger buffers can help further, but this is deemed a fair memory/speed
-   compromise. */
-#define UPLOAD_BUFSIZE 65536
 
 
 /* The "master buffer" is for HTTP pipelining */
 /* The "master buffer" is for HTTP pipelining */
 #define MASTERBUF_SIZE 16384
 #define MASTERBUF_SIZE 16384
@@ -476,7 +468,6 @@ struct hostname {
 #define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE)
 #define KEEP_SENDBITS (KEEP_SEND | KEEP_SEND_HOLD | KEEP_SEND_PAUSE)
 
 
 
 
-#ifdef CURLRES_ASYNCH
 struct Curl_async {
 struct Curl_async {
   char *hostname;
   char *hostname;
   int port;
   int port;
@@ -485,7 +476,6 @@ struct Curl_async {
   int status; /* if done is TRUE, this is the status from the callback */
   int status; /* if done is TRUE, this is the status from the callback */
   void *os_specific;  /* 'struct thread_data' for Windows */
   void *os_specific;  /* 'struct thread_data' for Windows */
 };
 };
-#endif
 
 
 #define FIRSTSOCKET     0
 #define FIRSTSOCKET     0
 #define SECONDARYSOCKET 1
 #define SECONDARYSOCKET 1
@@ -511,6 +501,28 @@ enum upgrade101 {
   UPGR101_WORKING             /* talking upgraded protocol */
   UPGR101_WORKING             /* talking upgraded protocol */
 };
 };
 
 
+struct dohresponse {
+  unsigned char *memory;
+  size_t size;
+};
+
+/* one of these for each DoH request */
+struct dnsprobe {
+  CURL *easy;
+  int dnstype;
+  unsigned char dohbuffer[512];
+  size_t dohlen;
+  struct dohresponse serverdoh;
+};
+
+struct dohdata {
+  struct curl_slist *headers;
+  struct dnsprobe probe[2];
+  unsigned int pending; /* still outstanding requests */
+  const char *host;
+  int port;
+};
+
 /*
 /*
  * Request specific data in the easy handle (Curl_easy).  Previously,
  * Request specific data in the easy handle (Curl_easy).  Previously,
  * these members were on the connectdata struct but since a conn struct may
  * these members were on the connectdata struct but since a conn struct may
@@ -606,6 +618,7 @@ struct SingleRequest {
 
 
   void *protop;       /* Allocated protocol-specific data. Each protocol
   void *protop;       /* Allocated protocol-specific data. Each protocol
                          handler makes sure this points to data it needs. */
                          handler makes sure this points to data it needs. */
+  struct dohdata doh; /* DoH specific data for this request */
 };
 };
 
 
 /*
 /*
@@ -636,7 +649,7 @@ struct Curl_handler {
    */
    */
   CURLcode (*connect_it)(struct connectdata *, bool *done);
   CURLcode (*connect_it)(struct connectdata *, bool *done);
 
 
-  /* See above. Currently only used for FTP. */
+  /* See above. */
   CURLcode (*connecting)(struct connectdata *, bool *done);
   CURLcode (*connecting)(struct connectdata *, bool *done);
   CURLcode (*doing)(struct connectdata *, bool *done);
   CURLcode (*doing)(struct connectdata *, bool *done);
 
 
@@ -716,6 +729,7 @@ struct Curl_handler {
 
 
 #define CONNCHECK_NONE 0                 /* No checks */
 #define CONNCHECK_NONE 0                 /* No checks */
 #define CONNCHECK_ISDEAD (1<<0)          /* Check if the connection is dead. */
 #define CONNCHECK_ISDEAD (1<<0)          /* Check if the connection is dead. */
+#define CONNCHECK_KEEPALIVE (1<<1)       /* Perform any keepalive function. */
 
 
 #define CONNRESULT_NONE 0                /* No extra information. */
 #define CONNRESULT_NONE 0                /* No extra information. */
 #define CONNRESULT_DEAD (1<<0)           /* The connection is dead. */
 #define CONNRESULT_DEAD (1<<0)           /* The connection is dead. */
@@ -892,6 +906,13 @@ struct connectdata {
 
 
   long ip_version; /* copied from the Curl_easy at creation time */
   long ip_version; /* copied from the Curl_easy at creation time */
 
 
+  /* Protocols can use a custom keepalive mechanism to keep connections alive.
+     This allows those protocols to track the last time the keepalive mechanism
+     was used on this connection. */
+  struct curltime keepalive;
+
+  long upkeep_interval_ms;      /* Time between calls for connection upkeep. */
+
   /**** curl_get() phase fields */
   /**** curl_get() phase fields */
 
 
   curl_socket_t sockfd;   /* socket to read from or CURL_SOCKET_BAD */
   curl_socket_t sockfd;   /* socket to read from or CURL_SOCKET_BAD */
@@ -969,11 +990,8 @@ struct connectdata {
 #endif
 #endif
 
 
   char syserr_buf [256]; /* buffer for Curl_strerror() */
   char syserr_buf [256]; /* buffer for Curl_strerror() */
-
-#ifdef CURLRES_ASYNCH
   /* data used for the asynch name resolve callback */
   /* data used for the asynch name resolve callback */
   struct Curl_async async;
   struct Curl_async async;
-#endif
 
 
   /* These three are used for chunked-encoding trailer support */
   /* These three are used for chunked-encoding trailer support */
   char *trailer; /* allocated buffer to store trailer in */
   char *trailer; /* allocated buffer to store trailer in */
@@ -1206,6 +1224,18 @@ struct time_node {
   expire_id eid;
   expire_id eid;
 };
 };
 
 
+/* individual pieces of the URL */
+struct urlpieces {
+  char *scheme;
+  char *hostname;
+  char *port;
+  char *user;
+  char *password;
+  char *options;
+  char *path;
+  char *query;
+};
+
 struct UrlState {
 struct UrlState {
 
 
   /* Points to the connection cache */
   /* Points to the connection cache */
@@ -1225,7 +1255,7 @@ struct UrlState {
   size_t headersize;   /* size of the allocation */
   size_t headersize;   /* size of the allocation */
 
 
   char *buffer; /* download buffer */
   char *buffer; /* download buffer */
-  char *ulbuf; /* alloced upload buffer or NULL */
+  char *ulbuf; /* allocated upload buffer or NULL */
   curl_off_t current_speed;  /* the ProgressShow() function sets this,
   curl_off_t current_speed;  /* the ProgressShow() function sets this,
                                 bytes / second */
                                 bytes / second */
   bool this_is_a_follow; /* this is a followed Location: request */
   bool this_is_a_follow; /* this is a followed Location: request */
@@ -1296,9 +1326,6 @@ struct UrlState {
   /* for FTP downloads: how many CRLFs did we converted to LFs? */
   /* for FTP downloads: how many CRLFs did we converted to LFs? */
   curl_off_t crlf_conversions;
   curl_off_t crlf_conversions;
 #endif
 #endif
-  char *pathbuffer;/* allocated buffer to store the URL's path part in */
-  char *path;      /* path to use, points to somewhere within the pathbuffer
-                      area */
   bool slash_removed; /* set TRUE if the 'path' points to a path where the
   bool slash_removed; /* set TRUE if the 'path' points to a path where the
                          initial URL slash separator has been taken off */
                          initial URL slash separator has been taken off */
   bool use_range;
   bool use_range;
@@ -1332,6 +1359,8 @@ struct UrlState {
 #ifdef CURLDEBUG
 #ifdef CURLDEBUG
   bool conncache_lock;
   bool conncache_lock;
 #endif
 #endif
+  CURLU *uh; /* URL handle for the current parsed URL */
+  struct urlpieces up;
 };
 };
 
 
 
 
@@ -1442,18 +1471,23 @@ enum dupstring {
   STRING_UNIX_SOCKET_PATH,      /* path to Unix socket, if used */
   STRING_UNIX_SOCKET_PATH,      /* path to Unix socket, if used */
 #endif
 #endif
   STRING_TARGET,                /* CURLOPT_REQUEST_TARGET */
   STRING_TARGET,                /* CURLOPT_REQUEST_TARGET */
+  STRING_DOH,                   /* CURLOPT_DOH_URL */
   /* -- end of zero-terminated strings -- */
   /* -- end of zero-terminated strings -- */
 
 
   STRING_LASTZEROTERMINATED,
   STRING_LASTZEROTERMINATED,
 
 
-  /* -- below this are pointers to binary data that cannot be strdup'ed.
-     Each such pointer must be added manually to Curl_dupset() --- */
+  /* -- below this are pointers to binary data that cannot be strdup'ed. --- */
 
 
   STRING_COPYPOSTFIELDS,  /* if POST, set the fields' values here */
   STRING_COPYPOSTFIELDS,  /* if POST, set the fields' values here */
 
 
   STRING_LAST /* not used, just an end-of-list marker */
   STRING_LAST /* not used, just an end-of-list marker */
 };
 };
 
 
+/* callback that gets called when this easy handle is completed within a multi
+   handle.  Only used for internally created transfers, like for example
+   DoH. */
+typedef int (*multidone_func)(struct Curl_easy *easy, CURLcode result);
+
 struct UserDefined {
 struct UserDefined {
   FILE *err;         /* the stderr user data goes here */
   FILE *err;         /* the stderr user data goes here */
   void *debugdata;   /* the data that will be passed to fdebug */
   void *debugdata;   /* the data that will be passed to fdebug */
@@ -1562,8 +1596,8 @@ struct UserDefined {
   curl_proxytype proxytype; /* what kind of proxy that is in use */
   curl_proxytype proxytype; /* what kind of proxy that is in use */
   long dns_cache_timeout; /* DNS cache timeout */
   long dns_cache_timeout; /* DNS cache timeout */
   long buffer_size;      /* size of receive buffer to use */
   long buffer_size;      /* size of receive buffer to use */
-  long upload_buffer_size; /* size of upload buffer to use,
-                              keep it >= CURL_MAX_WRITE_SIZE */
+  size_t upload_buffer_size; /* size of upload buffer to use,
+                                keep it >= CURL_MAX_WRITE_SIZE */
   void *private_data; /* application-private data */
   void *private_data; /* application-private data */
 
 
   struct curl_slist *http200aliases; /* linked list of aliases for http200 */
   struct curl_slist *http200aliases; /* linked list of aliases for http200 */
@@ -1689,6 +1723,11 @@ struct UserDefined {
                                                   before resolver start */
                                                   before resolver start */
   void *resolver_start_client; /* pointer to pass to resolver start callback */
   void *resolver_start_client; /* pointer to pass to resolver start callback */
   bool disallow_username_in_url; /* disallow username in url */
   bool disallow_username_in_url; /* disallow username in url */
+  long upkeep_interval_ms;      /* Time between calls for connection upkeep. */
+  bool doh; /* DNS-over-HTTPS enabled */
+  bool doh_get; /* use GET for DoH requests, instead of POST */
+  multidone_func fmultidone;
+  struct Curl_easy *dohfor; /* this is a DoH request for that transfer */
 };
 };
 
 
 struct Names {
 struct Names {

+ 2 - 2
lib/vauth/cleartext.c

@@ -50,7 +50,7 @@
  *
  *
  * data    [in]     - The session handle.
  * data    [in]     - The session handle.
  * userp   [in]     - The user name.
  * userp   [in]     - The user name.
- * passdwp [in]     - The user's password.
+ * passwdp [in]     - The user's password.
  * outptr  [in/out] - The address where a pointer to newly allocated memory
  * outptr  [in/out] - The address where a pointer to newly allocated memory
  *                    holding the result will be stored upon completion.
  *                    holding the result will be stored upon completion.
  * outlen  [out]    - The length of the output message.
  * outlen  [out]    - The length of the output message.
@@ -74,7 +74,7 @@ CURLcode Curl_auth_create_plain_message(struct Curl_easy *data,
   plen = strlen(passwdp);
   plen = strlen(passwdp);
 
 
   /* Compute binary message length. Check for overflows. */
   /* Compute binary message length. Check for overflows. */
-  if((ulen > SIZE_T_MAX/2) || (plen > (SIZE_T_MAX/2 - 2)))
+  if((ulen > SIZE_T_MAX/4) || (plen > (SIZE_T_MAX/2 - 2)))
     return CURLE_OUT_OF_MEMORY;
     return CURLE_OUT_OF_MEMORY;
   plainlen = 2 * ulen + plen + 2;
   plainlen = 2 * ulen + plen + 2;
 
 

+ 1 - 1
lib/vauth/cram.c

@@ -81,7 +81,7 @@ CURLcode Curl_auth_decode_cram_md5_message(const char *chlg64, char **outptr,
  * data    [in]     - The session handle.
  * data    [in]     - The session handle.
  * chlg    [in]     - The challenge.
  * chlg    [in]     - The challenge.
  * userp   [in]     - The user name.
  * userp   [in]     - The user name.
- * passdwp [in]     - The user's password.
+ * passwdp [in]     - The user's password.
  * outptr  [in/out] - The address where a pointer to newly allocated memory
  * outptr  [in/out] - The address where a pointer to newly allocated memory
  *                    holding the result will be stored upon completion.
  *                    holding the result will be stored upon completion.
  * outlen  [out]    - The length of the output message.
  * outlen  [out]    - The length of the output message.

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