mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
Merge branch 'upstream-curl' into update-curl
* upstream-curl: curl 2026-06-24 (68720b48)
This commit is contained in:
@@ -283,7 +283,7 @@ static void check(char c)
|
||||
int main(void)
|
||||
{
|
||||
char buffer[1024];
|
||||
/* This will not compile if strerror_r does not return a char* */
|
||||
/* This does not compile if strerror_r does not return a char* */
|
||||
/* !checksrc! disable ERRNOVAR 1 */
|
||||
check(strerror_r(EACCES, buffer, sizeof(buffer))[0]);
|
||||
return 0;
|
||||
@@ -303,7 +303,7 @@ static void check(float f)
|
||||
int main(void)
|
||||
{
|
||||
char buffer[1024];
|
||||
/* This will not compile if strerror_r does not return an int */
|
||||
/* This does not compile if strerror_r does not return an int */
|
||||
/* !checksrc! disable ERRNOVAR 1 */
|
||||
check(strerror_r(EACCES, buffer, sizeof(buffer)));
|
||||
return 0;
|
||||
|
||||
@@ -25,15 +25,14 @@
|
||||
#
|
||||
# Input variables:
|
||||
#
|
||||
# - `GSS_ROOT_DIR`: Absolute path to the root installation of GSS. (also supported as environment)
|
||||
# - `GSS_ROOT_DIR`: Absolute path to the root installation of GSS. (also supported as environment)
|
||||
#
|
||||
# Defines:
|
||||
#
|
||||
# - `GSS_FOUND`: System has a GSS library.
|
||||
# - `GSS_VERSION`: This is set to version advertised by pkg-config or read from manifest.
|
||||
# In case the library is found but no version info available it is set to "unknown"
|
||||
# - `CURL::gss`: GSS library target.
|
||||
# - `CURL_GSS_FLAVOUR`: Custom property. "GNU" or "MIT" if detected.
|
||||
# - `GSS_FOUND`: System has GSS.
|
||||
# - `GSS_VERSION`: Version of GSS.
|
||||
# - `CURL::gss`: GSS library target.
|
||||
# - `INTERFACE_CURL_GSS_FLAVOR`: Custom property. "GNU" or "MIT" if detected.
|
||||
|
||||
set(_gnu_modname "gss")
|
||||
set(_mit_modname "mit-krb5-gssapi")
|
||||
@@ -51,7 +50,7 @@ set(_gss_LIBRARY_DIRS "")
|
||||
if(NOT GSS_ROOT_DIR AND NOT "$ENV{GSS_ROOT_DIR}")
|
||||
if(CURL_USE_PKGCONFIG)
|
||||
find_package(PkgConfig QUIET)
|
||||
pkg_search_module(_gss ${_gnu_modname} ${_mit_modname})
|
||||
pkg_search_module(_gss ${_mit_modname} ${_gnu_modname})
|
||||
list(APPEND _gss_root_hints "${_gss_PREFIX}")
|
||||
set(_gss_version "${_gss_VERSION}")
|
||||
endif()
|
||||
@@ -121,7 +120,7 @@ if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional appr
|
||||
RESULT_VARIABLE _gss_configure_failed
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
# Older versions may not have the "--version" parameter. In this case we just do not care.
|
||||
# Older versions may not have the "--version" parameter. In this case we do not care.
|
||||
if(_gss_configure_failed)
|
||||
set(_gss_version 0)
|
||||
else()
|
||||
@@ -134,9 +133,9 @@ if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional appr
|
||||
RESULT_VARIABLE _gss_configure_failed
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
|
||||
# Older versions may not have the "--vendor" parameter. In this case we just do not care.
|
||||
# Older versions may not have the "--vendor" parameter. In this case we do not care.
|
||||
if(NOT _gss_configure_failed AND NOT _gss_vendor MATCHES "Heimdal|heimdal")
|
||||
set(_gss_flavour "MIT") # assume a default, should not really matter
|
||||
set(_gss_flavor "MIT") # assume a default, should not really matter
|
||||
endif()
|
||||
|
||||
else() # Either there is no config script or we are on a platform that does not provide one (Windows?)
|
||||
@@ -152,7 +151,7 @@ if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional appr
|
||||
cmake_pop_check_state()
|
||||
|
||||
if(_gss_have_mit_headers)
|
||||
set(_gss_flavour "MIT")
|
||||
set(_gss_flavor "MIT")
|
||||
if(WIN32)
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
list(APPEND _gss_libdir_suffixes "lib/AMD64")
|
||||
@@ -170,14 +169,14 @@ if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional appr
|
||||
find_path(_gss_INCLUDE_DIRS NAMES "gss.h" HINTS ${_gss_root_hints} PATH_SUFFIXES "include")
|
||||
|
||||
if(_gss_INCLUDE_DIRS)
|
||||
set(_gss_flavour "GNU")
|
||||
set(_gss_flavor "GNU")
|
||||
set(_gss_pc_requires ${_gnu_modname})
|
||||
set(_gss_libname "gss")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# If we have headers, look up libraries
|
||||
if(_gss_flavour)
|
||||
if(_gss_flavor)
|
||||
set(_gss_libdir_hints ${_gss_root_hints})
|
||||
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20)
|
||||
cmake_path(GET _gss_INCLUDE_DIRS PARENT_PATH _gss_calculated_potential_root)
|
||||
@@ -189,28 +188,26 @@ if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional appr
|
||||
find_library(_gss_LIBRARIES NAMES ${_gss_libname} HINTS ${_gss_libdir_hints} PATH_SUFFIXES ${_gss_libdir_suffixes})
|
||||
endif()
|
||||
endif()
|
||||
if(NOT _gss_flavour)
|
||||
if(NOT _gss_flavor)
|
||||
message(FATAL_ERROR "GNU or MIT GSS is required")
|
||||
endif()
|
||||
else()
|
||||
# _gss_MODULE_NAME set since CMake 3.16.
|
||||
# _pkg_check_modules_pkg_name is undocumented and used as a fallback for CMake <3.16 versions.
|
||||
if(_gss_MODULE_NAME STREQUAL _gnu_modname OR _pkg_check_modules_pkg_name STREQUAL _gnu_modname)
|
||||
set(_gss_flavour "GNU")
|
||||
if(_gss_MODULE_NAME STREQUAL _gnu_modname)
|
||||
set(_gss_flavor "GNU")
|
||||
set(_gss_pc_requires ${_gnu_modname})
|
||||
elseif(_gss_MODULE_NAME STREQUAL _mit_modname OR _pkg_check_modules_pkg_name STREQUAL _mit_modname)
|
||||
set(_gss_flavour "MIT")
|
||||
elseif(_gss_MODULE_NAME STREQUAL _mit_modname)
|
||||
set(_gss_flavor "MIT")
|
||||
set(_gss_pc_requires ${_mit_modname})
|
||||
else()
|
||||
message(FATAL_ERROR "GNU or MIT GSS is required")
|
||||
endif()
|
||||
message(STATUS "Found GSS/${_gss_flavour} (via pkg-config): ${_gss_INCLUDE_DIRS} (found version \"${_gss_version}\")")
|
||||
message(STATUS "Found GSS/${_gss_flavor} (via pkg-config): ${_gss_INCLUDE_DIRS} (found version \"${_gss_version}\")")
|
||||
endif()
|
||||
|
||||
set(GSS_VERSION ${_gss_version})
|
||||
|
||||
if(NOT GSS_VERSION)
|
||||
if(_gss_flavour STREQUAL "MIT")
|
||||
if(_gss_flavor STREQUAL "MIT" AND WIN32)
|
||||
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24)
|
||||
cmake_host_system_information(RESULT _mit_version QUERY WINDOWS_REGISTRY
|
||||
"HKLM/SOFTWARE/MIT/Kerberos/SDK/CurrentVersion" VALUE "VersionString")
|
||||
@@ -218,12 +215,8 @@ if(NOT GSS_VERSION)
|
||||
get_filename_component(_mit_version
|
||||
"[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos\\SDK\\CurrentVersion;VersionString]" NAME CACHE)
|
||||
endif()
|
||||
if(WIN32 AND _mit_version)
|
||||
set(GSS_VERSION "${_mit_version}")
|
||||
else()
|
||||
set(GSS_VERSION "MIT Unknown")
|
||||
endif()
|
||||
else() # GNU
|
||||
set(GSS_VERSION "${_mit_version}")
|
||||
elseif(_gss_flavor STREQUAL "GNU")
|
||||
if(_gss_INCLUDE_DIRS AND EXISTS "${_gss_INCLUDE_DIRS}/gss.h")
|
||||
set(_version_regex "#[\t ]*define[\t ]+GSS_VERSION[\t ]+\"([^\"]*)\"")
|
||||
file(STRINGS "${_gss_INCLUDE_DIRS}/gss.h" _version_str REGEX "${_version_regex}")
|
||||
@@ -238,7 +231,7 @@ endif()
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(GSS
|
||||
REQUIRED_VARS
|
||||
_gss_flavour
|
||||
_gss_flavor
|
||||
_gss_LIBRARIES
|
||||
VERSION_VAR
|
||||
GSS_VERSION
|
||||
@@ -261,7 +254,7 @@ if(GSS_FOUND)
|
||||
if(NOT TARGET CURL::gss)
|
||||
add_library(CURL::gss INTERFACE IMPORTED)
|
||||
set_target_properties(CURL::gss PROPERTIES
|
||||
INTERFACE_CURL_GSS_FLAVOUR "${_gss_flavour}"
|
||||
INTERFACE_CURL_GSS_FLAVOR "${_gss_flavor}"
|
||||
INTERFACE_LIBCURL_PC_MODULES "${_gss_pc_requires}"
|
||||
INTERFACE_COMPILE_OPTIONS "${_gss_CFLAGS}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${_gss_INCLUDE_DIRS}"
|
||||
|
||||
@@ -61,7 +61,7 @@ if(_nghttp2_FOUND)
|
||||
elseif(nghttp2_CONFIG)
|
||||
set(NGHTTP2_FOUND TRUE)
|
||||
set(NGHTTP2_VERSION ${nghttp2_VERSION})
|
||||
if(NGHTTP2_USE_STATIC_LIBS)
|
||||
if(NGHTTP2_USE_STATIC_LIBS OR NOT TARGET nghttp2::nghttp2)
|
||||
set(_nghttp2_LIBRARIES nghttp2::nghttp2_static)
|
||||
else()
|
||||
set(_nghttp2_LIBRARIES nghttp2::nghttp2)
|
||||
|
||||
@@ -61,7 +61,7 @@ if(_nghttp3_FOUND)
|
||||
elseif(nghttp3_CONFIG)
|
||||
set(NGHTTP3_FOUND TRUE)
|
||||
set(NGHTTP3_VERSION ${nghttp3_VERSION})
|
||||
if(NGHTTP3_USE_STATIC_LIBS)
|
||||
if(NGHTTP3_USE_STATIC_LIBS OR NOT TARGET nghttp3::nghttp3)
|
||||
set(_nghttp3_LIBRARIES nghttp3::nghttp3_static)
|
||||
else()
|
||||
set(_nghttp3_LIBRARIES nghttp3::nghttp3)
|
||||
|
||||
@@ -49,27 +49,28 @@
|
||||
#
|
||||
# - `NGTCP2_FOUND`: System has ngtcp2.
|
||||
# - `NGTCP2_VERSION`: Version of ngtcp2.
|
||||
# - `NGTCP2_CRYPTO_BACKEND`: Name of the crypto library component. (Empty if COMPONENTS was not used.)
|
||||
# - `CURL::ngtcp2`: ngtcp2 library target.
|
||||
|
||||
set(NGTCP2_CRYPTO_BACKEND "")
|
||||
if(NGTCP2_FIND_COMPONENTS)
|
||||
set(_ngtcp2_crypto_backend "")
|
||||
foreach(_component IN LISTS NGTCP2_FIND_COMPONENTS)
|
||||
if(_component MATCHES "^(BoringSSL|GnuTLS|LibreSSL|ossl|quictls|wolfSSL)")
|
||||
if(_ngtcp2_crypto_backend)
|
||||
if(NGTCP2_CRYPTO_BACKEND)
|
||||
message(FATAL_ERROR "NGTCP2: Only one crypto library can be selected")
|
||||
endif()
|
||||
set(_ngtcp2_crypto_backend ${_component})
|
||||
set(NGTCP2_CRYPTO_BACKEND ${_component})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(_ngtcp2_crypto_backend)
|
||||
string(TOLOWER "ngtcp2_crypto_${_ngtcp2_crypto_backend}" _crypto_library_lower)
|
||||
string(TOUPPER "ngtcp2_crypto_${_ngtcp2_crypto_backend}" _crypto_library_upper)
|
||||
if(NGTCP2_CRYPTO_BACKEND)
|
||||
string(TOLOWER "ngtcp2_crypto_${NGTCP2_CRYPTO_BACKEND}" _crypto_library_lower)
|
||||
string(TOUPPER "ngtcp2_crypto_${NGTCP2_CRYPTO_BACKEND}" _crypto_library_upper)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(_ngtcp2_pc_requires "libngtcp2")
|
||||
if(_ngtcp2_crypto_backend)
|
||||
if(NGTCP2_CRYPTO_BACKEND)
|
||||
list(APPEND _ngtcp2_pc_requires "lib${_crypto_library_lower}")
|
||||
endif()
|
||||
|
||||
@@ -81,7 +82,7 @@ if(NOT DEFINED NGTCP2_INCLUDE_DIR AND
|
||||
pkg_check_modules(_ngtcp2 ${_ngtcp2_pc_requires})
|
||||
set(_tried_pkgconfig TRUE)
|
||||
endif()
|
||||
if(NOT _ngtcp2_FOUND AND CURL_USE_CMAKECONFIG)
|
||||
if(NOT _ngtcp2_FOUND AND CURL_USE_CMAKECONFIG AND NGTCP2_CRYPTO_BACKEND)
|
||||
find_package(ngtcp2 CONFIG QUIET)
|
||||
# Skip using it if the crypto library target is not available
|
||||
if(ngtcp2_CONFIG AND
|
||||
@@ -105,7 +106,7 @@ if(_ngtcp2_FOUND)
|
||||
elseif(ngtcp2_CONFIG)
|
||||
set(NGTCP2_FOUND TRUE)
|
||||
set(NGTCP2_VERSION ${ngtcp2_VERSION})
|
||||
if(NGTCP2_USE_STATIC_LIBS)
|
||||
if(NGTCP2_USE_STATIC_LIBS OR NOT TARGET ngtcp2::ngtcp2)
|
||||
set(_ngtcp2_LIBRARIES ngtcp2::ngtcp2_static ngtcp2::${_crypto_library_lower}_static)
|
||||
else()
|
||||
set(_ngtcp2_LIBRARIES ngtcp2::ngtcp2 ngtcp2::${_crypto_library_lower})
|
||||
@@ -130,7 +131,7 @@ else()
|
||||
unset(_version_str)
|
||||
endif()
|
||||
|
||||
if(_ngtcp2_crypto_backend)
|
||||
if(NGTCP2_CRYPTO_BACKEND)
|
||||
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20)
|
||||
cmake_path(GET NGTCP2_LIBRARY PARENT_PATH _ngtcp2_library_dir)
|
||||
else()
|
||||
@@ -145,7 +146,7 @@ else()
|
||||
endif()
|
||||
|
||||
if(${_crypto_library_upper}_LIBRARY)
|
||||
set(NGTCP2_${_ngtcp2_crypto_backend}_FOUND TRUE)
|
||||
set(NGTCP2_${NGTCP2_CRYPTO_BACKEND}_FOUND TRUE)
|
||||
set(NGTCP2_CRYPTO_LIBRARY ${${_crypto_library_upper}_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -53,12 +53,12 @@ if(NOT DEFINED ZSTD_INCLUDE_DIR AND
|
||||
pkg_check_modules(_zstd ${_zstd_pc_requires})
|
||||
endif()
|
||||
if(NOT _zstd_FOUND AND CURL_USE_CMAKECONFIG)
|
||||
find_package(Zstd CONFIG QUIET)
|
||||
find_package(zstd CONFIG QUIET)
|
||||
# Skip using if older than v1.4.5
|
||||
if(Zstd_CONFIG AND
|
||||
if(zstd_CONFIG AND
|
||||
NOT TARGET zstd::libzstd_static AND
|
||||
NOT TARGET zstd::libzstd_shared)
|
||||
unset(Zstd_CONFIG)
|
||||
unset(zstd_CONFIG)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@@ -74,9 +74,10 @@ if(_zstd_FOUND)
|
||||
set(_zstd_LIBRARIES "${_zstd_STATIC_LIBRARIES}")
|
||||
endif()
|
||||
message(STATUS "Found Zstd (via pkg-config): ${_zstd_INCLUDE_DIRS} (found version \"${ZSTD_VERSION}\")")
|
||||
elseif(Zstd_CONFIG)
|
||||
elseif(zstd_CONFIG)
|
||||
set(Zstd_FOUND TRUE)
|
||||
set(ZSTD_FOUND TRUE)
|
||||
set(ZSTD_VERSION ${Zstd_VERSION})
|
||||
set(ZSTD_VERSION ${zstd_VERSION})
|
||||
if(ZSTD_USE_STATIC_LIBS)
|
||||
set(_zstd_LIBRARIES zstd::libzstd_static)
|
||||
elseif(TARGET zstd::libzstd)
|
||||
@@ -84,7 +85,7 @@ elseif(Zstd_CONFIG)
|
||||
else()
|
||||
set(_zstd_LIBRARIES zstd::libzstd_shared)
|
||||
endif()
|
||||
message(STATUS "Found Zstd (via CMake Config): ${Zstd_CONFIG} (found version \"${ZSTD_VERSION}\")")
|
||||
message(STATUS "Found Zstd (via CMake Config): ${zstd_CONFIG} (found version \"${ZSTD_VERSION}\")")
|
||||
else()
|
||||
find_path(ZSTD_INCLUDE_DIR NAMES "zstd.h")
|
||||
if(ZSTD_USE_STATIC_LIBS)
|
||||
|
||||
@@ -267,6 +267,8 @@ macro(curl_collect_target_link_options _target)
|
||||
get_target_property(_val ${_target} INTERFACE_LINK_LIBRARIES)
|
||||
if(_val)
|
||||
foreach(_lib IN LISTS _val)
|
||||
# Extract imported target name from e.g. "$<LINK_ONLY:OpenSSL::Crypto>" set by libssh2
|
||||
string(REGEX REPLACE "^\\\$<LINK_ONLY:(.*)>\$" "\\1" _lib "${_lib}")
|
||||
if(TARGET "${_lib}")
|
||||
curl_collect_target_link_options(${_lib})
|
||||
else()
|
||||
|
||||
@@ -250,7 +250,7 @@ if(PICKY_COMPILER)
|
||||
if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.1) OR
|
||||
(CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 17.0))
|
||||
list(APPEND _picky_enable
|
||||
-Wno-format-signedness # clang 19.1 gcc 5.1 appleclang 17.0 # In clang-cl enums are signed ints by default
|
||||
-Wformat-signedness # clang 19.1 gcc 5.1 appleclang 17.0 # In clang-cl enums are signed ints by default
|
||||
)
|
||||
endif()
|
||||
if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 21.1) OR
|
||||
@@ -300,13 +300,14 @@ if(PICKY_COMPILER)
|
||||
list(APPEND _picky_enable
|
||||
-Wdouble-promotion # clang 3.6 gcc 4.6 appleclang 6.1
|
||||
-Wformat=2 # clang 2.7 gcc 4.8
|
||||
-Wlogical-op # gcc 4.4
|
||||
-Wtrampolines # gcc 4.6
|
||||
)
|
||||
endif()
|
||||
if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 5.0)
|
||||
list(APPEND _picky_enable
|
||||
-Warray-bounds=2 # clang 2.9 gcc 5.0 (clang default: -Warray-bounds)
|
||||
-Wno-format-signedness # clang 19.1 gcc 5.1 appleclang 17.0
|
||||
-Wformat-signedness # clang 19.1 gcc 5.1 appleclang 17.0
|
||||
)
|
||||
endif()
|
||||
if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 6.0)
|
||||
@@ -391,7 +392,7 @@ if(PICKY_COMPILER)
|
||||
list(APPEND _picky "-Wno-conversion") # Avoid false positives
|
||||
endif()
|
||||
endif()
|
||||
elseif(MSVC AND MSVC_VERSION LESS_EQUAL 1950) # Skip for untested/unreleased newer versions
|
||||
elseif(MSVC AND MSVC_VERSION LESS_EQUAL 1951) # Enable for tested versions only
|
||||
list(APPEND _picky "-Wall")
|
||||
list(APPEND _picky "-wd4061") # enumerator 'A' in switch of enum 'B' is not explicitly handled by a case label
|
||||
list(APPEND _picky "-wd4191") # 'type cast': unsafe conversion from 'FARPROC' to 'void (__cdecl *)(void)'
|
||||
@@ -407,7 +408,7 @@ if(PICKY_COMPILER)
|
||||
list(APPEND _picky "-wd4746")
|
||||
list(APPEND _picky "-wd4820") # 'A': 'N' bytes padding added after data member 'B'
|
||||
if(MSVC_VERSION GREATER_EQUAL 1900)
|
||||
list(APPEND _picky "-wd5045") # Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified
|
||||
list(APPEND _picky "-wd5045") # Compiler inserts Spectre mitigation for memory load if /Qspectre switch specified
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@@ -437,7 +438,7 @@ if(CMAKE_C_STANDARD STREQUAL 90)
|
||||
endif()
|
||||
if((CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.9) OR
|
||||
(CMAKE_C_COMPILER_ID STREQUAL "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 8.1))
|
||||
list(APPEND _picky "-Wno-comma") # Just silly
|
||||
list(APPEND _picky "-Wno-comma") # Silly
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ if("@USE_NGHTTP3@")
|
||||
list(APPEND _curl_libs CURL::nghttp3)
|
||||
endif()
|
||||
if("@USE_NGTCP2@")
|
||||
find_dependency(NGTCP2 MODULE)
|
||||
find_dependency(NGTCP2 MODULE COMPONENTS "@NGTCP2_CRYPTO_BACKEND@")
|
||||
list(APPEND _curl_libs CURL::ngtcp2)
|
||||
endif()
|
||||
if("@USE_GNUTLS@")
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
# SPDX-License-Identifier: curl
|
||||
#
|
||||
###########################################################################
|
||||
# Based on CI runs for Cygwin/MSYS2, Linux, macOS, FreeBSD, NetBSD, OpenBSD
|
||||
# Based on CI runs for Cygwin/MSYS2, Linux, macOS/iOS, DragonFly BSD, FreeBSD, MidnightBSD, NetBSD, OpenBSD
|
||||
if(NOT UNIX)
|
||||
message(FATAL_ERROR "This file should be included on Unix platforms only")
|
||||
endif()
|
||||
@@ -30,7 +30,9 @@ if(APPLE OR
|
||||
CYGWIN)
|
||||
set(HAVE_ACCEPT4 0)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
|
||||
set(HAVE_ACCEPT4 1)
|
||||
@@ -60,13 +62,26 @@ set(HAVE_DECL_FSEEKO 1)
|
||||
set(HAVE_DIRENT_H 1)
|
||||
if(APPLE OR
|
||||
CYGWIN OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
|
||||
set(HAVE_EVENTFD 0)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
|
||||
set(HAVE_EVENTFD 1)
|
||||
endif()
|
||||
if(ANDROID AND ANDROID_PLATFORM_LEVEL GREATER_EQUAL 34)
|
||||
set(HAVE_MEMSET_EXPLICIT 1)
|
||||
endif()
|
||||
if((APPLE AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_GREATER_EQUAL 10.9) OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR # v6+
|
||||
CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR # v11.2+
|
||||
CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD") # v1.3+
|
||||
set(HAVE_MEMSET_S 1)
|
||||
elseif(NOT APPLE)
|
||||
set(HAVE_MEMSET_S 0)
|
||||
endif()
|
||||
set(HAVE_FCNTL 1)
|
||||
set(HAVE_FCNTL_H 1)
|
||||
set(HAVE_FCNTL_O_NONBLOCK 1)
|
||||
@@ -78,7 +93,9 @@ if(APPLE)
|
||||
set(HAVE_FSETXATTR 1)
|
||||
set(HAVE_FSETXATTR_5 0)
|
||||
set(HAVE_FSETXATTR_6 1)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
|
||||
set(HAVE_FSETXATTR 0)
|
||||
set(HAVE_FSETXATTR_5 0)
|
||||
@@ -95,7 +112,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
|
||||
set(HAVE_GETADDRINFO_THREADSAFE 0)
|
||||
elseif(CYGWIN OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
|
||||
set(HAVE_GETADDRINFO_THREADSAFE 1)
|
||||
endif()
|
||||
@@ -106,14 +125,18 @@ if(APPLE OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
|
||||
set(HAVE_GETHOSTBYNAME_R 0)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
|
||||
CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD")
|
||||
set(HAVE_GETHOSTBYNAME_R 1)
|
||||
endif()
|
||||
set(HAVE_GETHOSTBYNAME_R_3 0)
|
||||
set(HAVE_GETHOSTBYNAME_R_3_REENTRANT 0)
|
||||
set(HAVE_GETHOSTBYNAME_R_5 0)
|
||||
set(HAVE_GETHOSTBYNAME_R_5_REENTRANT 0)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD")
|
||||
set(HAVE_GETHOSTBYNAME_R_6 1)
|
||||
set(HAVE_GETHOSTBYNAME_R_6_REENTRANT 1)
|
||||
else()
|
||||
@@ -129,7 +152,9 @@ endif()
|
||||
if(APPLE OR
|
||||
CYGWIN OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
|
||||
set(HAVE_GETPASS_R 0)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
|
||||
@@ -201,6 +226,7 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
|
||||
BSD OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "SunOS")
|
||||
@@ -221,7 +247,8 @@ set(HAVE_SCHED_YIELD 1)
|
||||
set(HAVE_SELECT 1)
|
||||
set(HAVE_SEND 1)
|
||||
if(APPLE OR
|
||||
CYGWIN)
|
||||
CYGWIN OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD")
|
||||
set(HAVE_SENDMMSG 0)
|
||||
else()
|
||||
set(HAVE_SENDMMSG 1)
|
||||
@@ -259,10 +286,12 @@ if(ANDROID OR CMAKE_SYSTEM_NAME STREQUAL "iOS")
|
||||
endif()
|
||||
if(APPLE OR
|
||||
CYGWIN OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "DragonFlyBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
|
||||
set(HAVE_SYS_EVENTFD_H 0)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "MidnightBSD" OR
|
||||
CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
|
||||
set(HAVE_SYS_EVENTFD_H 1)
|
||||
endif()
|
||||
|
||||
@@ -535,14 +535,14 @@ elseif(BUILD_STATIC_CURL AND NOT BUILD_STATIC_LIBS)
|
||||
set(BUILD_STATIC_CURL OFF)
|
||||
endif()
|
||||
|
||||
# Lib flavour selected for curl tool
|
||||
# Lib flavor selected for curl tool
|
||||
if(BUILD_STATIC_CURL)
|
||||
set(LIB_SELECTED_FOR_EXE ${LIB_STATIC})
|
||||
else()
|
||||
set(LIB_SELECTED_FOR_EXE ${LIB_SHARED})
|
||||
endif()
|
||||
|
||||
# Lib flavour selected for example and test programs.
|
||||
# Lib flavor selected for example and test programs.
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set(LIB_SELECTED ${LIB_SHARED})
|
||||
else()
|
||||
@@ -774,7 +774,7 @@ option(BUILD_MISC_DOCS "Build misc man pages (e.g. curl-config and mk-ca-bundle)
|
||||
option(ENABLE_CURL_MANUAL "Build the man page for curl and enable its -M/--manual option" ON)
|
||||
|
||||
if((ENABLE_CURL_MANUAL OR BUILD_LIBCURL_DOCS) AND NOT Perl_FOUND)
|
||||
message(WARNING "Perl not found. Will not build manuals.")
|
||||
message(WARNING "Perl not found. Cannot build manuals.")
|
||||
endif()
|
||||
endif() # XXX(cmake): end
|
||||
|
||||
@@ -868,6 +868,7 @@ elseif(AMIGA)
|
||||
set(OPENSSL_CRYPTO_LIBRARY "${AMISSL_AUTO_LIBRARY}")
|
||||
set(CURL_USE_OPENSSL ON)
|
||||
set(CURL_CA_FALLBACK ON CACHE BOOL "")
|
||||
list(PREPEND CURL_NETWORK_AND_TIME_LIBS "${AMISSL_STUBS_LIBRARY}" "${AMISSL_AUTO_LIBRARY}")
|
||||
endif()
|
||||
elseif(NOT WIN32 AND NOT APPLE)
|
||||
check_library_exists("socket" "connect" "" HAVE_LIBSOCKET)
|
||||
@@ -926,7 +927,7 @@ endif()
|
||||
cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
|
||||
cmake_dependent_option(CURL_USE_WOLFSSL "Enable wolfSSL for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
|
||||
cmake_dependent_option(CURL_USE_GNUTLS "Enable GnuTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
|
||||
cmake_dependent_option(CURL_USE_RUSTLS "Enable Rustls for SSL/TLS" OFF CURL_ENABLE_SSL OFF)
|
||||
cmake_dependent_option(CURL_USE_RUSTLS "Enable Rustls for SSL/TLS (experimental)" OFF CURL_ENABLE_SSL OFF)
|
||||
|
||||
if(WIN32 OR
|
||||
CURL_USE_SCHANNEL OR
|
||||
@@ -1029,12 +1030,12 @@ if(CURL_USE_OPENSSL)
|
||||
|
||||
cmake_push_check_state()
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
|
||||
if(NOT DEFINED HAVE_BORINGSSL)
|
||||
check_symbol_exists("OPENSSL_IS_BORINGSSL" "openssl/base.h" HAVE_BORINGSSL)
|
||||
endif()
|
||||
if(NOT DEFINED HAVE_AWSLC)
|
||||
check_symbol_exists("OPENSSL_IS_AWSLC" "openssl/base.h" HAVE_AWSLC)
|
||||
endif()
|
||||
if(NOT DEFINED HAVE_BORINGSSL)
|
||||
check_symbol_exists("OPENSSL_IS_BORINGSSL" "openssl/base.h" HAVE_BORINGSSL)
|
||||
endif()
|
||||
if(NOT DEFINED HAVE_LIBRESSL)
|
||||
check_symbol_exists("LIBRESSL_VERSION_NUMBER" "openssl/opensslv.h" HAVE_LIBRESSL)
|
||||
endif()
|
||||
@@ -1049,15 +1050,15 @@ if(CURL_USE_OPENSSL)
|
||||
add_definitions(-DCURL_CA_PATH="${CURL_CA_PATH}")
|
||||
endif()
|
||||
|
||||
if(HAVE_BORINGSSL OR HAVE_AWSLC)
|
||||
if(NOT MSVC AND NOT ANDROID) # BoringSSL/AWS-LC MSVC builds use native Windows threads
|
||||
if(HAVE_AWSLC OR HAVE_BORINGSSL)
|
||||
if(NOT MSVC AND NOT ANDROID) # AWS-LC/BoringSSL MSVC builds use native Windows threads
|
||||
find_package(Threads)
|
||||
if(CMAKE_USE_PTHREADS_INIT)
|
||||
set(HAVE_THREADS_POSIX_BORINGSSL 1)
|
||||
list(APPEND CURL_NETWORK_AND_TIME_LIBS Threads::Threads)
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES Threads::Threads)
|
||||
elseif(OPENSSL_USE_STATIC_LIBS)
|
||||
message(WARNING "BoringSSL/AWS-LC requires POSIX Threads.")
|
||||
message(WARNING "AWS-LC/BoringSSL requires POSIX Threads.")
|
||||
endif()
|
||||
endif()
|
||||
if(OPENSSL_USE_STATIC_LIBS AND CMAKE_C_COMPILER_ID MATCHES "Clang")
|
||||
@@ -1066,17 +1067,17 @@ if(CURL_USE_OPENSSL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(HAVE_BORINGSSL)
|
||||
if(USE_AMISSL)
|
||||
set(_openssl "AmiSSL")
|
||||
elseif(HAVE_AWSLC)
|
||||
set(_openssl "AWS-LC")
|
||||
elseif(HAVE_BORINGSSL)
|
||||
if(BORINGSSL_VERSION)
|
||||
set(CURL_BORINGSSL_VERSION "\"${BORINGSSL_VERSION}\"")
|
||||
endif()
|
||||
set(_openssl "BoringSSL")
|
||||
elseif(HAVE_AWSLC)
|
||||
set(_openssl "AWS-LC")
|
||||
elseif(HAVE_LIBRESSL)
|
||||
set(_openssl "LibreSSL")
|
||||
elseif(USE_AMISSL)
|
||||
set(_openssl "AmiSSL")
|
||||
else()
|
||||
set(_openssl "OpenSSL")
|
||||
endif()
|
||||
@@ -1291,8 +1292,8 @@ if(USE_OPENSSL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(USE_HTTPSRR "Enable HTTPS RR support" OFF)
|
||||
option(USE_ECH "Enable ECH support" OFF)
|
||||
option(USE_HTTPSRR "Enable HTTPS RR support (experimental)" OFF)
|
||||
option(USE_ECH "Enable ECH support (experimental)" OFF)
|
||||
if(USE_ECH)
|
||||
if(USE_OPENSSL OR USE_WOLFSSL OR USE_RUSTLS)
|
||||
# Be sure that the TLS library actually supports ECH.
|
||||
@@ -1308,7 +1309,7 @@ if(USE_ECH)
|
||||
set(HAVE_ECH 1)
|
||||
endif()
|
||||
if(NOT HAVE_ECH)
|
||||
message(FATAL_ERROR "ECH support missing in OpenSSL/BoringSSL/AWS-LC/wolfSSL/rustls-ffi")
|
||||
message(FATAL_ERROR "ECH support missing in AWS-LC/BoringSSL/OpenSSL/Rustls/wolfSSL")
|
||||
else()
|
||||
message(STATUS "ECH enabled")
|
||||
# ECH wants HTTPSRR
|
||||
@@ -1316,11 +1317,11 @@ if(USE_ECH)
|
||||
message(STATUS "HTTPSRR enabled")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "ECH requires ECH-enabled OpenSSL, BoringSSL, AWS-LC, wolfSSL or rustls-ffi")
|
||||
message(FATAL_ERROR "ECH requires ECH-enabled AWS-LC, BoringSSL, OpenSSL, Rustls or wolfSSL")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(USE_SSLS_EXPORT "Enable SSL session export support" OFF)
|
||||
option(USE_SSLS_EXPORT "Enable SSL session import/export (experimental)" OFF)
|
||||
if(USE_SSLS_EXPORT)
|
||||
if(_ssl_enabled)
|
||||
message(STATUS "SSL export enabled.")
|
||||
@@ -1329,6 +1330,8 @@ if(USE_SSLS_EXPORT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(USE_PROXY_HTTP3 "Enable HTTP/3 proxy support (experimental)" OFF)
|
||||
|
||||
option(USE_NGHTTP2 "Use nghttp2 library" ON)
|
||||
if(USE_NGHTTP2)
|
||||
find_package(NGHTTP2 MODULE)
|
||||
@@ -1376,7 +1379,7 @@ if(USE_NGTCP2)
|
||||
list(APPEND CURL_LIBS CURL::nghttp3)
|
||||
endif()
|
||||
|
||||
option(USE_QUICHE "Use quiche library for HTTP/3 support" OFF)
|
||||
option(USE_QUICHE "Use quiche library for HTTP/3 support (experimental)" OFF)
|
||||
if(USE_QUICHE)
|
||||
if(USE_NGTCP2)
|
||||
message(FATAL_ERROR "Only one HTTP/3 backend can be selected")
|
||||
@@ -1397,6 +1400,20 @@ if(USE_QUICHE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(USE_PROXY_HTTP3)
|
||||
if(CURL_DISABLE_PROXY)
|
||||
message(FATAL_ERROR "USE_PROXY_HTTP3 requires proxy support")
|
||||
elseif(CURL_DISABLE_HTTP)
|
||||
message(FATAL_ERROR "USE_PROXY_HTTP3 requires HTTP support")
|
||||
elseif(NOT USE_NGTCP2 OR NOT USE_NGHTTP3)
|
||||
message(FATAL_ERROR "USE_PROXY_HTTP3 requires ngtcp2 + nghttp3")
|
||||
elseif(NOT USE_OPENSSL)
|
||||
message(FATAL_ERROR "USE_PROXY_HTTP3 currently requires OpenSSL")
|
||||
else()
|
||||
message(STATUS "HTTP/3 proxy support enabled (experimental)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT CURL_DISABLE_SRP AND (HAVE_GNUTLS_SRP OR HAVE_OPENSSL_SRP))
|
||||
set(USE_TLS_SRP 1)
|
||||
endif()
|
||||
@@ -1544,8 +1561,8 @@ if(CURL_USE_GSSAPI)
|
||||
if(GSS_FOUND)
|
||||
list(APPEND CURL_LIBS CURL::gss)
|
||||
|
||||
get_target_property(_gss_flavour CURL::gss INTERFACE_CURL_GSS_FLAVOUR)
|
||||
if(_gss_flavour STREQUAL "GNU")
|
||||
get_target_property(_gss_flavor CURL::gss INTERFACE_CURL_GSS_FLAVOR)
|
||||
if(_gss_flavor STREQUAL "GNU")
|
||||
set(HAVE_GSSGNU 1)
|
||||
elseif(GSS_VERSION) # MIT
|
||||
set(CURL_KRB5_VERSION "\"${GSS_VERSION}\"")
|
||||
@@ -1856,7 +1873,9 @@ check_function_exists("getrlimit" HAVE_GETRLIMIT)
|
||||
check_function_exists("setlocale" HAVE_SETLOCALE)
|
||||
check_function_exists("setrlimit" HAVE_SETRLIMIT)
|
||||
|
||||
if(NOT APPLE)
|
||||
if(APPLE)
|
||||
check_function_exists("mach_absolute_time" HAVE_MACH_ABSOLUTE_TIME)
|
||||
else()
|
||||
# Apple platforms do not offer pipe2(), but the iPhone Simulator-specific
|
||||
# /usr/lib/system/libsystem_sim_kernel.dylib exports it. To avoid false
|
||||
# detection, omit this feature check for Apple targets.
|
||||
@@ -1867,9 +1886,17 @@ if(NOT WIN32)
|
||||
check_function_exists("if_nametoindex" HAVE_IF_NAMETOINDEX) # net/if.h
|
||||
check_function_exists("realpath" HAVE_REALPATH)
|
||||
check_function_exists("sched_yield" HAVE_SCHED_YIELD)
|
||||
check_symbol_exists("strcasecmp" "string.h" HAVE_STRCASECMP)
|
||||
check_symbol_exists("stricmp" "string.h" HAVE_STRICMP)
|
||||
check_symbol_exists("strcmpi" "string.h" HAVE_STRCMPI)
|
||||
|
||||
check_symbol_exists("inet_ntop" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP) # arpa/inet.h netinet/in.h sys/socket.h
|
||||
check_symbol_exists("inet_pton" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON) # arpa/inet.h netinet/in.h sys/socket.h
|
||||
check_symbol_exists("strcasecmp" "string.h" HAVE_STRCASECMP)
|
||||
check_symbol_exists("stricmp" "string.h" HAVE_STRICMP)
|
||||
check_symbol_exists("strcmpi" "string.h" HAVE_STRCMPI)
|
||||
|
||||
check_symbol_exists("memset_s" "string.h" HAVE_MEMSET_S)
|
||||
if(NOT HAVE_MEMSET_S)
|
||||
check_function_exists("memset_explicit" HAVE_MEMSET_EXPLICIT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(AMIGA)
|
||||
@@ -1880,14 +1907,6 @@ if(NOT _ssl_enabled)
|
||||
check_symbol_exists("arc4random" "${CURL_INCLUDES};stdlib.h" HAVE_ARC4RANDOM)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
check_function_exists("mach_absolute_time" HAVE_MACH_ABSOLUTE_TIME)
|
||||
endif()
|
||||
if(NOT WIN32)
|
||||
check_symbol_exists("inet_ntop" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_NTOP) # arpa/inet.h netinet/in.h sys/socket.h
|
||||
check_symbol_exists("inet_pton" "${CURL_INCLUDES};stdlib.h;string.h" HAVE_INET_PTON) # arpa/inet.h netinet/in.h sys/socket.h
|
||||
endif()
|
||||
|
||||
check_symbol_exists("fsetxattr" "sys/xattr.h" HAVE_FSETXATTR)
|
||||
if(HAVE_FSETXATTR)
|
||||
curl_internal_test(HAVE_FSETXATTR_5)
|
||||
@@ -2268,6 +2287,7 @@ curl_add_if("NTLM" CURL_ENABLE_NTLM AND
|
||||
curl_add_if("TLS-SRP" USE_TLS_SRP)
|
||||
curl_add_if("HTTP2" USE_NGHTTP2)
|
||||
curl_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE)
|
||||
curl_add_if("proxy-HTTP3" USE_PROXY_HTTP3)
|
||||
curl_add_if("MultiSSL" CURL_WITH_MULTI_SSL)
|
||||
curl_add_if("HTTPS-proxy" NOT CURL_DISABLE_PROXY AND _ssl_enabled AND (USE_OPENSSL OR USE_GNUTLS
|
||||
OR USE_SCHANNEL OR USE_RUSTLS OR USE_MBEDTLS OR
|
||||
@@ -2385,8 +2405,8 @@ if(NOT CURL_DISABLE_INSTALL)
|
||||
set(_explicit_libs "")
|
||||
get_target_property(_imported "${_lib}" IMPORTED)
|
||||
if(NOT _imported)
|
||||
# Reading the LOCATION property on non-imported target will error out.
|
||||
# Assume the user will not need this information in the .pc file.
|
||||
# Reading the LOCATION property on non-imported target errors out.
|
||||
# Assume the user does not need this information in the .pc file.
|
||||
continue()
|
||||
endif()
|
||||
set(_libdirs "")
|
||||
@@ -2399,7 +2419,7 @@ if(NOT CURL_DISABLE_INSTALL)
|
||||
endif()
|
||||
if(_lib STREQUAL OpenSSL::SSL AND NOT HAVE_BORINGSSL) # BoringSSL does not provide openssl.pc
|
||||
set(_modules "openssl")
|
||||
elseif(_lib STREQUAL ZLIB::ZLIB)
|
||||
elseif(_lib STREQUAL ZLIB::ZLIB AND NOT ANDROID) # Android does not provide zlib.pc
|
||||
set(_modules "zlib")
|
||||
else()
|
||||
get_target_property(_modules "${_lib}" INTERFACE_LIBCURL_PC_MODULES)
|
||||
@@ -2433,6 +2453,9 @@ if(NOT CURL_DISABLE_INSTALL)
|
||||
list(APPEND LIBCURL_PC_LIBS_PRIVATE "${_lib}")
|
||||
list(APPEND LIBCURL_PC_LIBS_PRIVATE_LIST "${_lib}")
|
||||
endif()
|
||||
elseif(_lib MATCHES "^-") # '-option'
|
||||
list(APPEND _ldflags "${_lib}")
|
||||
list(APPEND LIBCURL_PC_LIBS_PRIVATE_LIST "${_lib}")
|
||||
else()
|
||||
list(APPEND LIBCURL_PC_LIBS_PRIVATE "-l${_lib}")
|
||||
list(APPEND LIBCURL_PC_LIBS_PRIVATE_LIST "${_lib}")
|
||||
@@ -2463,6 +2486,17 @@ if(NOT CURL_DISABLE_INSTALL)
|
||||
string(REPLACE ";" "," LIBCURL_PC_REQUIRES_PRIVATE "${LIBCURL_PC_REQUIRES_PRIVATE}")
|
||||
endif()
|
||||
if(LIBCURL_PC_LIBS_PRIVATE)
|
||||
# Remove duplicates listed next to each other
|
||||
set(_libs "")
|
||||
set(_prev "")
|
||||
foreach(_lib IN LISTS LIBCURL_PC_LIBS_PRIVATE)
|
||||
if(NOT _prev STREQUAL _lib)
|
||||
list(APPEND _libs "${_lib}")
|
||||
set(_prev "${_lib}")
|
||||
endif()
|
||||
endforeach()
|
||||
set(LIBCURL_PC_LIBS_PRIVATE "${_libs}")
|
||||
|
||||
string(REPLACE ";" " " LIBCURL_PC_LIBS_PRIVATE "${LIBCURL_PC_LIBS_PRIVATE}")
|
||||
endif()
|
||||
if(_ldflags)
|
||||
@@ -2598,7 +2632,7 @@ if(NOT CURL_DISABLE_INSTALL)
|
||||
# USE_MBEDTLS
|
||||
# USE_NGHTTP2
|
||||
# USE_NGHTTP3
|
||||
# USE_NGTCP2
|
||||
# USE_NGTCP2 NGTCP2_CRYPTO_BACKEND
|
||||
# USE_OPENSSL OPENSSL_VERSION_MAJOR
|
||||
# USE_QUICHE
|
||||
# USE_RUSTLS
|
||||
|
||||
@@ -252,7 +252,7 @@ typedef int (*curl_xferinfo_callback)(void *clientp,
|
||||
|
||||
#ifndef CURL_MAX_READ_SIZE
|
||||
/* The maximum receive buffer size configurable via CURLOPT_BUFFERSIZE. */
|
||||
#define CURL_MAX_READ_SIZE (10*1024*1024)
|
||||
#define CURL_MAX_READ_SIZE (10 * 1024 * 1024)
|
||||
#endif
|
||||
|
||||
#ifndef CURL_MAX_WRITE_SIZE
|
||||
@@ -269,7 +269,7 @@ typedef int (*curl_xferinfo_callback)(void *clientp,
|
||||
/* The only reason to have a max limit for this is to avoid the risk of a bad
|
||||
server feeding libcurl with a never-ending header that causes reallocs
|
||||
infinitely */
|
||||
#define CURL_MAX_HTTP_HEADER (100*1024)
|
||||
#define CURL_MAX_HTTP_HEADER (100 * 1024)
|
||||
#endif
|
||||
|
||||
/* This is a magic return code for the write callback that, when returned,
|
||||
@@ -593,7 +593,7 @@ typedef enum {
|
||||
CURLE_USE_SSL_FAILED, /* 64 - Requested FTP SSL level failed */
|
||||
CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind
|
||||
that failed */
|
||||
CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */
|
||||
CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialize ENGINE */
|
||||
CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not
|
||||
accepted and we failed to login */
|
||||
CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */
|
||||
@@ -802,9 +802,11 @@ typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */
|
||||
#define CURLPROXY_SOCKS5_HOSTNAME 7L /* Use the SOCKS5 protocol but pass along
|
||||
the hostname rather than the IP
|
||||
address. added in 7.18.0 */
|
||||
#define CURLPROXY_HTTPS3 8L /* HTTPS and attempt HTTP/3
|
||||
added in 8.21.0 */
|
||||
|
||||
typedef enum {
|
||||
CURLPROXY_LAST = 8 /* never use */
|
||||
CURLPROXY_LAST = 9 /* never use */
|
||||
} curl_proxytype; /* this enum was added in 7.10 */
|
||||
|
||||
/*
|
||||
@@ -816,7 +818,7 @@ typedef enum {
|
||||
* CURLAUTH_NEGOTIATE - HTTP Negotiate (SPNEGO) authentication
|
||||
* CURLAUTH_GSSNEGOTIATE - Alias for CURLAUTH_NEGOTIATE (deprecated)
|
||||
* CURLAUTH_NTLM - HTTP NTLM authentication
|
||||
* CURLAUTH_DIGEST_IE - HTTP Digest authentication with IE flavour
|
||||
* CURLAUTH_DIGEST_IE - HTTP Digest authentication with IE flavor
|
||||
* CURLAUTH_NTLM_WB - HTTP NTLM authentication delegated to winbind helper
|
||||
* CURLAUTH_BEARER - HTTP Bearer token authentication
|
||||
* CURLAUTH_ONLY - Use together with a single other type to force no
|
||||
@@ -902,16 +904,16 @@ enum curl_khmatch {
|
||||
};
|
||||
|
||||
typedef int
|
||||
(*curl_sshkeycallback) (CURL *easy, /* easy handle */
|
||||
const struct curl_khkey *knownkey, /* known */
|
||||
const struct curl_khkey *foundkey, /* found */
|
||||
enum curl_khmatch, /* libcurl's view on the keys */
|
||||
void *clientp); /* custom pointer passed with */
|
||||
/* CURLOPT_SSH_KEYDATA */
|
||||
(*curl_sshkeycallback)(CURL *easy, /* easy handle */
|
||||
const struct curl_khkey *knownkey, /* known */
|
||||
const struct curl_khkey *foundkey, /* found */
|
||||
enum curl_khmatch, /* libcurl's view on the keys */
|
||||
void *clientp); /* custom pointer passed with */
|
||||
/* CURLOPT_SSH_KEYDATA */
|
||||
|
||||
typedef int
|
||||
(*curl_sshhostkeycallback) (void *clientp,/* custom pointer passed */
|
||||
/* with CURLOPT_SSH_HOSTKEYDATA */
|
||||
(*curl_sshhostkeycallback)(void *clientp,/* custom pointer passed */
|
||||
/* with CURLOPT_SSH_HOSTKEYDATA */
|
||||
int keytype, /* CURLKHTYPE */
|
||||
const char *key, /* hostkey to check */
|
||||
size_t keylen); /* length of the key */
|
||||
@@ -932,7 +934,7 @@ typedef enum {
|
||||
|
||||
/* - ALLOW_BEAST tells libcurl to allow the BEAST SSL vulnerability in the
|
||||
name of improving interoperability with older servers. Some SSL libraries
|
||||
have introduced work-arounds for this flaw but those work-arounds sometimes
|
||||
have introduced workarounds for this flaw but those workarounds sometimes
|
||||
make the SSL communication fail. To regain functionality with those broken
|
||||
servers, a user can this way allow the vulnerability back. */
|
||||
#define CURLSSLOPT_ALLOW_BEAST (1L << 0)
|
||||
@@ -1362,7 +1364,7 @@ typedef enum {
|
||||
CURLOPTDEPRECATED(CURLOPT_KRBLEVEL, CURLOPTTYPE_STRINGPOINT, 63,
|
||||
8.17.0, "removed"),
|
||||
|
||||
/* Set if we should verify the peer in ssl handshake, set 1 to verify. */
|
||||
/* Set if we should verify the peer in SSL handshake, set 1 to verify. */
|
||||
CURLOPT(CURLOPT_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 64),
|
||||
|
||||
/* The CApath or CAfile used to validate the peer certificate
|
||||
@@ -1420,7 +1422,7 @@ typedef enum {
|
||||
*/
|
||||
CURLOPT(CURLOPT_HTTPGET, CURLOPTTYPE_LONG, 80),
|
||||
|
||||
/* Set if we should verify the Common name from the peer certificate in ssl
|
||||
/* Set if we should verify the Common name from the peer certificate in SSL
|
||||
* handshake, set 1 to check existence, 2 to ensure that it matches the
|
||||
* provided hostname. */
|
||||
CURLOPT(CURLOPT_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 81),
|
||||
@@ -1494,8 +1496,8 @@ typedef enum {
|
||||
CURLOPT(CURLOPT_SHARE, CURLOPTTYPE_OBJECTPOINT, 100),
|
||||
|
||||
/* indicates type of proxy. accepted values are CURLPROXY_HTTP (default),
|
||||
CURLPROXY_HTTPS, CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and
|
||||
CURLPROXY_SOCKS5. */
|
||||
CURLPROXY_HTTPS, CURLPROXY_HTTPS2, CURLPROXY_HTTPS3, CURLPROXY_SOCKS4,
|
||||
CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */
|
||||
CURLOPT(CURLOPT_PROXYTYPE, CURLOPTTYPE_VALUES, 101),
|
||||
|
||||
/* Set the Accept-Encoding string. Use this to tell a server you would like
|
||||
@@ -1524,12 +1526,12 @@ typedef enum {
|
||||
Note that setting multiple bits may cause extra network round-trips. */
|
||||
CURLOPT(CURLOPT_HTTPAUTH, CURLOPTTYPE_VALUES, 107),
|
||||
|
||||
/* Set the ssl context callback function, currently only for OpenSSL or
|
||||
/* Set the SSL context callback function, currently only for OpenSSL or
|
||||
wolfSSL ssl_ctx, or mbedTLS mbedtls_ssl_config in the second argument.
|
||||
The function must match the curl_ssl_ctx_callback prototype. */
|
||||
CURLOPT(CURLOPT_SSL_CTX_FUNCTION, CURLOPTTYPE_FUNCTIONPOINT, 108),
|
||||
|
||||
/* Set the userdata for the ssl context callback function's third
|
||||
/* Set the userdata for the SSL context callback function's third
|
||||
argument */
|
||||
CURLOPT(CURLOPT_SSL_CTX_DATA, CURLOPTTYPE_CBPOINT, 109),
|
||||
|
||||
@@ -1935,11 +1937,11 @@ typedef enum {
|
||||
/* Set authentication options directly */
|
||||
CURLOPT(CURLOPT_LOGIN_OPTIONS, CURLOPTTYPE_STRINGPOINT, 224),
|
||||
|
||||
/* Enable/disable TLS NPN extension (http2 over ssl might fail without) */
|
||||
/* Enable/disable TLS NPN extension (http2 over SSL might fail without) */
|
||||
CURLOPTDEPRECATED(CURLOPT_SSL_ENABLE_NPN, CURLOPTTYPE_LONG, 225,
|
||||
7.86.0, "Has no function"),
|
||||
|
||||
/* Enable/disable TLS ALPN extension (http2 over ssl might fail without) */
|
||||
/* Enable/disable TLS ALPN extension (http2 over SSL might fail without) */
|
||||
CURLOPT(CURLOPT_SSL_ENABLE_ALPN, CURLOPTTYPE_LONG, 226),
|
||||
|
||||
/* Time to wait for a response to an HTTP request containing an
|
||||
@@ -1985,10 +1987,12 @@ typedef enum {
|
||||
CURLOPT(CURLOPT_STREAM_WEIGHT, CURLOPTTYPE_LONG, 239),
|
||||
|
||||
/* Set stream dependency on another curl handle */
|
||||
CURLOPT(CURLOPT_STREAM_DEPENDS, CURLOPTTYPE_OBJECTPOINT, 240),
|
||||
CURLOPTDEPRECATED(CURLOPT_STREAM_DEPENDS, CURLOPTTYPE_OBJECTPOINT, 240,
|
||||
8.21.0, "Has no function"),
|
||||
|
||||
/* Set E-xclusive stream dependency on another curl handle */
|
||||
CURLOPT(CURLOPT_STREAM_DEPENDS_E, CURLOPTTYPE_OBJECTPOINT, 241),
|
||||
CURLOPTDEPRECATED(CURLOPT_STREAM_DEPENDS_E, CURLOPTTYPE_OBJECTPOINT, 241,
|
||||
8.21.0, "Has no function"),
|
||||
|
||||
/* Do not send any tftp option requests to the server */
|
||||
CURLOPT(CURLOPT_TFTP_NO_OPTIONS, CURLOPTTYPE_LONG, 242),
|
||||
@@ -2012,11 +2016,11 @@ typedef enum {
|
||||
this option is used only if PROXY_SSL_VERIFYPEER is true */
|
||||
CURLOPT(CURLOPT_PROXY_CAPATH, CURLOPTTYPE_STRINGPOINT, 247),
|
||||
|
||||
/* Set if we should verify the proxy in ssl handshake,
|
||||
/* Set if we should verify the proxy in SSL handshake,
|
||||
set 1 to verify. */
|
||||
CURLOPT(CURLOPT_PROXY_SSL_VERIFYPEER, CURLOPTTYPE_LONG, 248),
|
||||
|
||||
/* Set if we should verify the Common name from the proxy certificate in ssl
|
||||
/* Set if we should verify the Common name from the proxy certificate in SSL
|
||||
* handshake, set 1 to check existence, 2 to ensure that it matches
|
||||
* the provided hostname. */
|
||||
CURLOPT(CURLOPT_PROXY_SSL_VERIFYHOST, CURLOPTTYPE_LONG, 249),
|
||||
@@ -2808,14 +2812,14 @@ struct curl_slist {
|
||||
* backend can also be specified via the name parameter (passing -1 as id). If
|
||||
* both id and name are specified, the name is ignored. If neither id nor
|
||||
* name are specified, the function fails with CURLSSLSET_UNKNOWN_BACKEND
|
||||
* and set the "avail" pointer to the NULL-terminated list of available
|
||||
* and set the "avail" pointer to the null-terminated list of available
|
||||
* backends.
|
||||
*
|
||||
* Upon success, the function returns CURLSSLSET_OK.
|
||||
*
|
||||
* If the specified SSL backend is not available, the function returns
|
||||
* CURLSSLSET_UNKNOWN_BACKEND and sets the "avail" pointer to a
|
||||
* NULL-terminated list of available SSL backends.
|
||||
* null-terminated list of available SSL backends.
|
||||
*
|
||||
* The SSL backend can be set only once. If it has already been set, a
|
||||
* subsequent attempt to change it results in a CURLSSLSET_TOO_LATE.
|
||||
|
||||
@@ -32,12 +32,12 @@
|
||||
|
||||
/* This is the version number of the libcurl package from which this header
|
||||
file origins: */
|
||||
#define LIBCURL_VERSION "8.20.0"
|
||||
#define LIBCURL_VERSION "8.21.0"
|
||||
|
||||
/* The numeric version number is also available "in parts" by using these
|
||||
defines: */
|
||||
#define LIBCURL_VERSION_MAJOR 8
|
||||
#define LIBCURL_VERSION_MINOR 20
|
||||
#define LIBCURL_VERSION_MINOR 21
|
||||
#define LIBCURL_VERSION_PATCH 0
|
||||
/* This is the numeric version of the libcurl version number, meant for easier
|
||||
parsing and comparisons by programs. The LIBCURL_VERSION_NUM define always
|
||||
@@ -47,7 +47,7 @@
|
||||
|
||||
Where XX, YY and ZZ are the main version, release and patch numbers in
|
||||
hexadecimal (using 8 bits each). All three numbers are always represented
|
||||
using two digits. 1.2 would appear as "0x010200" while version 9.11.7
|
||||
using two digits. Version 1.2 would appear as "0x010200" while 9.11.7
|
||||
appears as "0x090b07".
|
||||
|
||||
This 6-digit (24 bits) hexadecimal number does not show pre-release number,
|
||||
@@ -58,7 +58,7 @@
|
||||
CURL_VERSION_BITS() macro since curl's own configure script greps for it
|
||||
and needs it to contain the full number.
|
||||
*/
|
||||
#define LIBCURL_VERSION_NUM 0x081400
|
||||
#define LIBCURL_VERSION_NUM 0x081500
|
||||
|
||||
/*
|
||||
* This is the date and time when the full source package was created. The
|
||||
|
||||
@@ -297,7 +297,7 @@
|
||||
/* ===================================== */
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
# if (_MSC_VER >= 1800)
|
||||
# if _MSC_VER >= 1800
|
||||
# include <inttypes.h>
|
||||
# define CURL_FORMAT_CURL_OFF_T PRId64
|
||||
# define CURL_FORMAT_CURL_OFF_TU PRIu64
|
||||
|
||||
@@ -149,8 +149,7 @@ if(SHARE_LIB_OBJECT)
|
||||
set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB")
|
||||
endif()
|
||||
target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS})
|
||||
set_target_properties(${LIB_OBJECT} PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE ON)
|
||||
set_target_properties(${LIB_OBJECT} PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAGS}")
|
||||
if(CURL_HIDES_PRIVATE_SYMBOLS)
|
||||
set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}")
|
||||
|
||||
@@ -96,6 +96,7 @@ LIB_VTLS_CFILES = \
|
||||
vtls/schannel.c \
|
||||
vtls/schannel_verify.c \
|
||||
vtls/vtls.c \
|
||||
vtls/vtls_config.c \
|
||||
vtls/vtls_scache.c \
|
||||
vtls/vtls_spack.c \
|
||||
vtls/wolfssl.c \
|
||||
@@ -113,23 +114,32 @@ LIB_VTLS_HFILES = \
|
||||
vtls/schannel.h \
|
||||
vtls/schannel_int.h \
|
||||
vtls/vtls.h \
|
||||
vtls/vtls_config.h \
|
||||
vtls/vtls_int.h \
|
||||
vtls/vtls_scache.h \
|
||||
vtls/vtls_spack.h \
|
||||
vtls/wolfssl.h \
|
||||
vtls/x509asn1.h
|
||||
|
||||
LIB_VQUIC_CFILES = \
|
||||
vquic/curl_ngtcp2.c \
|
||||
vquic/curl_quiche.c \
|
||||
vquic/vquic.c \
|
||||
LIB_VQUIC_CFILES = \
|
||||
vquic/capsule.c \
|
||||
vquic/cf-capsule.c \
|
||||
vquic/cf-ngtcp2.c \
|
||||
vquic/cf-ngtcp2-cmn.c \
|
||||
vquic/cf-ngtcp2-proxy.c \
|
||||
vquic/cf-quiche.c \
|
||||
vquic/vquic.c \
|
||||
vquic/vquic-tls.c
|
||||
|
||||
LIB_VQUIC_HFILES = \
|
||||
vquic/curl_ngtcp2.h \
|
||||
vquic/curl_quiche.h \
|
||||
vquic/vquic.h \
|
||||
vquic/vquic_int.h \
|
||||
LIB_VQUIC_HFILES = \
|
||||
vquic/capsule.h \
|
||||
vquic/cf-capsule.h \
|
||||
vquic/cf-ngtcp2.h \
|
||||
vquic/cf-ngtcp2-cmn.h \
|
||||
vquic/cf-ngtcp2-proxy.h \
|
||||
vquic/cf-quiche.h \
|
||||
vquic/vquic.h \
|
||||
vquic/vquic_int.h \
|
||||
vquic/vquic-tls.h
|
||||
|
||||
LIB_VSSH_CFILES = \
|
||||
@@ -155,12 +165,15 @@ LIB_CFILES = \
|
||||
cf-haproxy.c \
|
||||
cf-https-connect.c \
|
||||
cf-ip-happy.c \
|
||||
cf-recvbuf.c \
|
||||
cf-setup.c \
|
||||
cf-socket.c \
|
||||
cfilters.c \
|
||||
conncache.c \
|
||||
connect.c \
|
||||
content_encoding.c \
|
||||
cookie.c \
|
||||
creds.c \
|
||||
cshutdn.c \
|
||||
curl_addrinfo.c \
|
||||
curl_endian.c \
|
||||
@@ -230,13 +243,14 @@ LIB_CFILES = \
|
||||
multi_ev.c \
|
||||
multi_ntfy.c \
|
||||
netrc.c \
|
||||
noproxy.c \
|
||||
openldap.c \
|
||||
parsedate.c \
|
||||
peer.c \
|
||||
pingpong.c \
|
||||
pop3.c \
|
||||
progress.c \
|
||||
protocol.c \
|
||||
proxy.c \
|
||||
psl.c \
|
||||
rand.c \
|
||||
ratelimit.c \
|
||||
@@ -285,6 +299,8 @@ LIB_HFILES = \
|
||||
cf-haproxy.h \
|
||||
cf-https-connect.h \
|
||||
cf-ip-happy.h \
|
||||
cf-recvbuf.h \
|
||||
cf-setup.h \
|
||||
cf-socket.h \
|
||||
cfilters.h \
|
||||
conncache.h \
|
||||
@@ -292,6 +308,7 @@ LIB_HFILES = \
|
||||
connect.h \
|
||||
content_encoding.h \
|
||||
cookie.h \
|
||||
creds.h \
|
||||
curl_addrinfo.h \
|
||||
curl_ctype.h \
|
||||
curl_endian.h \
|
||||
@@ -362,12 +379,13 @@ LIB_HFILES = \
|
||||
multi_ntfy.h \
|
||||
multiif.h \
|
||||
netrc.h \
|
||||
noproxy.h \
|
||||
parsedate.h \
|
||||
peer.h \
|
||||
pingpong.h \
|
||||
pop3.h \
|
||||
progress.h \
|
||||
protocol.h \
|
||||
proxy.h \
|
||||
psl.h \
|
||||
rand.h \
|
||||
ratelimit.h \
|
||||
|
||||
@@ -113,11 +113,11 @@ static struct altsvc *altsvc_createid(const char *srchost,
|
||||
return NULL;
|
||||
as->src.host = (char *)as + sizeof(struct altsvc);
|
||||
memcpy(as->src.host, srchost, hlen);
|
||||
/* the null terminator is already there */
|
||||
/* the null-terminator is already there */
|
||||
|
||||
as->dst.host = (char *)as + sizeof(struct altsvc) + hlen + 1;
|
||||
memcpy(as->dst.host, dsthost, dlen);
|
||||
/* the null terminator is already there */
|
||||
/* the null-terminator is already there */
|
||||
|
||||
as->src.alpnid = srcalpnid;
|
||||
as->dst.alpnid = dstalpnid;
|
||||
@@ -149,7 +149,7 @@ static struct altsvc *altsvc_create(struct Curl_str *srchost,
|
||||
static void altsvc_append(struct altsvcinfo *asi, struct altsvc *as)
|
||||
{
|
||||
while(Curl_llist_count(&asi->list) >= MAX_ALTSVC_ENTRIES) {
|
||||
/* It's full. Remove the first entry in the list */
|
||||
/* It is full. Remove the first entry in the list */
|
||||
struct Curl_llist_node *e = Curl_llist_head(&asi->list);
|
||||
struct altsvc *oldas = Curl_node_elem(e);
|
||||
Curl_node_remove(e);
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#define CURL_TELOPT_NAWS 31 /* Negotiate About Window Size */
|
||||
#define CURL_TELOPT_XDISPLOC 35 /* X DISPlay LOCation */
|
||||
#define CURL_TELOPT_NEW_ENVIRON 39 /* NEW ENVIRONment variables */
|
||||
|
||||
#define CURL_NEW_ENV_VAR 0
|
||||
#define CURL_NEW_ENV_VALUE 1
|
||||
|
||||
|
||||
@@ -281,8 +281,8 @@ CURLcode Curl_async_take_result(struct Curl_easy *data,
|
||||
if(ares->ares_status == ARES_SUCCESS && !result) {
|
||||
struct Curl_dns_entry *dns =
|
||||
Curl_dnscache_mk_entry2(data, async->dns_queries,
|
||||
&ares->res_AAAA, &ares->res_A,
|
||||
async->hostname, async->port);
|
||||
&ares->res_AAAA, &ares->res_A,
|
||||
async->hostname, async->port);
|
||||
if(!dns) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
@@ -314,7 +314,7 @@ CURLcode Curl_async_take_result(struct Curl_easy *data,
|
||||
}
|
||||
|
||||
CURL_TRC_DNS(data, "ares: is_resolved() result=%d, dns=%sfound",
|
||||
result, *pdns ? "" : "not ");
|
||||
(int)result, *pdns ? "" : "not ");
|
||||
async_ares_cleanup(async);
|
||||
|
||||
out:
|
||||
@@ -489,7 +489,7 @@ static struct Curl_addrinfo *async_ares_node2addr(
|
||||
struct Curl_addrinfo *calast = NULL;
|
||||
int error = 0;
|
||||
|
||||
for(ai = node; ai != NULL; ai = ai->ai_next) {
|
||||
for(ai = node; ai; ai = ai->ai_next) {
|
||||
size_t ss_size;
|
||||
struct Curl_addrinfo *ca;
|
||||
/* ignore elements with unsupported address family,
|
||||
|
||||
@@ -111,7 +111,7 @@ struct async_thrdd_item {
|
||||
#ifdef CURLVERBOSE
|
||||
char description[CURL_ASYN_ITEM_DESC_LEN];
|
||||
#endif
|
||||
int sock_error;
|
||||
int sockerr;
|
||||
uint32_t mid;
|
||||
uint32_t resolv_id;
|
||||
uint16_t port;
|
||||
@@ -372,9 +372,9 @@ static void async_thrdd_item_process(void *arg)
|
||||
|
||||
rc = Curl_getaddrinfo_ex(item->hostname, service, &hints, &item->res);
|
||||
if(rc) {
|
||||
item->sock_error = SOCKERRNO ? SOCKERRNO : rc;
|
||||
if(item->sock_error == 0)
|
||||
item->sock_error = RESOLVER_ENOMEM;
|
||||
item->sockerr = SOCKERRNO ? SOCKERRNO : rc;
|
||||
if(item->sockerr == 0)
|
||||
item->sockerr = RESOLVER_ENOMEM;
|
||||
}
|
||||
else {
|
||||
Curl_addrinfo_set_port(item->res, item->port);
|
||||
@@ -399,9 +399,9 @@ static void async_thrdd_item_process(void *arg)
|
||||
#endif
|
||||
item->res = Curl_ipv4_resolve_r(item->hostname, item->port);
|
||||
if(!item->res) {
|
||||
item->sock_error = SOCKERRNO;
|
||||
if(item->sock_error == 0)
|
||||
item->sock_error = RESOLVER_ENOMEM;
|
||||
item->sockerr = SOCKERRNO;
|
||||
if(item->sockerr == 0)
|
||||
item->sockerr = RESOLVER_ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -481,8 +481,13 @@ static void async_thrdd_report_item(struct Curl_easy *data,
|
||||
struct dynbuf tmp;
|
||||
const char *sep = "";
|
||||
const struct Curl_addrinfo *ai = item->res;
|
||||
int ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET;
|
||||
CURLcode result;
|
||||
int ai_family;
|
||||
#ifdef USE_IPV6
|
||||
ai_family = (item->dns_queries & CURL_DNSQ_AAAA) ? AF_INET6 : AF_INET;
|
||||
#else
|
||||
ai_family = AF_INET;
|
||||
#endif
|
||||
|
||||
if(!CURL_TRC_DNS_is_verbose(data))
|
||||
return;
|
||||
@@ -635,7 +640,7 @@ CURLcode Curl_async_getaddrinfo(struct Curl_easy *data,
|
||||
out:
|
||||
if(result)
|
||||
CURL_TRC_DNS(data, "error queueing query %s:%d -> %d",
|
||||
async->hostname, async->port, result);
|
||||
async->hostname, async->port, (int)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -701,6 +706,9 @@ CURLcode Curl_async_take_result(struct Curl_easy *data,
|
||||
if(thrdd->rr.channel)
|
||||
(void)Curl_ares_perform(thrdd->rr.channel, 0);
|
||||
#endif
|
||||
#ifndef ENABLE_WAKEUP
|
||||
Curl_async_thrdd_multi_process(data->multi);
|
||||
#endif
|
||||
|
||||
if(!async->done)
|
||||
return CURLE_AGAIN;
|
||||
@@ -756,7 +764,7 @@ out:
|
||||
(result != CURLE_COULDNT_RESOLVE_HOST) &&
|
||||
(result != CURLE_COULDNT_RESOLVE_PROXY)) {
|
||||
CURL_TRC_DNS(data, "Error %d resolving %s:%d",
|
||||
result, async->hostname, async->port);
|
||||
(int)result, async->hostname, async->port);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -789,10 +797,12 @@ const struct Curl_addrinfo *Curl_async_get_ai(struct Curl_easy *data,
|
||||
if(thrdd->res_A)
|
||||
return async_thrdd_get_ai(thrdd->res_A->res, ai_family, index);
|
||||
break;
|
||||
#ifdef USE_IPV6
|
||||
case AF_INET6:
|
||||
if(thrdd->res_AAAA)
|
||||
return async_thrdd_get_ai(thrdd->res_AAAA->res, ai_family, index);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -204,6 +204,7 @@ void Curl_bufq_skip(struct bufq *q, size_t amount);
|
||||
typedef CURLcode Curl_bufq_writer(void *writer_ctx,
|
||||
const uint8_t *buf, size_t len,
|
||||
size_t *pwritten);
|
||||
|
||||
/**
|
||||
* Passes the chunks in the buffer queue to the writer and returns
|
||||
* the amount of buf written. A writer may return -1 and CURLE_AGAIN
|
||||
|
||||
+92
-163
@@ -37,48 +37,38 @@
|
||||
|
||||
struct cf_dns_ctx {
|
||||
struct Curl_dns_entry *dns;
|
||||
struct Curl_peer *peer;
|
||||
CURLcode resolv_result;
|
||||
uint32_t resolv_id;
|
||||
uint16_t port;
|
||||
uint8_t dns_queries;
|
||||
uint8_t transport;
|
||||
BIT(started);
|
||||
BIT(announced);
|
||||
BIT(abstract_unix_socket);
|
||||
BIT(complete_resolve);
|
||||
BIT(for_proxy);
|
||||
char hostname[1];
|
||||
};
|
||||
|
||||
static struct cf_dns_ctx *cf_dns_ctx_create(struct Curl_easy *data,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t dns_queries,
|
||||
const char *hostname,
|
||||
uint16_t port, uint8_t transport,
|
||||
bool abstract_unix_socket,
|
||||
uint8_t transport,
|
||||
bool for_proxy,
|
||||
bool complete_resolve,
|
||||
struct Curl_dns_entry *dns)
|
||||
bool complete_resolve)
|
||||
{
|
||||
struct cf_dns_ctx *ctx;
|
||||
size_t hlen = strlen(hostname);
|
||||
|
||||
ctx = curlx_calloc(1, sizeof(*ctx) + hlen);
|
||||
ctx = curlx_calloc(1, sizeof(*ctx));
|
||||
if(!ctx)
|
||||
return NULL;
|
||||
|
||||
ctx->port = port;
|
||||
Curl_peer_link(&ctx->peer, peer);
|
||||
ctx->dns_queries = dns_queries;
|
||||
ctx->transport = transport;
|
||||
ctx->abstract_unix_socket = abstract_unix_socket;
|
||||
ctx->for_proxy = for_proxy;
|
||||
ctx->complete_resolve = complete_resolve;
|
||||
ctx->dns = Curl_dns_entry_link(data, dns);
|
||||
ctx->started = !!ctx->dns;
|
||||
if(hlen)
|
||||
memcpy(ctx->hostname, hostname, hlen);
|
||||
|
||||
CURL_TRC_DNS(data, "created DNS filter for %s:%u, transport=%x, queries=%x",
|
||||
ctx->hostname, ctx->port, ctx->transport, ctx->dns_queries);
|
||||
peer->hostname, peer->port, ctx->transport, ctx->dns_queries);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@@ -86,6 +76,7 @@ static void cf_dns_ctx_destroy(struct Curl_easy *data,
|
||||
struct cf_dns_ctx *ctx)
|
||||
{
|
||||
if(ctx) {
|
||||
Curl_peer_unlink(&ctx->peer);
|
||||
Curl_dns_entry_unlink(data, &ctx->dns);
|
||||
curlx_free(ctx);
|
||||
}
|
||||
@@ -131,16 +122,14 @@ static void cf_dns_report(struct Curl_cfilter *cf,
|
||||
!dns->hostname[0] || Curl_host_is_ipnum(dns->hostname))
|
||||
return;
|
||||
|
||||
switch(ctx->transport) {
|
||||
case TRNSPRT_UNIX:
|
||||
if(ctx->peer->unix_socket) {
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
CURL_TRC_CF(data, cf, "resolved unix domain %s",
|
||||
Curl_conn_get_unix_path(data->conn));
|
||||
CURL_TRC_CF(data, cf, "resolved unix://%s", ctx->peer->hostname);
|
||||
#else
|
||||
DEBUGASSERT(0);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
}
|
||||
else {
|
||||
curlx_dyn_init(&tmp, 1024);
|
||||
infof(data, "Host %s:%u was resolved.", dns->hostname, dns->port);
|
||||
#ifdef CURLRES_IPV6
|
||||
@@ -161,7 +150,6 @@ static void cf_dns_report(struct Curl_cfilter *cf,
|
||||
}
|
||||
#endif
|
||||
curlx_dyn_free(&tmp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
@@ -181,24 +169,19 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf,
|
||||
|
||||
*pdns = NULL;
|
||||
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
if(ctx->transport == TRNSPRT_UNIX) {
|
||||
CURL_TRC_CF(data, cf, "resolve unix socket %s", ctx->hostname);
|
||||
return Curl_resolv_unix(data, ctx->hostname,
|
||||
(bool)cf->conn->bits.abstract_unix_socket, pdns);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Resolve target host right on */
|
||||
CURL_TRC_CF(data, cf, "cf_dns_start host %s:%u", ctx->hostname, ctx->port);
|
||||
if(Curl_is_ipv4addr(ctx->hostname))
|
||||
CURL_TRC_CF(data, cf, "cf_dns_start %s %s:%u",
|
||||
ctx->peer->unix_socket ? "unix-domain-socket" : "host",
|
||||
ctx->peer->hostname, ctx->peer->port);
|
||||
if(ctx->peer->unix_socket)
|
||||
ctx->dns_queries = 0;
|
||||
else if(Curl_is_ipv4addr(ctx->peer->hostname))
|
||||
ctx->dns_queries |= CURL_DNSQ_A;
|
||||
#ifdef USE_IPV6
|
||||
else if(Curl_is_ipaddr(ctx->hostname)) /* not ipv4, must be ipv6 then */
|
||||
else if(ctx->peer->ipv6)
|
||||
ctx->dns_queries |= CURL_DNSQ_AAAA;
|
||||
#endif
|
||||
result = Curl_resolv(data, ctx->dns_queries,
|
||||
ctx->hostname, ctx->port, ctx->transport,
|
||||
|
||||
result = Curl_resolv(data, ctx->peer, ctx->dns_queries, ctx->transport,
|
||||
(bool)ctx->for_proxy, timeout_ms,
|
||||
&ctx->resolv_id, pdns);
|
||||
DEBUGASSERT(!result || !*pdns);
|
||||
@@ -211,14 +194,14 @@ static CURLcode cf_dns_start(struct Curl_cfilter *cf,
|
||||
}
|
||||
else if(result == CURLE_OPERATION_TIMEDOUT) { /* took too long */
|
||||
failf(data, "Failed to resolve '%s' with timeout after %"
|
||||
FMT_TIMEDIFF_T " ms", ctx->hostname,
|
||||
FMT_TIMEDIFF_T " ms", ctx->peer->hostname,
|
||||
curlx_ptimediff_ms(Curl_pgrs_now(data),
|
||||
&data->progress.t_startsingle));
|
||||
return CURLE_OPERATION_TIMEDOUT;
|
||||
}
|
||||
else {
|
||||
DEBUGASSERT(result);
|
||||
failf(data, "Could not resolve: %s", ctx->hostname);
|
||||
failf(data, "Could not resolve: %s", ctx->peer->hostname);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -281,7 +264,7 @@ static CURLcode cf_dns_connect(struct Curl_cfilter *cf,
|
||||
}
|
||||
|
||||
if(ctx->resolv_result) {
|
||||
CURL_TRC_CF(data, cf, "error resolving: %d", ctx->resolv_result);
|
||||
CURL_TRC_CF(data, cf, "error resolving: %d", (int)ctx->resolv_result);
|
||||
return ctx->resolv_result;
|
||||
}
|
||||
|
||||
@@ -327,13 +310,6 @@ static void cf_dns_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
cf_dns_ctx_destroy(data, ctx);
|
||||
}
|
||||
|
||||
static void cf_dns_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
cf->connected = FALSE;
|
||||
if(cf->next)
|
||||
cf->next->cft->do_close(cf->next, data);
|
||||
}
|
||||
|
||||
static CURLcode cf_dns_adjust_pollset(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct easy_pollset *ps)
|
||||
@@ -379,7 +355,6 @@ struct Curl_cftype Curl_cft_dns = {
|
||||
CURL_LOG_LVL_NONE,
|
||||
cf_dns_destroy,
|
||||
cf_dns_connect,
|
||||
cf_dns_close,
|
||||
Curl_cf_def_shutdown,
|
||||
cf_dns_adjust_pollset,
|
||||
Curl_cf_def_data_pending,
|
||||
@@ -393,23 +368,19 @@ struct Curl_cftype Curl_cft_dns = {
|
||||
|
||||
static CURLcode cf_dns_create(struct Curl_cfilter **pcf,
|
||||
struct Curl_easy *data,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t dns_queries,
|
||||
const char *hostname,
|
||||
uint16_t port,
|
||||
uint8_t transport,
|
||||
bool abstract_unix_socket,
|
||||
bool for_proxy,
|
||||
bool complete_resolve,
|
||||
struct Curl_dns_entry *dns)
|
||||
bool complete_resolve)
|
||||
{
|
||||
struct Curl_cfilter *cf = NULL;
|
||||
struct cf_dns_ctx *ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
(void)data;
|
||||
ctx = cf_dns_ctx_create(data, dns_queries, hostname, port, transport,
|
||||
abstract_unix_socket, for_proxy,
|
||||
complete_resolve, dns);
|
||||
ctx = cf_dns_ctx_create(data, peer, dns_queries, transport,
|
||||
for_proxy, complete_resolve);
|
||||
if(!ctx) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
@@ -424,88 +395,28 @@ out:
|
||||
return result;
|
||||
}
|
||||
|
||||
/* Create a "resolv" filter for the transfer's connection. Figures
|
||||
* out the hostname/path and port where to connect to. */
|
||||
static CURLcode cf_dns_conn_create(struct Curl_cfilter **pcf,
|
||||
struct Curl_easy *data,
|
||||
uint8_t dns_queries,
|
||||
uint8_t transport,
|
||||
bool complete_resolve,
|
||||
struct Curl_dns_entry *dns)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
const char *hostname = NULL;
|
||||
uint16_t port = 0;
|
||||
bool abstract_unix_socket = FALSE, for_proxy = FALSE;
|
||||
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
{
|
||||
const char *unix_path = Curl_conn_get_unix_path(conn);
|
||||
if(unix_path) {
|
||||
DEBUGASSERT(transport == TRNSPRT_UNIX);
|
||||
hostname = unix_path;
|
||||
abstract_unix_socket = (bool)conn->bits.abstract_unix_socket;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(!hostname && conn->bits.proxy) {
|
||||
for_proxy = TRUE;
|
||||
hostname = conn->bits.socksproxy ?
|
||||
conn->socks_proxy.host.name : conn->http_proxy.host.name;
|
||||
port = conn->bits.socksproxy ?
|
||||
conn->socks_proxy.port : conn->http_proxy.port;
|
||||
}
|
||||
#endif
|
||||
if(!hostname) {
|
||||
struct hostname *ehost;
|
||||
ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host;
|
||||
/* If not connecting via a proxy, extract the port from the URL, if it is
|
||||
* there, thus overriding any defaults that might have been set above. */
|
||||
hostname = ehost->name;
|
||||
port = conn->bits.conn_to_port ?
|
||||
conn->conn_to_port : (uint16_t)conn->remote_port;
|
||||
}
|
||||
|
||||
if(!hostname) {
|
||||
DEBUGASSERT(0);
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
return cf_dns_create(pcf, data, dns_queries,
|
||||
hostname, port, transport,
|
||||
abstract_unix_socket, for_proxy,
|
||||
complete_resolve, dns);
|
||||
}
|
||||
|
||||
/* Adds a "resolv" filter at the top of the connection's filter chain.
|
||||
* For FIRSTSOCKET, the `dns` parameter may be NULL. The filter will
|
||||
* figure out hostname and port to connect to and start the DNS resolve
|
||||
* on the first connect attempt.
|
||||
* For SECONDARYSOCKET, the `dns` parameter must be given.
|
||||
*/
|
||||
* The filter will resolve the peer on the first connect attempt. */
|
||||
CURLcode Curl_cf_dns_add(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t dns_queries,
|
||||
uint8_t transport,
|
||||
struct Curl_dns_entry *dns)
|
||||
uint8_t transport)
|
||||
{
|
||||
struct Curl_cfilter *cf = NULL;
|
||||
bool for_proxy = FALSE;
|
||||
CURLcode result;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
if(sockindex == FIRSTSOCKET)
|
||||
result = cf_dns_conn_create(&cf, data, dns_queries, transport, FALSE, dns);
|
||||
else if(dns) {
|
||||
result = cf_dns_create(&cf, data, dns_queries,
|
||||
dns->hostname, dns->port, transport,
|
||||
FALSE, FALSE, FALSE, dns);
|
||||
}
|
||||
else {
|
||||
DEBUGASSERT(0);
|
||||
result = CURLE_FAILED_INIT;
|
||||
}
|
||||
if(!peer)
|
||||
return CURLE_FAILED_INIT;
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
for_proxy = (peer == conn->socks_proxy.peer) ||
|
||||
(peer == conn->http_proxy.peer);
|
||||
#endif
|
||||
|
||||
result = cf_dns_create(&cf, data, peer, dns_queries, transport,
|
||||
for_proxy, FALSE);
|
||||
if(result)
|
||||
goto out;
|
||||
Curl_conn_cf_add(data, conn, sockindex, cf);
|
||||
@@ -514,7 +425,7 @@ out:
|
||||
}
|
||||
|
||||
/* Insert a new "resolv" filter directly after `cf`. It will
|
||||
* start a DNS resolve for the given hostnmae and port on the
|
||||
* start a DNS resolve for the given peer on the
|
||||
* first connect attempt.
|
||||
* See socks.c on how this is used to make a non-blocking DNS
|
||||
* resolve during connect.
|
||||
@@ -522,17 +433,15 @@ out:
|
||||
CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at,
|
||||
struct Curl_easy *data,
|
||||
uint8_t dns_queries,
|
||||
const char *hostname,
|
||||
uint16_t port,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t transport,
|
||||
bool complete_resolve)
|
||||
{
|
||||
struct Curl_cfilter *cf;
|
||||
CURLcode result;
|
||||
|
||||
result = cf_dns_create(&cf, data, dns_queries,
|
||||
hostname, port, transport,
|
||||
FALSE, FALSE, complete_resolve, NULL);
|
||||
result = cf_dns_create(&cf, data, peer, dns_queries, transport,
|
||||
FALSE, complete_resolve);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
@@ -543,29 +452,35 @@ CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at,
|
||||
/* Return the resolv result from the first "resolv" filter, starting
|
||||
* the given filter `cf` downwards.
|
||||
*/
|
||||
static CURLcode cf_dns_result(struct Curl_cfilter *cf)
|
||||
static CURLcode cf_dns_result(struct Curl_cfilter *cf,
|
||||
struct Curl_peer *peer)
|
||||
{
|
||||
for(; cf; cf = cf->next) {
|
||||
if(cf->cft == &Curl_cft_dns) {
|
||||
struct cf_dns_ctx *ctx = cf->ctx;
|
||||
if(ctx->dns || ctx->resolv_result)
|
||||
return ctx->resolv_result;
|
||||
return CURLE_AGAIN;
|
||||
if(Curl_peer_same_destination(ctx->peer, peer)) {
|
||||
if(ctx->dns || ctx->resolv_result)
|
||||
return ctx->resolv_result;
|
||||
return CURLE_AGAIN;
|
||||
}
|
||||
return CURLE_OK; /* ok, but no results */
|
||||
}
|
||||
}
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
|
||||
/* Return the result of the DNS resolution. Searches for a "resolv"
|
||||
/* Return the result of the DNS resolution for peer. Searches for a "resolv"
|
||||
* filter from the top of the filter chain down. Returns
|
||||
* - CURLE_AGAIN when not done yet
|
||||
* - CURLE_OK when DNS was successfully resolved
|
||||
* - CURLR_FAILED_INIT when no resolv filter was found
|
||||
* - error returned by the DNS resolv
|
||||
*/
|
||||
CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex)
|
||||
CURLcode Curl_conn_dns_result(struct connectdata *conn,
|
||||
int sockindex,
|
||||
struct Curl_peer *peer)
|
||||
{
|
||||
return cf_dns_result(conn->cfilter[sockindex]);
|
||||
return cf_dns_result(conn->cfilter[sockindex], peer);
|
||||
}
|
||||
|
||||
static const struct Curl_addrinfo *cf_dns_get_nth_ai(
|
||||
@@ -598,6 +513,7 @@ static const struct Curl_addrinfo *cf_dns_get_nth_ai(
|
||||
*/
|
||||
const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct Curl_peer *peer,
|
||||
int ai_family,
|
||||
unsigned int index)
|
||||
{
|
||||
@@ -605,12 +521,14 @@ const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf,
|
||||
for(; cf; cf = cf->next) {
|
||||
if(cf->cft == &Curl_cft_dns) {
|
||||
struct cf_dns_ctx *ctx = cf->ctx;
|
||||
if(ctx->resolv_result)
|
||||
return NULL;
|
||||
else if(ctx->dns)
|
||||
return cf_dns_get_nth_ai(cf, ctx->dns->addr, ai_family, index);
|
||||
else
|
||||
return Curl_resolv_get_ai(data, ctx->resolv_id, ai_family, index);
|
||||
if(Curl_peer_same_destination(ctx->peer, peer)) {
|
||||
if(ctx->resolv_result)
|
||||
return NULL;
|
||||
else if(ctx->dns)
|
||||
return cf_dns_get_nth_ai(cf, ctx->dns->addr, ai_family, index);
|
||||
else
|
||||
return Curl_resolv_get_ai(data, ctx->resolv_id, ai_family, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
@@ -621,11 +539,14 @@ const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf,
|
||||
* not done yet or if no address for the family exists, returns NULL.
|
||||
*/
|
||||
const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data,
|
||||
int sockindex, int ai_family,
|
||||
struct Curl_peer *peer,
|
||||
int sockindex,
|
||||
int ai_family,
|
||||
unsigned int index)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
return Curl_cf_dns_get_ai(conn->cfilter[sockindex], data, ai_family, index);
|
||||
return Curl_cf_dns_get_ai(conn->cfilter[sockindex], data, peer,
|
||||
ai_family, index);
|
||||
}
|
||||
|
||||
#ifdef USE_HTTPSRR
|
||||
@@ -633,35 +554,43 @@ const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data,
|
||||
* connection. If the DNS resolving is not done yet or if there
|
||||
* is no HTTPS-RR info, returns NULL.
|
||||
*/
|
||||
const struct Curl_https_rrinfo *Curl_conn_dns_get_https(struct Curl_easy *data,
|
||||
int sockindex)
|
||||
const struct Curl_https_rrinfo *
|
||||
Curl_conn_dns_get_https(struct Curl_easy *data,
|
||||
int sockindex,
|
||||
struct Curl_peer *peer)
|
||||
{
|
||||
struct Curl_cfilter *cf = data->conn->cfilter[sockindex];
|
||||
for(; cf; cf = cf->next) {
|
||||
if(cf->cft == &Curl_cft_dns) {
|
||||
struct cf_dns_ctx *ctx = cf->ctx;
|
||||
if(ctx->dns)
|
||||
return ctx->dns->hinfo;
|
||||
else
|
||||
return Curl_resolv_get_https(data, ctx->resolv_id);
|
||||
if(Curl_peer_same_destination(ctx->peer, peer)) {
|
||||
if(ctx->dns)
|
||||
return ctx->dns->hinfo;
|
||||
else
|
||||
return Curl_resolv_get_https(data, ctx->resolv_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex)
|
||||
bool Curl_conn_dns_resolved_https(struct Curl_easy *data,
|
||||
int sockindex,
|
||||
struct Curl_peer *peer)
|
||||
{
|
||||
struct Curl_cfilter *cf = data->conn->cfilter[sockindex];
|
||||
for(; cf; cf = cf->next) {
|
||||
if(cf->cft == &Curl_cft_dns) {
|
||||
struct cf_dns_ctx *ctx = cf->ctx;
|
||||
if(ctx->dns)
|
||||
return TRUE;
|
||||
else
|
||||
return Curl_resolv_knows_https(data, ctx->resolv_id);
|
||||
if(Curl_peer_same_destination(ctx->peer, peer)) {
|
||||
if(ctx->dns)
|
||||
return TRUE;
|
||||
else
|
||||
return Curl_resolv_knows_https(data, ctx->resolv_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif /* USE_HTTPSRR */
|
||||
|
||||
@@ -29,41 +29,49 @@ struct Curl_easy;
|
||||
struct connectdata;
|
||||
struct Curl_dns_entry;
|
||||
struct Curl_addrinfo;
|
||||
struct Curl_peer;
|
||||
|
||||
CURLcode Curl_cf_dns_add(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t dns_queries,
|
||||
uint8_t transport,
|
||||
struct Curl_dns_entry *dns);
|
||||
uint8_t transport);
|
||||
|
||||
CURLcode Curl_cf_dns_insert_after(struct Curl_cfilter *cf_at,
|
||||
struct Curl_easy *data,
|
||||
uint8_t dns_queries,
|
||||
const char *hostname,
|
||||
uint16_t port,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t transport,
|
||||
bool complete_resolve);
|
||||
|
||||
CURLcode Curl_conn_dns_result(struct connectdata *conn, int sockindex);
|
||||
CURLcode Curl_conn_dns_result(struct connectdata *conn,
|
||||
int sockindex,
|
||||
struct Curl_peer *peer);
|
||||
|
||||
const struct Curl_addrinfo *Curl_conn_dns_get_ai(struct Curl_easy *data,
|
||||
struct Curl_peer *peer,
|
||||
int sockindex,
|
||||
int ai_family,
|
||||
unsigned int index);
|
||||
|
||||
const struct Curl_addrinfo *Curl_cf_dns_get_ai(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct Curl_peer *peer,
|
||||
int ai_family,
|
||||
unsigned int index);
|
||||
|
||||
#ifdef USE_HTTPSRR
|
||||
const struct Curl_https_rrinfo *Curl_conn_dns_get_https(struct Curl_easy *data,
|
||||
int sockindex);
|
||||
bool Curl_conn_dns_resolved_https(struct Curl_easy *data, int sockindex);
|
||||
const struct Curl_https_rrinfo *
|
||||
Curl_conn_dns_get_https(struct Curl_easy *data,
|
||||
int sockindex,
|
||||
struct Curl_peer *peer);
|
||||
bool Curl_conn_dns_resolved_https(struct Curl_easy *data,
|
||||
int sockindex,
|
||||
struct Curl_peer *peer);
|
||||
#else
|
||||
#define Curl_conn_dns_get_https(a, b) NULL
|
||||
#define Curl_conn_dns_resolved_https(a, b) TRUE
|
||||
#define Curl_conn_dns_get_https(a, b, c) NULL
|
||||
#define Curl_conn_dns_resolved_https(a, b, c) TRUE
|
||||
#endif
|
||||
|
||||
extern struct Curl_cftype Curl_cft_dns;
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
|
||||
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include "urldata.h"
|
||||
#include "curlx/dynbuf.h"
|
||||
#include "sendf.h"
|
||||
@@ -33,6 +35,7 @@
|
||||
#include "http_proxy.h"
|
||||
#include "select.h"
|
||||
#include "progress.h"
|
||||
#include "multiif.h"
|
||||
#include "cfilters.h"
|
||||
#include "cf-h1-proxy.h"
|
||||
#include "connect.h"
|
||||
@@ -40,7 +43,6 @@
|
||||
#include "strcase.h"
|
||||
#include "curlx/strparse.h"
|
||||
|
||||
|
||||
typedef enum {
|
||||
H1_TUNNEL_INIT, /* init/default/no tunnel state */
|
||||
H1_TUNNEL_CONNECT, /* CONNECT request is being send */
|
||||
@@ -52,11 +54,13 @@ typedef enum {
|
||||
|
||||
/* struct for HTTP CONNECT tunneling */
|
||||
struct h1_tunnel_state {
|
||||
struct Curl_peer *dest;
|
||||
struct dynbuf rcvbuf;
|
||||
struct dynbuf request_data;
|
||||
size_t nsent;
|
||||
size_t headerlines;
|
||||
struct Curl_chunker ch;
|
||||
int httpversion;
|
||||
enum keeponval {
|
||||
KEEPON_DONE,
|
||||
KEEPON_CONNECT,
|
||||
@@ -70,6 +74,12 @@ struct h1_tunnel_state {
|
||||
BIT(leading_unfold);
|
||||
};
|
||||
|
||||
/* Persistent context for the H1-PROXY filter */
|
||||
struct cf_h1_proxy_ctx {
|
||||
struct h1_tunnel_state *ts;
|
||||
BIT(udp_tunnel);
|
||||
};
|
||||
|
||||
static bool tunnel_is_established(struct h1_tunnel_state *ts)
|
||||
{
|
||||
return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED);
|
||||
@@ -80,6 +90,12 @@ static bool tunnel_is_failed(struct h1_tunnel_state *ts)
|
||||
return ts && (ts->tunnel_state == H1_TUNNEL_FAILED);
|
||||
}
|
||||
|
||||
static bool h1_proxy_is_udp(struct Curl_cfilter *cf)
|
||||
{
|
||||
struct cf_h1_proxy_ctx *pctx = cf->ctx;
|
||||
return (pctx->udp_tunnel ? TRUE : FALSE);
|
||||
}
|
||||
|
||||
static CURLcode tunnel_reinit(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct h1_tunnel_state *ts)
|
||||
@@ -95,6 +111,8 @@ static CURLcode tunnel_reinit(struct Curl_cfilter *cf,
|
||||
ts->close_connection = FALSE;
|
||||
ts->maybe_folded = FALSE;
|
||||
ts->leading_unfold = FALSE;
|
||||
ts->nsent = 0;
|
||||
ts->headerlines = 0;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
@@ -156,7 +174,9 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf,
|
||||
|
||||
case H1_TUNNEL_ESTABLISHED:
|
||||
CURL_TRC_CF(data, cf, "new tunnel state 'established'");
|
||||
infof(data, "CONNECT phase completed");
|
||||
infof(data, "CONNECT%s phase completed for HTTP proxy",
|
||||
h1_proxy_is_udp(cf) ? "-UDP" : "");
|
||||
|
||||
data->state.authproxy.done = TRUE;
|
||||
data->state.authproxy.multipass = FALSE;
|
||||
FALLTHROUGH();
|
||||
@@ -172,23 +192,33 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf,
|
||||
/* If a proxy-authorization header was used for the proxy, then we should
|
||||
make sure that it is not accidentally used for the document request
|
||||
after we have connected. Let's thus free and clear it here. */
|
||||
curlx_safefree(data->req.proxyuserpwd);
|
||||
curlx_safefree(data->req.hd_proxy_auth);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void tunnel_free(struct Curl_cfilter *cf,
|
||||
static void tunnel_free(struct h1_tunnel_state *ts,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
if(ts) {
|
||||
Curl_peer_unlink(&ts->dest);
|
||||
curlx_dyn_free(&ts->rcvbuf);
|
||||
curlx_dyn_free(&ts->request_data);
|
||||
Curl_httpchunk_free(data, &ts->ch);
|
||||
curlx_free(ts);
|
||||
}
|
||||
}
|
||||
|
||||
static void cf_tunnel_free(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
if(cf) {
|
||||
struct h1_tunnel_state *ts = cf->ctx;
|
||||
struct cf_h1_proxy_ctx *pctx = cf->ctx;
|
||||
struct h1_tunnel_state *ts = pctx ? pctx->ts : NULL;
|
||||
if(ts) {
|
||||
h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
|
||||
curlx_dyn_free(&ts->rcvbuf);
|
||||
curlx_dyn_free(&ts->request_data);
|
||||
Curl_httpchunk_free(data, &ts->ch);
|
||||
curlx_free(ts);
|
||||
cf->ctx = NULL;
|
||||
tunnel_free(ts, data);
|
||||
pctx->ts = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -206,20 +236,21 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf,
|
||||
int http_minor;
|
||||
CURLcode result;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
/* This only happens if we have looped here due to authentication reasons,
|
||||
and we do not really use the newly cloned URL here then. Free it. */
|
||||
curlx_safefree(data->req.newurl);
|
||||
|
||||
result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1);
|
||||
result = Curl_http_proxy_create_tunnel_request(&req, cf, data, ts->dest,
|
||||
PROXY_HTTP_V1,
|
||||
h1_proxy_is_udp(cf));
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
infof(data, "Establish HTTP proxy tunnel to %s", req->authority);
|
||||
|
||||
curlx_dyn_reset(&ts->request_data);
|
||||
ts->nsent = 0;
|
||||
ts->headerlines = 0;
|
||||
http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1;
|
||||
http_minor = ts->httpversion % 10;
|
||||
|
||||
result = Curl_h1_req_write_head(req, http_minor, &ts->request_data);
|
||||
if(!result)
|
||||
@@ -268,6 +299,92 @@ out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode on_resp_header_udp(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct h1_tunnel_state *ts,
|
||||
const char *header)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct SingleRequest *k = &data->req;
|
||||
|
||||
if((checkprefix("WWW-Authenticate:", header) && (401 == k->httpcode)) ||
|
||||
(checkprefix("Proxy-authenticate:", header) && (407 == k->httpcode))) {
|
||||
|
||||
bool proxy = (k->httpcode == 407);
|
||||
char *auth = Curl_copy_header_value(header);
|
||||
if(!auth)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
CURL_TRC_CF(data, cf, "CONNECT-UDP: fwd auth header '%s'", header);
|
||||
result = Curl_http_input_auth(data, proxy, auth);
|
||||
|
||||
curlx_free(auth);
|
||||
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
else if(checkprefix("Content-Length:", header)) {
|
||||
if(k->httpcode / 100 == 2 || k->httpcode == 101) {
|
||||
infof(data, "Ignoring Content-Length in CONNECT-UDP %03d response",
|
||||
k->httpcode);
|
||||
}
|
||||
else {
|
||||
const char *p = header + strlen("Content-Length:");
|
||||
if(curlx_str_numblanks(&p, &ts->cl)) {
|
||||
failf(data, "Unsupported Content-Length value");
|
||||
return CURLE_WEIRD_SERVER_REPLY;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(checkprefix("Transfer-Encoding:", header)) {
|
||||
if(k->httpcode / 100 == 2 || k->httpcode == 101) {
|
||||
infof(data, "Ignoring Transfer-Encoding in "
|
||||
"CONNECT-UDP %03d response", k->httpcode);
|
||||
}
|
||||
else if(Curl_compareheader(header,
|
||||
STRCONST("Transfer-Encoding:"),
|
||||
STRCONST("chunked"))) {
|
||||
CURL_TRC_CF(data, cf, "CONNECT-UDP Response --> "
|
||||
"Transfer-Encoding: chunked");
|
||||
ts->chunked_encoding = TRUE;
|
||||
/* reset our chunky engine */
|
||||
Curl_httpchunk_reset(data, &ts->ch, TRUE);
|
||||
}
|
||||
}
|
||||
else if(checkprefix("Capsule-protocol:", header)) {
|
||||
if(Curl_compareheader(header,
|
||||
STRCONST("Capsule-protocol:"),
|
||||
STRCONST("?1"))) {
|
||||
CURL_TRC_CF(data, cf, "CONNECT-UDP Response --> Capsule-protocol: ?1");
|
||||
}
|
||||
}
|
||||
else if(Curl_compareheader(header,
|
||||
STRCONST("Connection:"), STRCONST("close"))) {
|
||||
ts->close_connection = TRUE;
|
||||
CURL_TRC_CF(data, cf, "CONNECT-UDP Response --> Connection: close");
|
||||
}
|
||||
else if(Curl_compareheader(header,
|
||||
STRCONST("Proxy-Connection:"),
|
||||
STRCONST("close"))) {
|
||||
ts->close_connection = TRUE;
|
||||
CURL_TRC_CF(data, cf,
|
||||
"CONNECT-UDP Response --> Proxy-Connection: close");
|
||||
}
|
||||
else if(!strncmp(header, "HTTP/1.", 7) &&
|
||||
((header[7] == '0') || (header[7] == '1')) &&
|
||||
(header[8] == ' ') &&
|
||||
ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) &&
|
||||
!ISDIGIT(header[12])) {
|
||||
/* store the HTTP code from the proxy */
|
||||
data->info.httpproxycode = k->httpcode =
|
||||
((header[9] - '0') * 100) +
|
||||
((header[10] - '0') * 10) +
|
||||
(header[11] - '0');
|
||||
CURL_TRC_CF(data, cf, "CONNECT-UDP Response --> %d", k->httpcode);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode on_resp_header(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct h1_tunnel_state *ts,
|
||||
@@ -359,6 +476,12 @@ static CURLcode single_header(struct Curl_cfilter *cf,
|
||||
/* output debug if that is requested */
|
||||
Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len);
|
||||
|
||||
/* a CONNECT response line is handed to the client as a header, so it must
|
||||
pass the same checks as a regular response header before delivery */
|
||||
result = Curl_verify_header(data, linep, line_len);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
/* send the header to the callback */
|
||||
writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
|
||||
(ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
|
||||
@@ -406,7 +529,13 @@ static CURLcode single_header(struct Curl_cfilter *cf,
|
||||
return result;
|
||||
}
|
||||
|
||||
result = on_resp_header(cf, data, ts, linep);
|
||||
if(h1_proxy_is_udp(cf)) {
|
||||
result = on_resp_header_udp(cf, data, ts, linep);
|
||||
}
|
||||
else {
|
||||
result = on_resp_header(cf, data, ts, linep);
|
||||
}
|
||||
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
@@ -448,8 +577,15 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
|
||||
}
|
||||
|
||||
if(!nread) {
|
||||
if(ts->maybe_folded) {
|
||||
/* EOF right after LF: finalize the pending header line. */
|
||||
result = single_header(cf, data, ts);
|
||||
if(result)
|
||||
return result;
|
||||
ts->maybe_folded = FALSE;
|
||||
}
|
||||
if(data->set.proxyauth && data->state.authproxy.avail &&
|
||||
data->req.proxyuserpwd) {
|
||||
data->req.hd_proxy_auth) {
|
||||
/* proxy auth was requested and there was proxy auth available,
|
||||
then deem this as "mere" proxy disconnect */
|
||||
ts->close_connection = TRUE;
|
||||
@@ -539,12 +675,16 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
|
||||
ts->maybe_folded = TRUE;
|
||||
}
|
||||
|
||||
if(result)
|
||||
return result;
|
||||
} /* while there is buffer left and loop is requested */
|
||||
|
||||
if(error)
|
||||
result = CURLE_RECV_ERROR;
|
||||
*done = (ts->keepon == KEEPON_DONE);
|
||||
if(!result && *done && data->info.httpproxycode / 100 != 2) {
|
||||
if(!result && *done &&
|
||||
data->info.httpproxycode / 100 != 2 &&
|
||||
!(h1_proxy_is_udp(cf) && data->info.httpproxycode == 101)) {
|
||||
/* Deal with the possibly already received authenticate
|
||||
headers. 'newurl' is set to a new URL if we must loop. */
|
||||
result = Curl_http_auth_act(data);
|
||||
@@ -597,7 +737,8 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
|
||||
CURL_TRC_CF(data, cf, "CONNECT receive");
|
||||
result = recv_CONNECT_resp(cf, data, ts, &done);
|
||||
if(result)
|
||||
CURL_TRC_CF(data, cf, "error receiving CONNECT response: %d", result);
|
||||
CURL_TRC_CF(data, cf, "error receiving CONNECT response: %d",
|
||||
(int)result);
|
||||
if(!result)
|
||||
result = Curl_pgrsUpdate(data);
|
||||
/* error or not complete yet. return for more multi-multi */
|
||||
@@ -623,9 +764,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
|
||||
*/
|
||||
CURL_TRC_CF(data, cf, "CONNECT need to close+open");
|
||||
infof(data, "Connect me again please");
|
||||
Curl_conn_cf_close(cf, data);
|
||||
result = Curl_conn_cf_connect(cf->next, data, &done);
|
||||
goto out;
|
||||
return CURLE_AGAIN;
|
||||
}
|
||||
else {
|
||||
/* staying on this connection, reset state */
|
||||
@@ -641,17 +780,36 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
|
||||
} while(data->req.newurl);
|
||||
|
||||
DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE);
|
||||
if(data->info.httpproxycode / 100 != 2) {
|
||||
/* a non-2xx response and we have no next URL to try. */
|
||||
curlx_safefree(data->req.newurl);
|
||||
h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
|
||||
failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode);
|
||||
return CURLE_COULDNT_CONNECT;
|
||||
if(h1_proxy_is_udp(cf)) {
|
||||
/* RFC 9298: Accept 101 Upgrade for HTTP/1.1 and
|
||||
* 2xx responses for HTTP/2 and HTTP/3 proxies. */
|
||||
if(data->info.httpproxycode / 100 != 2 &&
|
||||
data->info.httpproxycode != 101) {
|
||||
curlx_safefree(data->req.newurl);
|
||||
h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
|
||||
failf(data, "CONNECT-UDP tunnel failed, response %d",
|
||||
data->req.httpcode);
|
||||
return CURLE_COULDNT_CONNECT;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(data->info.httpproxycode / 100 != 2) {
|
||||
/* a non-2xx response and we have no next URL to try. */
|
||||
curlx_safefree(data->req.newurl);
|
||||
h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
|
||||
failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode);
|
||||
return CURLE_COULDNT_CONNECT;
|
||||
}
|
||||
}
|
||||
/* 2xx response, SUCCESS! */
|
||||
/* 101 Switching Protocol for CONNECT-UDP */
|
||||
h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data);
|
||||
infof(data, "CONNECT tunnel established, response %d",
|
||||
data->info.httpproxycode);
|
||||
if(h1_proxy_is_udp(cf))
|
||||
infof(data, "CONNECT-UDP tunnel established, response %d",
|
||||
data->info.httpproxycode);
|
||||
else
|
||||
infof(data, "CONNECT tunnel established, response %d",
|
||||
data->info.httpproxycode);
|
||||
result = CURLE_OK;
|
||||
|
||||
out:
|
||||
@@ -665,7 +823,8 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
|
||||
bool *done)
|
||||
{
|
||||
CURLcode result;
|
||||
struct h1_tunnel_state *ts = cf->ctx;
|
||||
struct cf_h1_proxy_ctx *pctx = cf->ctx;
|
||||
struct h1_tunnel_state *ts = pctx->ts;
|
||||
|
||||
if(cf->connected) {
|
||||
*done = TRUE;
|
||||
@@ -682,7 +841,7 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
|
||||
result = tunnel_init(cf, data, &ts);
|
||||
if(result)
|
||||
return result;
|
||||
cf->ctx = ts;
|
||||
pctx->ts = ts;
|
||||
}
|
||||
|
||||
/* We want "seamless" operations through HTTP proxy tunnel */
|
||||
@@ -690,18 +849,17 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
|
||||
result = H1_CONNECT(cf, data, ts);
|
||||
if(result)
|
||||
goto out;
|
||||
curlx_safefree(data->req.proxyuserpwd);
|
||||
curlx_safefree(data->req.hd_proxy_auth);
|
||||
|
||||
out:
|
||||
*done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
|
||||
*done = (result == CURLE_OK) && tunnel_is_established(pctx->ts);
|
||||
if(*done) {
|
||||
cf->connected = TRUE;
|
||||
/* The real request will follow the CONNECT, reset request partially */
|
||||
Curl_req_soft_reset(&data->req, data);
|
||||
Curl_client_reset(data);
|
||||
Curl_pgrsReset(data);
|
||||
|
||||
tunnel_free(cf, data);
|
||||
cf_tunnel_free(cf, data);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -710,7 +868,8 @@ static CURLcode cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct easy_pollset *ps)
|
||||
{
|
||||
struct h1_tunnel_state *ts = cf->ctx;
|
||||
struct cf_h1_proxy_ctx *pctx = cf->ctx;
|
||||
struct h1_tunnel_state *ts = pctx->ts;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(!cf->connected) {
|
||||
@@ -730,28 +889,52 @@ static CURLcode cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
|
||||
else
|
||||
result = Curl_pollset_set_out_only(data, ps, sock);
|
||||
}
|
||||
else {
|
||||
if(cf->next)
|
||||
result = cf->next->cft->adjust_pollset(cf->next, data, ps);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool cf_h1_proxy_data_pending(struct Curl_cfilter *cf,
|
||||
const struct Curl_easy *data)
|
||||
{
|
||||
return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
|
||||
}
|
||||
|
||||
static void cf_h1_proxy_destroy(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
CURL_TRC_CF(data, cf, "destroy");
|
||||
tunnel_free(cf, data);
|
||||
cf_tunnel_free(cf, data);
|
||||
curlx_safefree(cf->ctx);
|
||||
}
|
||||
|
||||
static void cf_h1_proxy_close(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
static CURLcode cf_h1_proxy_query(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
int query, int *pres1, void *pres2)
|
||||
{
|
||||
CURL_TRC_CF(data, cf, "close");
|
||||
if(cf) {
|
||||
cf->connected = FALSE;
|
||||
if(cf->ctx) {
|
||||
h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data);
|
||||
}
|
||||
if(cf->next)
|
||||
cf->next->cft->do_close(cf->next, data);
|
||||
struct cf_h1_proxy_ctx *pctx = cf->ctx;
|
||||
struct h1_tunnel_state *ts = pctx ? pctx->ts : NULL;
|
||||
switch(query) {
|
||||
case CF_QUERY_HOST_PORT:
|
||||
if(!ts || !ts->dest)
|
||||
break;
|
||||
*pres1 = (int)ts->dest->port;
|
||||
*((const char **)pres2) = ts->dest->hostname;
|
||||
return CURLE_OK;
|
||||
case CF_QUERY_ALPN_NEGOTIATED: {
|
||||
const char **palpn = pres2;
|
||||
DEBUGASSERT(palpn);
|
||||
*palpn = NULL;
|
||||
return CURLE_OK;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return cf->next ?
|
||||
cf->next->cft->query(cf->next, data, query, pres1, pres2) :
|
||||
CURLE_UNKNOWN_OPTION;
|
||||
}
|
||||
|
||||
struct Curl_cftype Curl_cft_h1_proxy = {
|
||||
@@ -760,28 +943,62 @@ struct Curl_cftype Curl_cft_h1_proxy = {
|
||||
0,
|
||||
cf_h1_proxy_destroy,
|
||||
cf_h1_proxy_connect,
|
||||
cf_h1_proxy_close,
|
||||
Curl_cf_def_shutdown,
|
||||
cf_h1_proxy_adjust_pollset,
|
||||
Curl_cf_def_data_pending,
|
||||
cf_h1_proxy_data_pending,
|
||||
Curl_cf_def_send,
|
||||
Curl_cf_def_recv,
|
||||
Curl_cf_def_cntrl,
|
||||
Curl_cf_def_conn_is_alive,
|
||||
Curl_cf_def_conn_keep_alive,
|
||||
Curl_cf_http_proxy_query,
|
||||
cf_h1_proxy_query,
|
||||
};
|
||||
|
||||
CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at,
|
||||
struct Curl_easy *data)
|
||||
struct Curl_easy *data,
|
||||
struct Curl_peer *dest,
|
||||
int httpversion,
|
||||
bool udp_tunnel)
|
||||
{
|
||||
struct Curl_cfilter *cf;
|
||||
struct cf_h1_proxy_ctx *pctx;
|
||||
struct h1_tunnel_state *ts;
|
||||
CURLcode result;
|
||||
|
||||
(void)data;
|
||||
result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL);
|
||||
if(!result)
|
||||
Curl_conn_cf_insert_after(cf_at, cf);
|
||||
if(!dest)
|
||||
return CURLE_FAILED_INIT;
|
||||
if((httpversion < 10) || (httpversion >= 20))
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
ts = curlx_calloc(1, sizeof(*ts));
|
||||
if(!ts) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
Curl_peer_link(&ts->dest, dest);
|
||||
ts->httpversion = httpversion;
|
||||
curlx_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
|
||||
curlx_dyn_init(&ts->request_data, DYN_HTTP_REQUEST);
|
||||
Curl_httpchunk_init(data, &ts->ch, TRUE);
|
||||
|
||||
pctx = curlx_calloc(1, sizeof(*pctx));
|
||||
if(!pctx) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
pctx->udp_tunnel = udp_tunnel;
|
||||
pctx->ts = ts;
|
||||
result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, pctx);
|
||||
if(result) {
|
||||
curlx_free(pctx);
|
||||
goto out;
|
||||
}
|
||||
ts = NULL;
|
||||
Curl_conn_cf_insert_after(cf_at, cf);
|
||||
|
||||
out:
|
||||
tunnel_free(ts, data);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,13 @@
|
||||
|
||||
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
|
||||
|
||||
struct Curl_peer;
|
||||
|
||||
CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at,
|
||||
struct Curl_easy *data);
|
||||
struct Curl_easy *data,
|
||||
struct Curl_peer *dest,
|
||||
int httpversion,
|
||||
bool udp_tunnel);
|
||||
|
||||
extern struct Curl_cftype Curl_cft_h1_proxy;
|
||||
|
||||
|
||||
@@ -76,30 +76,40 @@ struct tunnel_stream {
|
||||
BIT(reset);
|
||||
};
|
||||
|
||||
static CURLcode tunnel_stream_init(struct Curl_cfilter *cf,
|
||||
struct tunnel_stream *ts)
|
||||
static CURLcode tunnel_stream_init(struct tunnel_stream *ts,
|
||||
struct Curl_peer *dest)
|
||||
{
|
||||
const char *hostname;
|
||||
uint16_t port;
|
||||
bool ipv6_ip;
|
||||
|
||||
ts->state = H2_TUNNEL_INIT;
|
||||
ts->stream_id = -1;
|
||||
Curl_bufq_init2(&ts->recvbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_RECV_CHUNKS,
|
||||
BUFQ_OPT_SOFT_LIMIT);
|
||||
Curl_bufq_init(&ts->sendbuf, PROXY_H2_CHUNK_SIZE, H2_TUNNEL_SEND_CHUNKS);
|
||||
|
||||
Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip);
|
||||
|
||||
/* host:port with IPv6 support */
|
||||
ts->authority = curl_maprintf("%s%s%s:%u", ipv6_ip ? "[" : "", hostname,
|
||||
ipv6_ip ? "]" : "", port);
|
||||
ts->authority = curl_maprintf("%s%s%s:%u", dest->ipv6 ? "[" : "",
|
||||
dest->hostname,
|
||||
dest->ipv6 ? "]" : "",
|
||||
dest->port);
|
||||
if(!ts->authority)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void tunnel_stream_reset(struct tunnel_stream *ts)
|
||||
{
|
||||
Curl_http_resp_free(ts->resp);
|
||||
ts->resp = NULL;
|
||||
Curl_bufq_reset(&ts->recvbuf);
|
||||
Curl_bufq_reset(&ts->sendbuf);
|
||||
ts->stream_id = -1;
|
||||
ts->error = 0;
|
||||
ts->has_final_response = FALSE;
|
||||
ts->closed = FALSE;
|
||||
ts->reset = FALSE;
|
||||
ts->state = H2_TUNNEL_INIT;
|
||||
}
|
||||
|
||||
static void tunnel_stream_clear(struct tunnel_stream *ts)
|
||||
{
|
||||
Curl_http_resp_free(ts->resp);
|
||||
@@ -113,9 +123,11 @@ static void tunnel_stream_clear(struct tunnel_stream *ts)
|
||||
static void h2_tunnel_go_state(struct Curl_cfilter *cf,
|
||||
struct tunnel_stream *ts,
|
||||
h2_tunnel_state new_state,
|
||||
struct Curl_easy *data)
|
||||
struct Curl_easy *data,
|
||||
bool udp_tunnel)
|
||||
{
|
||||
(void)cf;
|
||||
(void)udp_tunnel;
|
||||
|
||||
if(ts->state == new_state)
|
||||
return;
|
||||
@@ -131,7 +143,7 @@ static void h2_tunnel_go_state(struct Curl_cfilter *cf,
|
||||
switch(new_state) {
|
||||
case H2_TUNNEL_INIT:
|
||||
CURL_TRC_CF(data, cf, "[%d] new tunnel state 'init'", ts->stream_id);
|
||||
tunnel_stream_clear(ts);
|
||||
tunnel_stream_reset(ts);
|
||||
break;
|
||||
|
||||
case H2_TUNNEL_CONNECT:
|
||||
@@ -147,7 +159,8 @@ static void h2_tunnel_go_state(struct Curl_cfilter *cf,
|
||||
case H2_TUNNEL_ESTABLISHED:
|
||||
CURL_TRC_CF(data, cf, "[%d] new tunnel state 'established'",
|
||||
ts->stream_id);
|
||||
infof(data, "CONNECT phase completed");
|
||||
infof(data, "CONNECT%s phase completed for HTTP/2 proxy",
|
||||
udp_tunnel ? "-UDP" : "");
|
||||
data->state.authproxy.done = TRUE;
|
||||
data->state.authproxy.multipass = FALSE;
|
||||
FALLTHROUGH();
|
||||
@@ -158,7 +171,7 @@ static void h2_tunnel_go_state(struct Curl_cfilter *cf,
|
||||
/* If a proxy-authorization header was used for the proxy, then we should
|
||||
make sure that it is not accidentally used for the document request
|
||||
after we have connected. Let's thus free and clear it here. */
|
||||
curlx_safefree(data->req.proxyuserpwd);
|
||||
curlx_safefree(data->req.hd_proxy_auth);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -171,6 +184,7 @@ struct cf_h2_proxy_ctx {
|
||||
struct bufq inbufq; /* network receive buffer */
|
||||
struct bufq outbufq; /* network send buffer */
|
||||
|
||||
struct Curl_peer *dest; /* where to tunnel to */
|
||||
struct tunnel_stream tunnel; /* our tunnel CONNECT stream */
|
||||
int32_t goaway_error;
|
||||
int32_t last_stream_id;
|
||||
@@ -178,6 +192,7 @@ struct cf_h2_proxy_ctx {
|
||||
BIT(rcvd_goaway);
|
||||
BIT(sent_goaway);
|
||||
BIT(nw_out_blocked);
|
||||
BIT(udp_tunnel);
|
||||
};
|
||||
|
||||
/* How to access `call_data` from a cf_h2 filter */
|
||||
@@ -193,6 +208,7 @@ static void cf_h2_proxy_ctx_clear(struct cf_h2_proxy_ctx *ctx)
|
||||
}
|
||||
Curl_bufq_free(&ctx->inbufq);
|
||||
Curl_bufq_free(&ctx->outbufq);
|
||||
Curl_peer_unlink(&ctx->dest);
|
||||
tunnel_stream_clear(&ctx->tunnel);
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->call_data = save;
|
||||
@@ -213,7 +229,8 @@ static void drain_tunnel(struct Curl_cfilter *cf,
|
||||
struct cf_h2_proxy_ctx *ctx = cf->ctx;
|
||||
(void)cf;
|
||||
if(!tunnel->closed && !tunnel->reset &&
|
||||
!Curl_bufq_is_empty(&ctx->tunnel.sendbuf))
|
||||
(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf) ||
|
||||
!Curl_bufq_is_empty(&ctx->tunnel.recvbuf)))
|
||||
Curl_multi_mark_dirty(data);
|
||||
}
|
||||
|
||||
@@ -228,7 +245,7 @@ static CURLcode proxy_h2_nw_out_writer(void *writer_ctx,
|
||||
CURLcode result;
|
||||
result = Curl_conn_cf_send(cf->next, data, buf, buflen, FALSE, pnwritten);
|
||||
CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %d, %zu",
|
||||
buflen, result, *pnwritten);
|
||||
buflen, (int)result, *pnwritten);
|
||||
return result;
|
||||
}
|
||||
return CURLE_FAILED_INIT;
|
||||
@@ -247,8 +264,7 @@ static int proxy_h2_client_new(struct Curl_cfilter *cf,
|
||||
return rc;
|
||||
/* We handle window updates ourself to enforce buffer limits */
|
||||
nghttp2_option_set_no_auto_window_update(o, 1);
|
||||
#if NGHTTP2_VERSION_NUM >= 0x013200
|
||||
/* with 1.50.0 */
|
||||
#if NGHTTP2_VERSION_NUM >= 0x013200 /* with 1.50.0 */
|
||||
/* turn off RFC 9113 leading and trailing white spaces validation against
|
||||
HTTP field value. */
|
||||
nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1);
|
||||
@@ -354,7 +370,7 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf,
|
||||
|
||||
result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread);
|
||||
CURL_TRC_CF(data, cf, "[0] read %zu bytes nw data -> %d, %zu",
|
||||
Curl_bufq_len(&ctx->inbufq), result, nread);
|
||||
Curl_bufq_len(&ctx->inbufq), (int)result, nread);
|
||||
if(result) {
|
||||
if(result != CURLE_AGAIN) {
|
||||
failf(data, "Failed receiving HTTP2 proxy data");
|
||||
@@ -363,6 +379,7 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf,
|
||||
break;
|
||||
}
|
||||
else if(nread == 0) {
|
||||
CURL_TRC_CF(data, cf, "server closed connection");
|
||||
ctx->conn_closed = TRUE;
|
||||
break;
|
||||
}
|
||||
@@ -558,12 +575,12 @@ static int proxy_h2_on_header(nghttp2_session *session,
|
||||
}
|
||||
|
||||
if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 &&
|
||||
memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) {
|
||||
!memcmp(HTTP_PSEUDO_STATUS, name, namelen)) {
|
||||
int http_status;
|
||||
struct http_resp *resp;
|
||||
|
||||
/* status: always comes first, we might get more than one response,
|
||||
* link the previous ones for keepers */
|
||||
* discard previous, interim responses */
|
||||
result = Curl_http_decode_status(&http_status,
|
||||
(const char *)value, valuelen);
|
||||
if(result)
|
||||
@@ -571,7 +588,8 @@ static int proxy_h2_on_header(nghttp2_session *session,
|
||||
result = Curl_http_resp_make(&resp, http_status, NULL);
|
||||
if(result)
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
resp->prev = ctx->tunnel.resp;
|
||||
if(ctx->tunnel.resp)
|
||||
Curl_http_resp_free(ctx->tunnel.resp);
|
||||
ctx->tunnel.resp = resp;
|
||||
CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d",
|
||||
stream_id, ctx->tunnel.resp->status);
|
||||
@@ -750,15 +768,15 @@ static CURLcode submit_CONNECT(struct Curl_cfilter *cf,
|
||||
CURLcode result;
|
||||
struct httpreq *req = NULL;
|
||||
|
||||
result = Curl_http_proxy_create_CONNECT(&req, cf, data, 2);
|
||||
result = Curl_http_proxy_create_tunnel_request(&req, cf, data, ctx->dest,
|
||||
PROXY_HTTP_V2,
|
||||
(bool)ctx->udp_tunnel);
|
||||
if(result)
|
||||
goto out;
|
||||
result = Curl_creader_set_null(data);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
infof(data, "Establish HTTP/2 proxy tunnel to %s", req->authority);
|
||||
|
||||
result = proxy_h2_submit(&ts->stream_id, cf, data, ctx->h2, req,
|
||||
NULL, ts, tunnel_send_callback, cf);
|
||||
if(result) {
|
||||
@@ -778,41 +796,30 @@ static CURLcode inspect_response(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct tunnel_stream *ts)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct dynhds_entry *auth_reply = NULL;
|
||||
(void)cf;
|
||||
struct cf_h2_proxy_ctx *ctx = cf->ctx;
|
||||
proxy_inspect_result res;
|
||||
CURLcode result;
|
||||
|
||||
DEBUGASSERT(ts->resp);
|
||||
if(ts->resp->status / 100 == 2) {
|
||||
infof(data, "CONNECT tunnel established, response %d", ts->resp->status);
|
||||
h2_tunnel_go_state(cf, ts, H2_TUNNEL_ESTABLISHED, data);
|
||||
return CURLE_OK;
|
||||
result = Curl_http_proxy_inspect_tunnel_response(
|
||||
cf, data, ts->resp, (bool)ctx->udp_tunnel, &res);
|
||||
if(result)
|
||||
return result;
|
||||
switch(res) {
|
||||
case PROXY_INSPECT_OK:
|
||||
h2_tunnel_go_state(cf, ts, H2_TUNNEL_ESTABLISHED, data,
|
||||
(bool)ctx->udp_tunnel);
|
||||
break;
|
||||
case PROXY_INSPECT_FAILED:
|
||||
h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data,
|
||||
(bool)ctx->udp_tunnel);
|
||||
result = CURLE_COULDNT_CONNECT;
|
||||
break;
|
||||
case PROXY_INSPECT_AUTH_RETRY:
|
||||
h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data,
|
||||
(bool)ctx->udp_tunnel);
|
||||
break;
|
||||
}
|
||||
|
||||
if(ts->resp->status == 401) {
|
||||
auth_reply = Curl_dynhds_cget(&ts->resp->headers, "WWW-Authenticate");
|
||||
}
|
||||
else if(ts->resp->status == 407) {
|
||||
auth_reply = Curl_dynhds_cget(&ts->resp->headers, "Proxy-Authenticate");
|
||||
}
|
||||
|
||||
if(auth_reply) {
|
||||
CURL_TRC_CF(data, cf, "[0] CONNECT: fwd auth header '%s'",
|
||||
auth_reply->value);
|
||||
result = Curl_http_input_auth(data, ts->resp->status == 407,
|
||||
auth_reply->value);
|
||||
if(result)
|
||||
return result;
|
||||
if(data->req.newurl) {
|
||||
/* Indicator that we should try again */
|
||||
curlx_safefree(data->req.newurl);
|
||||
h2_tunnel_go_state(cf, ts, H2_TUNNEL_INIT, data);
|
||||
return CURLE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* Seems to have failed */
|
||||
return CURLE_COULDNT_CONNECT;
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode H2_CONNECT(struct Curl_cfilter *cf,
|
||||
@@ -824,6 +831,11 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf,
|
||||
|
||||
DEBUGASSERT(ts);
|
||||
DEBUGASSERT(ts->authority);
|
||||
if(ctx->conn_closed) {
|
||||
failf(data, "proxy closed connection");
|
||||
return CURLE_COULDNT_CONNECT;
|
||||
}
|
||||
|
||||
do {
|
||||
switch(ts->state) {
|
||||
case H2_TUNNEL_INIT:
|
||||
@@ -832,7 +844,8 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf,
|
||||
result = submit_CONNECT(cf, data, ts);
|
||||
if(result)
|
||||
goto out;
|
||||
h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data);
|
||||
h2_tunnel_go_state(cf, ts, H2_TUNNEL_CONNECT, data,
|
||||
(bool)ctx->udp_tunnel);
|
||||
FALLTHROUGH();
|
||||
|
||||
case H2_TUNNEL_CONNECT:
|
||||
@@ -841,12 +854,14 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf,
|
||||
if(!result)
|
||||
result = proxy_h2_progress_egress(cf, data);
|
||||
if(result && result != CURLE_AGAIN) {
|
||||
h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data);
|
||||
h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data,
|
||||
(bool)ctx->udp_tunnel);
|
||||
break;
|
||||
}
|
||||
|
||||
if(ts->has_final_response) {
|
||||
h2_tunnel_go_state(cf, ts, H2_TUNNEL_RESPONSE, data);
|
||||
h2_tunnel_go_state(cf, ts, H2_TUNNEL_RESPONSE, data,
|
||||
(bool)ctx->udp_tunnel);
|
||||
}
|
||||
else {
|
||||
result = CURLE_OK;
|
||||
@@ -875,7 +890,8 @@ static CURLcode H2_CONNECT(struct Curl_cfilter *cf,
|
||||
|
||||
out:
|
||||
if((result && (result != CURLE_AGAIN)) || ctx->tunnel.closed)
|
||||
h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data);
|
||||
h2_tunnel_go_state(cf, ts, H2_TUNNEL_FAILED, data,
|
||||
(bool)ctx->udp_tunnel);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -896,7 +912,7 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf,
|
||||
Curl_bufq_init(&ctx->inbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_RECV_CHUNKS);
|
||||
Curl_bufq_init(&ctx->outbufq, PROXY_H2_CHUNK_SIZE, PROXY_H2_NW_SEND_CHUNKS);
|
||||
|
||||
if(tunnel_stream_init(cf, &ctx->tunnel))
|
||||
if(tunnel_stream_init(&ctx->tunnel, ctx->dest))
|
||||
goto out;
|
||||
|
||||
rc = nghttp2_session_callbacks_new(&cbs);
|
||||
@@ -958,7 +974,7 @@ static CURLcode cf_h2_proxy_ctx_init(struct Curl_cfilter *cf,
|
||||
out:
|
||||
if(cbs)
|
||||
nghttp2_session_callbacks_del(cbs);
|
||||
CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", result);
|
||||
CURL_TRC_CF(data, cf, "[0] init proxy ctx -> %d", (int)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1016,21 +1032,6 @@ out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cf_h2_proxy_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
struct cf_h2_proxy_ctx *ctx = cf->ctx;
|
||||
|
||||
if(ctx) {
|
||||
struct cf_call_data save;
|
||||
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
cf_h2_proxy_ctx_clear(ctx);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
}
|
||||
if(cf->next)
|
||||
cf->next->cft->do_close(cf->next, data);
|
||||
}
|
||||
|
||||
static void cf_h2_proxy_destroy(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
@@ -1133,7 +1134,7 @@ static CURLcode cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf,
|
||||
|
||||
result = Curl_pollset_set(data, ps, sock, want_recv, want_send);
|
||||
CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d -> %d",
|
||||
want_recv, want_send, result);
|
||||
want_recv, want_send, (int)result);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
}
|
||||
else if(ctx->sent_goaway && !cf->shutdown) {
|
||||
@@ -1145,7 +1146,7 @@ static CURLcode cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf,
|
||||
want_recv = nghttp2_session_want_read(ctx->h2);
|
||||
result = Curl_pollset_set(data, ps, sock, want_recv, want_send);
|
||||
CURL_TRC_CF(data, cf, "adjust_pollset, want_recv=%d want_send=%d -> %d",
|
||||
want_recv, want_send, result);
|
||||
want_recv, want_send, (int)result);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
}
|
||||
return result;
|
||||
@@ -1194,7 +1195,7 @@ static CURLcode tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
}
|
||||
|
||||
CURL_TRC_CF(data, cf, "[%d] tunnel_recv(len=%zu) -> %d, %zu",
|
||||
ctx->tunnel.stream_id, len, result, *pnread);
|
||||
ctx->tunnel.stream_id, len, (int)result, *pnread);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1232,14 +1233,15 @@ static CURLcode cf_h2_proxy_recv(struct Curl_cfilter *cf,
|
||||
result = Curl_1st_fatal(result, proxy_h2_progress_egress(cf, data));
|
||||
|
||||
out:
|
||||
if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) &&
|
||||
if((!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) ||
|
||||
!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) &&
|
||||
(!result || (result == CURLE_AGAIN))) {
|
||||
/* data pending and no fatal error to report. Need to trigger
|
||||
* draining to avoid stalling when no socket events happen. */
|
||||
drain_tunnel(cf, data, &ctx->tunnel);
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %d, %zu",
|
||||
ctx->tunnel.stream_id, len, result, *pnread);
|
||||
ctx->tunnel.stream_id, len, (int)result, *pnread);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
return result;
|
||||
}
|
||||
@@ -1269,7 +1271,8 @@ static CURLcode cf_h2_proxy_send(struct Curl_cfilter *cf,
|
||||
}
|
||||
|
||||
result = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, pnwritten);
|
||||
CURL_TRC_CF(data, cf, "cf_send(), bufq_write %d, %zu", result, *pnwritten);
|
||||
CURL_TRC_CF(data, cf, "cf_send(), bufq_write %d, %zu", (int)result,
|
||||
*pnwritten);
|
||||
if(result && (result != CURLE_AGAIN))
|
||||
goto out;
|
||||
|
||||
@@ -1298,7 +1301,8 @@ static CURLcode cf_h2_proxy_send(struct Curl_cfilter *cf,
|
||||
}
|
||||
|
||||
out:
|
||||
if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) &&
|
||||
if((!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) ||
|
||||
!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) &&
|
||||
(!result || (result == CURLE_AGAIN))) {
|
||||
/* data pending and no fatal error to report. Need to trigger
|
||||
* draining to avoid stalling when no socket events happen. */
|
||||
@@ -1306,7 +1310,7 @@ out:
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %d, %zu, "
|
||||
"h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)",
|
||||
ctx->tunnel.stream_id, len, result, *pnwritten,
|
||||
ctx->tunnel.stream_id, len, (int)result, *pnwritten,
|
||||
nghttp2_session_get_stream_remote_window_size(
|
||||
ctx->h2, ctx->tunnel.stream_id),
|
||||
nghttp2_session_get_remote_window_size(ctx->h2),
|
||||
@@ -1338,7 +1342,7 @@ static CURLcode cf_h2_proxy_flush(struct Curl_cfilter *cf,
|
||||
out:
|
||||
CURL_TRC_CF(data, cf, "[%d] flush -> %d, "
|
||||
"h2 windows %d-%d (stream-conn), buffers %zu-%zu (stream-conn)",
|
||||
ctx->tunnel.stream_id, result,
|
||||
ctx->tunnel.stream_id, (int)result,
|
||||
nghttp2_session_get_stream_remote_window_size(
|
||||
ctx->h2, ctx->tunnel.stream_id),
|
||||
nghttp2_session_get_remote_window_size(ctx->h2),
|
||||
@@ -1410,8 +1414,8 @@ static CURLcode cf_h2_proxy_query(struct Curl_cfilter *cf,
|
||||
|
||||
switch(query) {
|
||||
case CF_QUERY_HOST_PORT:
|
||||
*pres1 = (int)cf->conn->http_proxy.port;
|
||||
*((const char **)pres2) = cf->conn->http_proxy.host.name;
|
||||
*pres1 = (int)ctx->dest->port;
|
||||
*((const char **)pres2) = ctx->dest->hostname;
|
||||
return CURLE_OK;
|
||||
case CF_QUERY_NEED_FLUSH: {
|
||||
if(!Curl_bufq_is_empty(&ctx->outbufq) ||
|
||||
@@ -1464,7 +1468,6 @@ struct Curl_cftype Curl_cft_h2_proxy = {
|
||||
CURL_LOG_LVL_NONE,
|
||||
cf_h2_proxy_destroy,
|
||||
cf_h2_proxy_connect,
|
||||
cf_h2_proxy_close,
|
||||
cf_h2_proxy_shutdown,
|
||||
cf_h2_proxy_adjust_pollset,
|
||||
cf_h2_proxy_data_pending,
|
||||
@@ -1477,7 +1480,9 @@ struct Curl_cftype Curl_cft_h2_proxy = {
|
||||
};
|
||||
|
||||
CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
struct Curl_easy *data,
|
||||
struct Curl_peer *dest,
|
||||
bool udp_tunnel)
|
||||
{
|
||||
struct Curl_cfilter *cf_h2_proxy = NULL;
|
||||
struct cf_h2_proxy_ctx *ctx;
|
||||
@@ -1487,17 +1492,17 @@ CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf,
|
||||
ctx = curlx_calloc(1, sizeof(*ctx));
|
||||
if(!ctx)
|
||||
goto out;
|
||||
Curl_peer_link(&ctx->dest, dest);
|
||||
ctx->udp_tunnel = udp_tunnel;
|
||||
|
||||
result = Curl_cf_create(&cf_h2_proxy, &Curl_cft_h2_proxy, ctx);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
ctx = NULL;
|
||||
Curl_conn_cf_insert_after(cf, cf_h2_proxy);
|
||||
result = CURLE_OK;
|
||||
|
||||
out:
|
||||
if(result)
|
||||
cf_h2_proxy_ctx_free(ctx);
|
||||
cf_h2_proxy_ctx_free(ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
#if defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY)
|
||||
|
||||
CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data);
|
||||
struct Curl_easy *data,
|
||||
struct Curl_peer *dest,
|
||||
bool udp_tunnel);
|
||||
|
||||
extern struct Curl_cftype Curl_cft_h2_proxy;
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "urldata.h"
|
||||
#include "cfilters.h"
|
||||
#include "cf-haproxy.h"
|
||||
#include "connect.h"
|
||||
#include "curl_addrinfo.h"
|
||||
#include "curl_trc.h"
|
||||
#include "select.h"
|
||||
@@ -44,13 +45,6 @@ struct cf_haproxy_ctx {
|
||||
struct dynbuf data_out;
|
||||
};
|
||||
|
||||
static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx)
|
||||
{
|
||||
DEBUGASSERT(ctx);
|
||||
ctx->state = HAPROXY_INIT;
|
||||
curlx_dyn_reset(&ctx->data_out);
|
||||
}
|
||||
|
||||
static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx)
|
||||
{
|
||||
if(ctx) {
|
||||
@@ -78,7 +72,7 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter *cf,
|
||||
DEBUGASSERT(ctx);
|
||||
DEBUGASSERT(ctx->state == HAPROXY_INIT);
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
if(cf->conn->unix_domain_socket)
|
||||
if(Curl_conn_get_first_peer(cf->conn, cf->sockindex)->unix_socket)
|
||||
/* the buffer is large enough to hold this! */
|
||||
result = curlx_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n"));
|
||||
else {
|
||||
@@ -97,7 +91,7 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter *cf,
|
||||
client_dest_ip = ipquad.remote_ip;
|
||||
}
|
||||
|
||||
result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
|
||||
result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %d %d\r\n",
|
||||
is_ipv6 ? "TCP6" : "TCP4",
|
||||
client_source_ip, client_dest_ip,
|
||||
ipquad.local_port, ipquad.remote_port);
|
||||
@@ -172,16 +166,6 @@ static void cf_haproxy_destroy(struct Curl_cfilter *cf,
|
||||
cf_haproxy_ctx_free(cf->ctx);
|
||||
}
|
||||
|
||||
static void cf_haproxy_close(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
CURL_TRC_CF(data, cf, "close");
|
||||
cf->connected = FALSE;
|
||||
cf_haproxy_ctx_reset(cf->ctx);
|
||||
if(cf->next)
|
||||
cf->next->cft->do_close(cf->next, data);
|
||||
}
|
||||
|
||||
static CURLcode cf_haproxy_adjust_pollset(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct easy_pollset *ps)
|
||||
@@ -201,7 +185,6 @@ struct Curl_cftype Curl_cft_haproxy = {
|
||||
0,
|
||||
cf_haproxy_destroy,
|
||||
cf_haproxy_connect,
|
||||
cf_haproxy_close,
|
||||
Curl_cf_def_shutdown,
|
||||
cf_haproxy_adjust_pollset,
|
||||
Curl_cf_def_data_pending,
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "curl_trc.h"
|
||||
#include "cfilters.h"
|
||||
#include "cf-dns.h"
|
||||
#include "cf-setup.h"
|
||||
#include "connect.h"
|
||||
#include "hostip.h"
|
||||
#include "httpsrr.h"
|
||||
@@ -62,7 +63,6 @@ static void cf_hc_baller_discard(struct cf_hc_baller *b,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
if(b->cf) {
|
||||
Curl_conn_cf_close(b->cf, data);
|
||||
Curl_conn_cf_discard_chain(&b->cf, data);
|
||||
b->cf = NULL;
|
||||
}
|
||||
@@ -304,13 +304,14 @@ static enum alpnid cf_hc_get_httpsrr_alpn(struct Curl_cfilter *cf,
|
||||
size_t i;
|
||||
|
||||
/* Do we have HTTPS-RR information? */
|
||||
rr = Curl_conn_dns_get_https(data, cf->sockindex);
|
||||
rr = Curl_conn_dns_get_https(
|
||||
data, cf->sockindex, Curl_conn_get_destination(cf->conn, cf->sockindex));
|
||||
|
||||
/* We do not support `rr->no_def_alpn`. */
|
||||
if(Curl_httpsrr_applicable(data, rr) && !rr->no_def_alpn) {
|
||||
for(i = 0; i < CURL_ARRAYSIZE(rr->alpns); ++i) {
|
||||
enum alpnid alpn_rr = (enum alpnid)rr->alpns[i];
|
||||
if(alpn_rr == not_this_one) /* don't want this one */
|
||||
if(alpn_rr == not_this_one) /* do not want this one */
|
||||
continue;
|
||||
switch(alpn_rr) {
|
||||
case ALPN_h3:
|
||||
@@ -493,7 +494,8 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
|
||||
*done = FALSE;
|
||||
|
||||
if(!ctx->httpsrr_resolved) {
|
||||
ctx->httpsrr_resolved = Curl_conn_dns_resolved_https(data, cf->sockindex);
|
||||
ctx->httpsrr_resolved = Curl_conn_dns_resolved_https(
|
||||
data, cf->sockindex, Curl_conn_get_destination(cf->conn, cf->sockindex));
|
||||
#ifdef DEBUGBUILD
|
||||
if(!ctx->httpsrr_resolved && getenv("CURL_DBG_AWAIT_HTTPSRR")) {
|
||||
CURL_TRC_CF(data, cf, "awaiting HTTPS-RR");
|
||||
@@ -576,7 +578,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
|
||||
}
|
||||
|
||||
out:
|
||||
CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done);
|
||||
CURL_TRC_CF(data, cf, "connect -> %d, done=%d", (int)result, *done);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -616,7 +618,7 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf,
|
||||
result = ctx->ballers[i].result;
|
||||
}
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done);
|
||||
CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", (int)result, *done);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -635,7 +637,8 @@ static CURLcode cf_hc_adjust_pollset(struct Curl_cfilter *cf,
|
||||
continue;
|
||||
result = Curl_conn_cf_adjust_pollset(b->cf, data, ps);
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", result, ps->n);
|
||||
CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", (int)result,
|
||||
ps->n);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -731,18 +734,6 @@ out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
CURL_TRC_CF(data, cf, "close");
|
||||
cf_hc_ctx_close(data, cf->ctx);
|
||||
cf->connected = FALSE;
|
||||
|
||||
if(cf->next) {
|
||||
cf->next->cft->do_close(cf->next, data);
|
||||
Curl_conn_cf_discard_chain(&cf->next, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
struct cf_hc_ctx *ctx = cf->ctx;
|
||||
@@ -757,7 +748,6 @@ struct Curl_cftype Curl_cft_http_connect = {
|
||||
CURL_LOG_LVL_NONE,
|
||||
cf_hc_destroy,
|
||||
cf_hc_connect,
|
||||
cf_hc_close,
|
||||
cf_hc_shutdown,
|
||||
cf_hc_adjust_pollset,
|
||||
cf_hc_data_pending,
|
||||
@@ -822,9 +812,14 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data,
|
||||
|
||||
DEBUGASSERT(conn->scheme->protocol == CURLPROTO_HTTPS);
|
||||
|
||||
/* This filter is intended for HTTPS using ALPN and does
|
||||
* not support HTTPS Eyeballing to a proxy. */
|
||||
if((conn->scheme->protocol != CURLPROTO_HTTPS) ||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
conn->bits.origin_is_proxy ||
|
||||
#endif
|
||||
!conn->bits.tls_enable_alpn)
|
||||
goto out;
|
||||
goto out;
|
||||
|
||||
result = cf_hc_add(data, conn, sockindex, conn->transport_wanted);
|
||||
|
||||
|
||||
+159
-132
@@ -56,12 +56,14 @@
|
||||
#include "multiif.h"
|
||||
#include "progress.h"
|
||||
#include "select.h"
|
||||
#include "sockaddr.h"
|
||||
#include "vquic/vquic.h" /* for quic cfilters */
|
||||
|
||||
|
||||
struct transport_provider {
|
||||
uint8_t transport;
|
||||
cf_ip_connect_create *cf_create;
|
||||
uint8_t transport;
|
||||
bool tunnel;
|
||||
};
|
||||
|
||||
static
|
||||
@@ -69,23 +71,30 @@ static
|
||||
const
|
||||
#endif
|
||||
struct transport_provider transport_providers[] = {
|
||||
{ TRNSPRT_TCP, Curl_cf_tcp_create },
|
||||
{ Curl_cf_tcp_create, TRNSPRT_TCP, FALSE },
|
||||
{ Curl_cf_tcp_create, TRNSPRT_TCP, TRUE },
|
||||
#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3)
|
||||
{ TRNSPRT_QUIC, Curl_cf_quic_create },
|
||||
{ Curl_cf_quic_create, TRNSPRT_QUIC, FALSE },
|
||||
#endif
|
||||
#if !defined(CURL_DISABLE_HTTP) && defined(USE_PROXY_HTTP3)
|
||||
{ Curl_cf_h3_proxy_create, TRNSPRT_QUIC, TRUE },
|
||||
#endif
|
||||
#ifndef CURL_DISABLE_TFTP
|
||||
{ TRNSPRT_UDP, Curl_cf_udp_create },
|
||||
{ Curl_cf_udp_create, TRNSPRT_UDP, FALSE },
|
||||
#endif
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
{ TRNSPRT_UNIX, Curl_cf_unix_create },
|
||||
{ Curl_cf_unix_create, TRNSPRT_UNIX, FALSE },
|
||||
{ Curl_cf_unix_create, TRNSPRT_UNIX, TRUE },
|
||||
#endif
|
||||
};
|
||||
|
||||
static cf_ip_connect_create *get_cf_create(uint8_t transport)
|
||||
static cf_ip_connect_create *get_cf_create(uint8_t transport,
|
||||
bool tunnel)
|
||||
{
|
||||
size_t i;
|
||||
for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
|
||||
if(transport == transport_providers[i].transport)
|
||||
if((transport == transport_providers[i].transport) &&
|
||||
(tunnel == transport_providers[i].tunnel))
|
||||
return transport_providers[i].cf_create;
|
||||
}
|
||||
return NULL;
|
||||
@@ -102,7 +111,6 @@ UNITTEST void debug_set_transport_provider(
|
||||
for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
|
||||
if(transport == transport_providers[i].transport) {
|
||||
transport_providers[i].cf_create = cf_create;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,15 +118,18 @@ UNITTEST void debug_set_transport_provider(
|
||||
|
||||
struct cf_ai_iter {
|
||||
struct Curl_cfilter *cf;
|
||||
struct Curl_peer *peer;
|
||||
int ai_family;
|
||||
unsigned int n;
|
||||
};
|
||||
|
||||
static void cf_ai_iter_init(struct cf_ai_iter *iter,
|
||||
struct Curl_cfilter *cf,
|
||||
struct Curl_peer *peer,
|
||||
int ai_family)
|
||||
{
|
||||
iter->cf = cf;
|
||||
iter->peer = peer; /* not linked, ctx->ballers owns and has same lifetime */
|
||||
iter->ai_family = ai_family;
|
||||
iter->n = 0;
|
||||
}
|
||||
@@ -131,7 +142,7 @@ static const struct Curl_addrinfo *cf_ai_iter_next(struct cf_ai_iter *iter,
|
||||
if(!iter->cf)
|
||||
return NULL;
|
||||
|
||||
addr = Curl_conn_dns_get_ai(data, iter->cf->sockindex,
|
||||
addr = Curl_conn_dns_get_ai(data, iter->peer, iter->cf->sockindex,
|
||||
iter->ai_family, iter->n);
|
||||
if(addr)
|
||||
iter->n++;
|
||||
@@ -142,19 +153,23 @@ static bool cf_ai_iter_has_more(struct cf_ai_iter *iter,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
return (iter->cf &&
|
||||
!!Curl_conn_dns_get_ai(data, iter->cf->sockindex,
|
||||
!!Curl_conn_dns_get_ai(data, iter->peer, iter->cf->sockindex,
|
||||
iter->ai_family, iter->n));
|
||||
}
|
||||
|
||||
struct cf_ip_attempt {
|
||||
struct cf_ip_attempt *next;
|
||||
struct Curl_peer *origin;
|
||||
struct Curl_peer *peer;
|
||||
struct Curl_peer *tunnel_peer;
|
||||
struct Curl_sockaddr_ex addr;
|
||||
struct Curl_cfilter *cf; /* current sub-cfilter connecting */
|
||||
cf_ip_connect_create *cf_create;
|
||||
struct curltime started; /* start of current attempt */
|
||||
CURLcode result;
|
||||
int ai_family;
|
||||
uint8_t transport;
|
||||
uint8_t transport_peer;
|
||||
uint8_t tunnel_transport;
|
||||
int error;
|
||||
BIT(connected); /* cf has connected */
|
||||
BIT(shutdown); /* cf has shutdown */
|
||||
@@ -168,16 +183,23 @@ static void cf_ip_attempt_free(struct cf_ip_attempt *a,
|
||||
if(a) {
|
||||
if(a->cf)
|
||||
Curl_conn_cf_discard_chain(&a->cf, data);
|
||||
Curl_peer_unlink(&a->origin);
|
||||
Curl_peer_unlink(&a->peer);
|
||||
Curl_peer_unlink(&a->tunnel_peer);
|
||||
curlx_free(a);
|
||||
}
|
||||
}
|
||||
|
||||
static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa,
|
||||
struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
struct Curl_cfilter *cf,
|
||||
struct Curl_peer *origin,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t transport_peer,
|
||||
struct Curl_sockaddr_ex *addr,
|
||||
int ai_family,
|
||||
uint8_t transport,
|
||||
struct Curl_peer *tunnel_peer,
|
||||
uint8_t tunnel_transport,
|
||||
cf_ip_connect_create *cf_create)
|
||||
{
|
||||
struct Curl_cfilter *wcf;
|
||||
@@ -189,14 +211,20 @@ static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa,
|
||||
if(!a)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
Curl_peer_link(&a->origin, origin);
|
||||
Curl_peer_link(&a->peer, peer);
|
||||
a->transport_peer = transport_peer;
|
||||
Curl_peer_link(&a->tunnel_peer, tunnel_peer);
|
||||
a->tunnel_transport = tunnel_transport;
|
||||
a->addr = *addr;
|
||||
a->ai_family = ai_family;
|
||||
a->transport = transport;
|
||||
a->result = CURLE_OK;
|
||||
a->cf_create = cf_create;
|
||||
*pa = a;
|
||||
|
||||
result = a->cf_create(&a->cf, data, cf->conn, &a->addr, a->transport);
|
||||
result = a->cf_create(&a->cf, data, a->origin, a->peer, a->transport_peer,
|
||||
cf->conn, &a->addr, a->tunnel_peer,
|
||||
a->tunnel_transport);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
@@ -245,13 +273,17 @@ struct cf_ip_ballers {
|
||||
#ifdef USE_IPV6
|
||||
struct cf_ai_iter ipv6_iter;
|
||||
#endif
|
||||
struct Curl_peer *origin;
|
||||
struct Curl_peer *peer;
|
||||
struct Curl_peer *tunnel_peer;
|
||||
cf_ip_connect_create *cf_create; /* for creating cf */
|
||||
struct curltime started;
|
||||
struct curltime last_attempt_started;
|
||||
timediff_t attempt_delay_ms;
|
||||
int last_attempt_ai_family;
|
||||
uint32_t max_concurrent;
|
||||
uint8_t transport;
|
||||
uint8_t transport_peer;
|
||||
uint8_t tunnel_transport;
|
||||
};
|
||||
|
||||
static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a,
|
||||
@@ -269,7 +301,9 @@ static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a,
|
||||
a->inconclusive = FALSE;
|
||||
a->cf = NULL;
|
||||
|
||||
result = a->cf_create(&a->cf, data, cf->conn, &a->addr, a->transport);
|
||||
result = a->cf_create(&a->cf, data, a->origin, a->peer, a->transport_peer,
|
||||
cf->conn, &a->addr,
|
||||
a->tunnel_peer, a->tunnel_transport);
|
||||
if(!result) {
|
||||
bool dummy;
|
||||
/* the new filter might have sub-filters */
|
||||
@@ -282,11 +316,9 @@ static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a,
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cf_ip_ballers_clear(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
static void cf_ip_ballers_clear(struct Curl_easy *data,
|
||||
struct cf_ip_ballers *bs)
|
||||
{
|
||||
(void)cf;
|
||||
while(bs->running) {
|
||||
struct cf_ip_attempt *a = bs->running;
|
||||
bs->running = a->next;
|
||||
@@ -294,35 +326,36 @@ static void cf_ip_ballers_clear(struct Curl_cfilter *cf,
|
||||
}
|
||||
cf_ip_attempt_free(bs->winner, data);
|
||||
bs->winner = NULL;
|
||||
Curl_peer_unlink(&bs->origin);
|
||||
Curl_peer_unlink(&bs->peer);
|
||||
Curl_peer_unlink(&bs->tunnel_peer);
|
||||
}
|
||||
|
||||
static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs,
|
||||
struct Curl_cfilter *cf,
|
||||
cf_ip_connect_create *cf_create,
|
||||
uint8_t transport,
|
||||
struct Curl_easy *data,
|
||||
struct Curl_peer *origin,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t transport_peer,
|
||||
struct Curl_peer *tunnel_peer,
|
||||
uint8_t tunnel_transport,
|
||||
timediff_t attempt_delay_ms,
|
||||
uint32_t max_concurrent)
|
||||
{
|
||||
memset(bs, 0, sizeof(*bs));
|
||||
bs->cf_create = cf_create;
|
||||
bs->transport = transport;
|
||||
bs->cf_create = get_cf_create(transport_peer, !!tunnel_peer);
|
||||
if(!bs->cf_create) {
|
||||
failf(data, "unsupported transport type %u%s",
|
||||
transport_peer, tunnel_peer ? " to proxy" : "");
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
}
|
||||
Curl_peer_link(&bs->origin, origin);
|
||||
Curl_peer_link(&bs->peer, peer);
|
||||
bs->transport_peer = transport_peer;
|
||||
Curl_peer_link(&bs->tunnel_peer, tunnel_peer);
|
||||
bs->tunnel_transport = tunnel_transport;
|
||||
bs->attempt_delay_ms = attempt_delay_ms;
|
||||
bs->max_concurrent = max_concurrent;
|
||||
bs->last_attempt_ai_family = AF_INET; /* so AF_INET6 is next */
|
||||
|
||||
if(transport == TRNSPRT_UNIX) {
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
cf_ai_iter_init(&bs->addr_iter, cf, AF_UNIX);
|
||||
#else
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
#endif
|
||||
}
|
||||
else { /* TCP/UDP/QUIC */
|
||||
#ifdef USE_IPV6
|
||||
cf_ai_iter_init(&bs->ipv6_iter, cf, AF_INET6);
|
||||
#endif
|
||||
cf_ai_iter_init(&bs->addr_iter, cf, AF_INET);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
@@ -458,15 +491,17 @@ evaluate:
|
||||
if(bs->max_concurrent)
|
||||
cf_ip_ballers_prune(bs, cf, data, bs->max_concurrent - 1);
|
||||
|
||||
result = Curl_socket_addr_from_ai(&addr, ai, bs->transport);
|
||||
result = Curl_socket_addr_from_ai(&addr, ai, bs->transport_peer);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
result = cf_ip_attempt_new(&a, cf, data, &addr, ai_family,
|
||||
bs->transport, bs->cf_create);
|
||||
result = cf_ip_attempt_new(&a, data, cf, bs->origin, bs->peer,
|
||||
bs->transport_peer, &addr, ai_family,
|
||||
bs->tunnel_peer, bs->tunnel_transport,
|
||||
bs->cf_create);
|
||||
CURL_TRC_CF(data, cf, "starting %s attempt for ipv%s -> %d",
|
||||
bs->running ? "next" : "first",
|
||||
(ai_family == AF_INET) ? "4" : "6", result);
|
||||
(ai_family == AF_INET) ? "4" : "6", (int)result);
|
||||
if(result)
|
||||
goto out;
|
||||
DEBUGASSERT(a);
|
||||
@@ -495,7 +530,7 @@ evaluate:
|
||||
if(!a->inconclusive)
|
||||
continue;
|
||||
result = cf_ip_attempt_restart(a, cf, data);
|
||||
CURL_TRC_CF(data, cf, "restarted baller %d -> %d", i, result);
|
||||
CURL_TRC_CF(data, cf, "restarted baller %d -> %d", i, (int)result);
|
||||
if(result) /* serious failure */
|
||||
goto out;
|
||||
bs->last_attempt_started = *Curl_pgrs_now(data);
|
||||
@@ -518,7 +553,7 @@ evaluate:
|
||||
result = CURLE_COULDNT_CONNECT;
|
||||
VERBOSE(i = 0);
|
||||
for(a = bs->running; a; a = a->next) {
|
||||
CURL_TRC_CF(data, cf, "baller %d: result=%d", i, a->result);
|
||||
CURL_TRC_CF(data, cf, "baller %d: result=%d", i, (int)a->result);
|
||||
if(a->result)
|
||||
result = a->result;
|
||||
}
|
||||
@@ -652,7 +687,6 @@ typedef enum {
|
||||
} cf_connect_state;
|
||||
|
||||
struct cf_ip_happy_ctx {
|
||||
uint8_t transport;
|
||||
cf_ip_connect_create *cf_create;
|
||||
cf_connect_state state;
|
||||
struct cf_ip_ballers ballers;
|
||||
@@ -674,40 +708,39 @@ static CURLcode is_connected(struct Curl_cfilter *cf,
|
||||
if(!result)
|
||||
return CURLE_OK;
|
||||
else {
|
||||
const char *hostname, *proxy_name = NULL;
|
||||
struct Curl_peer *peer = NULL, *proxy_peer = NULL;
|
||||
char viamsg[160];
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(conn->bits.socksproxy)
|
||||
proxy_name = conn->socks_proxy.host.name;
|
||||
else if(conn->bits.httpproxy)
|
||||
proxy_name = conn->http_proxy.host.name;
|
||||
#endif
|
||||
hostname = conn->bits.conn_to_host ? conn->conn_to_host.name :
|
||||
conn->host.name;
|
||||
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
if(conn->unix_domain_socket)
|
||||
curl_msnprintf(viamsg, sizeof(viamsg), "over %s",
|
||||
conn->unix_domain_socket);
|
||||
else
|
||||
peer = Curl_conn_get_first_peer(conn, cf->sockindex);
|
||||
if(!conn->origin || !peer)
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(conn->socks_proxy.peer)
|
||||
proxy_peer = conn->socks_proxy.peer;
|
||||
else if(conn->http_proxy.peer)
|
||||
proxy_peer = conn->http_proxy.peer;
|
||||
#endif
|
||||
{
|
||||
uint16_t port;
|
||||
if(cf->sockindex == SECONDARYSOCKET)
|
||||
port = conn->secondary_port;
|
||||
else if(cf->conn->bits.conn_to_port)
|
||||
port = conn->conn_to_port;
|
||||
|
||||
viamsg[0] = 0;
|
||||
if(!Curl_peer_equal(peer, conn->origin) &&
|
||||
!Curl_peer_equal(peer, proxy_peer)) {
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
if(peer->unix_socket)
|
||||
curl_msnprintf(viamsg, sizeof(viamsg), " over unix://%s",
|
||||
peer->hostname);
|
||||
else
|
||||
port = conn->remote_port;
|
||||
curl_msnprintf(viamsg, sizeof(viamsg), "port %d", port);
|
||||
#endif
|
||||
curl_msnprintf(viamsg, sizeof(viamsg), " via %s:%u",
|
||||
peer->hostname, peer->port);
|
||||
}
|
||||
|
||||
failf(data, "Failed to connect to %s %s %s%s%safter "
|
||||
failf(data, "Failed to connect to %s:%u%s %s%s%safter "
|
||||
"%" FMT_TIMEDIFF_T " ms: %s",
|
||||
hostname, viamsg,
|
||||
proxy_name ? "via " : "",
|
||||
proxy_name ? proxy_name : "",
|
||||
proxy_name ? " " : "",
|
||||
conn->origin->hostname, conn->origin->port, viamsg,
|
||||
proxy_peer ? "over proxy " : "",
|
||||
proxy_peer ? proxy_peer->hostname : "",
|
||||
proxy_peer ? " " : "",
|
||||
curlx_ptimediff_ms(Curl_pgrs_now(data),
|
||||
&data->progress.t_startsingle),
|
||||
curl_easy_strerror(result));
|
||||
@@ -734,28 +767,41 @@ static CURLcode cf_ip_happy_init(struct Curl_cfilter *cf,
|
||||
return CURLE_OPERATION_TIMEDOUT;
|
||||
}
|
||||
|
||||
CURL_TRC_CF(data, cf, "init ip ballers for transport %u", ctx->transport);
|
||||
if(ctx->ballers.transport_peer == TRNSPRT_UNIX) {
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
cf_ai_iter_init(&ctx->ballers.addr_iter, cf, ctx->ballers.peer, AF_UNIX);
|
||||
#else
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
#endif
|
||||
}
|
||||
else { /* TCP/UDP/QUIC */
|
||||
#ifdef USE_IPV6
|
||||
cf_ai_iter_init(&ctx->ballers.ipv6_iter, cf, ctx->ballers.peer, AF_INET6);
|
||||
#endif
|
||||
cf_ai_iter_init(&ctx->ballers.addr_iter, cf, ctx->ballers.peer, AF_INET);
|
||||
}
|
||||
|
||||
CURL_TRC_CF(data, cf, "init ip ballers for transport %u",
|
||||
ctx->ballers.transport_peer);
|
||||
ctx->started = *Curl_pgrs_now(data);
|
||||
return cf_ip_ballers_init(&ctx->ballers, cf,
|
||||
ctx->cf_create, ctx->transport,
|
||||
data->set.happy_eyeballs_timeout,
|
||||
IP_HE_MAX_CONCURRENT_ATTEMPTS);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void cf_ip_happy_ctx_clear(struct Curl_cfilter *cf,
|
||||
static void cf_ip_happy_ctx_clear(struct cf_ip_happy_ctx *ctx,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_ip_happy_ctx *ctx = cf->ctx;
|
||||
|
||||
DEBUGASSERT(ctx);
|
||||
DEBUGASSERT(data);
|
||||
cf_ip_ballers_clear(cf, data, &ctx->ballers);
|
||||
if(ctx)
|
||||
cf_ip_ballers_clear(data, &ctx->ballers);
|
||||
}
|
||||
|
||||
static void cf_ip_happy_ctx_destroy(struct cf_ip_happy_ctx *ctx)
|
||||
static void cf_ip_happy_ctx_destroy(struct cf_ip_happy_ctx *ctx,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
if(ctx)
|
||||
if(ctx) {
|
||||
cf_ip_happy_ctx_clear(ctx, data);
|
||||
curlx_free(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static CURLcode cf_ip_happy_shutdown(struct Curl_cfilter *cf,
|
||||
@@ -772,7 +818,7 @@ static CURLcode cf_ip_happy_shutdown(struct Curl_cfilter *cf,
|
||||
}
|
||||
|
||||
result = cf_ip_ballers_shutdown(&ctx->ballers, data, done);
|
||||
CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done);
|
||||
CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", (int)result, *done);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -785,7 +831,8 @@ static CURLcode cf_ip_happy_adjust_pollset(struct Curl_cfilter *cf,
|
||||
|
||||
if(!cf->connected) {
|
||||
result = cf_ip_ballers_pollset(&ctx->ballers, data, ps);
|
||||
CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", result, ps->n);
|
||||
CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %u socks", (int)result,
|
||||
ps->n);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -810,7 +857,7 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf,
|
||||
*done = FALSE;
|
||||
|
||||
if(!ctx->dns_resolved) {
|
||||
result = Curl_conn_dns_result(cf->conn, cf->sockindex);
|
||||
result = Curl_conn_dns_result(cf->conn, cf->sockindex, ctx->ballers.peer);
|
||||
if(!result)
|
||||
ctx->dns_resolved = TRUE;
|
||||
else if(result == CURLE_AGAIN) {
|
||||
@@ -841,9 +888,9 @@ static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf,
|
||||
cf->connected = TRUE;
|
||||
cf->next = ctx->ballers.winner->cf;
|
||||
ctx->ballers.winner->cf = NULL;
|
||||
cf_ip_happy_ctx_clear(cf, data);
|
||||
cf_ip_happy_ctx_clear(ctx, data);
|
||||
Curl_expire_done(data, EXPIRE_HAPPY_EYEBALLS);
|
||||
/* whatever errors where reported by ballers, clear our errorbuf */
|
||||
/* whatever errors were reported by ballers, clear our errorbuf */
|
||||
Curl_reset_fail(data);
|
||||
|
||||
if(cf->conn->scheme->protocol & PROTO_FAMILY_SSH)
|
||||
@@ -871,22 +918,6 @@ out:
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cf_ip_happy_close(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_ip_happy_ctx *ctx = cf->ctx;
|
||||
|
||||
CURL_TRC_CF(data, cf, "close");
|
||||
cf_ip_happy_ctx_clear(cf, data);
|
||||
cf->connected = FALSE;
|
||||
ctx->state = SCFST_INIT;
|
||||
|
||||
if(cf->next) {
|
||||
cf->next->cft->do_close(cf->next, data);
|
||||
Curl_conn_cf_discard_chain(&cf->next, data);
|
||||
}
|
||||
}
|
||||
|
||||
static bool cf_ip_happy_data_pending(struct Curl_cfilter *cf,
|
||||
const struct Curl_easy *data)
|
||||
{
|
||||
@@ -940,8 +971,8 @@ static void cf_ip_happy_destroy(struct Curl_cfilter *cf,
|
||||
|
||||
CURL_TRC_CF(data, cf, "destroy");
|
||||
if(ctx) {
|
||||
cf_ip_happy_ctx_clear(cf, data);
|
||||
cf_ip_happy_ctx_destroy(ctx);
|
||||
cf_ip_happy_ctx_clear(ctx, data);
|
||||
cf_ip_happy_ctx_destroy(ctx, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -951,7 +982,6 @@ struct Curl_cftype Curl_cft_ip_happy = {
|
||||
CURL_LOG_LVL_NONE,
|
||||
cf_ip_happy_destroy,
|
||||
cf_ip_happy_connect,
|
||||
cf_ip_happy_close,
|
||||
cf_ip_happy_shutdown,
|
||||
cf_ip_happy_adjust_pollset,
|
||||
cf_ip_happy_data_pending,
|
||||
@@ -963,21 +993,14 @@ struct Curl_cftype Curl_cft_ip_happy = {
|
||||
cf_ip_happy_query,
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an IP happy eyeball connection filter that uses the, once resolved,
|
||||
* address information to connect on ip families based on connection
|
||||
* configuration.
|
||||
* @param pcf output, the created cfilter
|
||||
* @param data easy handle used in creation
|
||||
* @param conn connection the filter is created for
|
||||
* @param cf_create method to create the sub-filters performing the
|
||||
* actual connects.
|
||||
*/
|
||||
static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf,
|
||||
struct Curl_easy *data,
|
||||
struct Curl_peer *origin,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t transport_peer,
|
||||
struct connectdata *conn,
|
||||
cf_ip_connect_create *cf_create,
|
||||
uint8_t transport)
|
||||
struct Curl_peer *tunnel_peer,
|
||||
uint8_t tunnel_transport)
|
||||
{
|
||||
struct cf_ip_happy_ctx *ctx = NULL;
|
||||
CURLcode result;
|
||||
@@ -990,35 +1013,39 @@ static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf,
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
ctx->transport = transport;
|
||||
ctx->cf_create = cf_create;
|
||||
result = cf_ip_ballers_init(&ctx->ballers, data,
|
||||
origin, peer, transport_peer,
|
||||
tunnel_peer, tunnel_transport,
|
||||
data->set.happy_eyeballs_timeout,
|
||||
IP_HE_MAX_CONCURRENT_ATTEMPTS);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
result = Curl_cf_create(pcf, &Curl_cft_ip_happy, ctx);
|
||||
|
||||
out:
|
||||
if(result) {
|
||||
curlx_safefree(*pcf);
|
||||
cf_ip_happy_ctx_destroy(ctx);
|
||||
cf_ip_happy_ctx_destroy(ctx, data);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at,
|
||||
struct Curl_easy *data,
|
||||
uint8_t transport)
|
||||
struct Curl_peer *origin,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t transport_peer,
|
||||
struct Curl_peer *tunnel_peer,
|
||||
uint8_t tunnel_transport)
|
||||
{
|
||||
cf_ip_connect_create *cf_create;
|
||||
struct Curl_cfilter *cf;
|
||||
CURLcode result;
|
||||
|
||||
/* Need to be first */
|
||||
DEBUGASSERT(cf_at);
|
||||
cf_create = get_cf_create(transport);
|
||||
if(!cf_create) {
|
||||
CURL_TRC_CF(data, cf_at, "unsupported transport type %u", transport);
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
}
|
||||
result = cf_ip_happy_create(&cf, data, cf_at->conn, cf_create, transport);
|
||||
result = cf_ip_happy_create(&cf, data, origin, peer, transport_peer,
|
||||
cf_at->conn, tunnel_peer, tunnel_transport);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
|
||||
@@ -29,28 +29,44 @@ struct connectdata;
|
||||
struct Curl_addrinfo;
|
||||
struct Curl_cfilter;
|
||||
struct Curl_easy;
|
||||
struct Curl_peer;
|
||||
struct Curl_sockaddr_ex;
|
||||
|
||||
/**
|
||||
* Create a cfilter for making an "ip" connection to the
|
||||
* given address, using parameters from `conn`. The "ip" connection
|
||||
* can be a TCP socket, a UDP socket or even a QUIC connection.
|
||||
* Create a cfilter to connect to `origin` via an optional `peer`
|
||||
* using `transport_peer` and `addr`.
|
||||
* With a `tunnel_peer` present, the filter will be used to proxy tunnel
|
||||
* to it and the tunnel will use `tunnel_transport`.
|
||||
* `pcf`: the filter created on success
|
||||
* `data`: the transfer initiating the connect
|
||||
* `conn`: the connection that gets connected
|
||||
*
|
||||
* It MUST use only the supplied `ai` for its connection attempt.
|
||||
*
|
||||
* Such a filter may be used in "happy eyeball" scenarios, and its
|
||||
* `connect` implementation needs to support non-blocking. Once connected,
|
||||
* The filter is used in "happy eyeball" scenarios. Once connected,
|
||||
* it MAY be installed in the connection filter chain to serve transfers.
|
||||
*/
|
||||
typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf,
|
||||
struct Curl_easy *data,
|
||||
struct Curl_peer *origin,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t transport_peer,
|
||||
struct connectdata *conn,
|
||||
struct Curl_sockaddr_ex *addr,
|
||||
uint8_t transport);
|
||||
struct Curl_peer *tunnel_peer,
|
||||
uint8_t tunnel_transport);
|
||||
|
||||
/**
|
||||
* Create an IP happy eyeball connection filter that connects to `origin`
|
||||
* via an optional `peer` using `transport_peer`.
|
||||
* With a `tunnel_peer` present, the filter will be used to proxy tunnel
|
||||
* to it and the tunnel will use `tunnel_transport`.
|
||||
*/
|
||||
CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at,
|
||||
struct Curl_easy *data,
|
||||
uint8_t transport);
|
||||
struct Curl_peer *origin,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t transport_peer,
|
||||
struct Curl_peer *tunnel_peer,
|
||||
uint8_t tunnel_transport);
|
||||
|
||||
extern struct Curl_cftype Curl_cft_ip_happy;
|
||||
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "curl_setup.h"
|
||||
|
||||
#ifndef CURL_DISABLE_WEBSOCKETS
|
||||
/* only used for this protocol, so far */
|
||||
|
||||
#include "urldata.h"
|
||||
#include "bufq.h"
|
||||
#include "cfilters.h"
|
||||
#include "cf-recvbuf.h"
|
||||
#include "curl_trc.h"
|
||||
|
||||
#define CURL_CF_RECVBUF_CHUNK (16 * 1024)
|
||||
|
||||
struct cf_recvbuf_ctx {
|
||||
struct bufq recvbuf;
|
||||
};
|
||||
|
||||
static void cf_recvbuf_destroy(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_recvbuf_ctx *ctx = cf->ctx;
|
||||
(void)data;
|
||||
if(ctx) {
|
||||
Curl_bufq_free(&ctx->recvbuf);
|
||||
curlx_free(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static CURLcode cf_recvbuf_recv(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
char *buf, size_t len,
|
||||
size_t *pnread)
|
||||
{
|
||||
struct cf_recvbuf_ctx *ctx = cf->ctx;
|
||||
|
||||
if(!Curl_bufq_is_empty(&ctx->recvbuf)) {
|
||||
return Curl_bufq_cread(&ctx->recvbuf, buf, len, pnread);
|
||||
}
|
||||
|
||||
if(cf->next)
|
||||
return cf->next->cft->do_recv(cf->next, data, buf, len, pnread);
|
||||
*pnread = 0;
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
|
||||
static bool cf_recvbuf_data_pending(struct Curl_cfilter *cf,
|
||||
const struct Curl_easy *data)
|
||||
{
|
||||
struct cf_recvbuf_ctx *ctx = cf->ctx;
|
||||
|
||||
if(!Curl_bufq_is_empty(&ctx->recvbuf))
|
||||
return TRUE;
|
||||
|
||||
return cf->next ?
|
||||
cf->next->cft->has_data_pending(cf->next, data) : FALSE;
|
||||
}
|
||||
|
||||
struct Curl_cftype Curl_cft_recvbuf = {
|
||||
"RECVBUF",
|
||||
0,
|
||||
CURL_LOG_LVL_NONE,
|
||||
cf_recvbuf_destroy,
|
||||
Curl_cf_def_connect,
|
||||
Curl_cf_def_shutdown,
|
||||
Curl_cf_def_adjust_pollset,
|
||||
cf_recvbuf_data_pending,
|
||||
Curl_cf_def_send,
|
||||
cf_recvbuf_recv,
|
||||
Curl_cf_def_cntrl,
|
||||
Curl_cf_def_conn_is_alive,
|
||||
Curl_cf_def_conn_keep_alive,
|
||||
Curl_cf_def_query,
|
||||
};
|
||||
|
||||
static CURLcode cf_recvbuf_create(struct Curl_cfilter **pcf,
|
||||
struct Curl_easy *data,
|
||||
const uint8_t *buf, size_t blen)
|
||||
{
|
||||
struct Curl_cfilter *cf = NULL;
|
||||
struct cf_recvbuf_ctx *ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
size_t nwritten = 0;
|
||||
|
||||
(void)data;
|
||||
ctx = curlx_calloc(1, sizeof(*ctx));
|
||||
if(!ctx) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
Curl_bufq_init2(&ctx->recvbuf, CURL_CF_RECVBUF_CHUNK,
|
||||
(blen / CURL_CF_RECVBUF_CHUNK) + 1,
|
||||
(BUFQ_OPT_SOFT_LIMIT | BUFQ_OPT_NO_SPARES));
|
||||
result = Curl_bufq_write(&ctx->recvbuf, buf, blen, &nwritten);
|
||||
if(result)
|
||||
goto out;
|
||||
if(nwritten != blen) {
|
||||
result = CURLE_FAILED_INIT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = Curl_cf_create(&cf, &Curl_cft_recvbuf, ctx);
|
||||
if(result)
|
||||
goto out;
|
||||
ctx = NULL;
|
||||
|
||||
out:
|
||||
*pcf = result ? NULL : cf;
|
||||
if(ctx) {
|
||||
Curl_bufq_free(&ctx->recvbuf);
|
||||
curlx_free(ctx);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_cf_recvbuf_add(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex,
|
||||
const uint8_t *buf, size_t blen)
|
||||
{
|
||||
struct Curl_cfilter *cf;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
result = cf_recvbuf_create(&cf, data, buf, blen);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
cf->connected = Curl_conn_is_connected(conn, sockindex);
|
||||
Curl_conn_cf_add(data, conn, sockindex, cf);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* !CURL_DISABLE_WEBSOCKETS */
|
||||
@@ -1,5 +1,5 @@
|
||||
#ifndef HEADER_CURL_NOPROXY_H
|
||||
#define HEADER_CURL_NOPROXY_H
|
||||
#ifndef HEADER_CURL_CF_RECVBUF_H
|
||||
#define HEADER_CURL_CF_RECVBUF_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
@@ -25,8 +25,16 @@
|
||||
***************************************************************************/
|
||||
#include "curl_setup.h"
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
bool Curl_check_noproxy(const char *name, const char *no_proxy);
|
||||
#endif
|
||||
#ifndef CURL_DISABLE_WEBSOCKETS
|
||||
/* only used for this protocol, so far */
|
||||
|
||||
#endif /* HEADER_CURL_NOPROXY_H */
|
||||
CURLcode Curl_cf_recvbuf_add(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex,
|
||||
const uint8_t *buf, size_t blen);
|
||||
|
||||
extern struct Curl_cftype Curl_cft_recvbuf;
|
||||
|
||||
#endif /* !CURL_DISABLE_WEBSOCKETS */
|
||||
|
||||
#endif /* HEADER_CURL_CF_RECVBUF_H */
|
||||
@@ -0,0 +1,476 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "curl_setup.h"
|
||||
|
||||
#include "urldata.h"
|
||||
#include "cfilters.h"
|
||||
#include "cf-haproxy.h"
|
||||
#include "cf-ip-happy.h"
|
||||
#include "cf-setup.h"
|
||||
#include "curl_trc.h"
|
||||
#include "connect.h"
|
||||
#include "http_proxy.h"
|
||||
#include "socks.h"
|
||||
#include "vquic/cf-capsule.h"
|
||||
#include "vquic/vquic.h"
|
||||
#include "vtls/vtls.h"
|
||||
|
||||
|
||||
typedef enum {
|
||||
CF_SETUP_INIT,
|
||||
CF_SETUP_CNNCT_EYEBALLS,
|
||||
CF_SETUP_CNNCT_SOCKS,
|
||||
CF_SETUP_CNNCT_HTTP_PROXY,
|
||||
CF_SETUP_CNNCT_HAPROXY,
|
||||
CF_SETUP_CNNCT_SSL,
|
||||
CF_SETUP_DONE
|
||||
} cf_setup_state;
|
||||
|
||||
struct cf_setup_ctx {
|
||||
cf_setup_state state;
|
||||
int ssl_mode;
|
||||
uint8_t transport;
|
||||
uint8_t retry_count;
|
||||
};
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
|
||||
static CURLcode cf_setup_add_haproxy(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(ctx->state < CF_SETUP_CNNCT_HAPROXY) {
|
||||
if(data->set.haproxyprotocol) {
|
||||
if(ctx->transport == TRNSPRT_QUIC) {
|
||||
failf(data, "haproxy protocol does not support QUIC");
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
}
|
||||
result = Curl_cf_haproxy_insert_after(cf, data);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding HAPROXY filter failed -> %d",
|
||||
(int)result);
|
||||
return result;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "added HAPROXY filter");
|
||||
}
|
||||
ctx->state = CF_SETUP_CNNCT_HAPROXY;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cf_setup_add_socks(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->socks_proxy.peer) {
|
||||
/* Add a SOCKS proxy to go through `first_peer` to `second_peer`*/
|
||||
struct Curl_peer *second_peer;
|
||||
|
||||
if(cf->conn->http_proxy.peer)
|
||||
second_peer = cf->conn->http_proxy.peer;
|
||||
else
|
||||
second_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
|
||||
if(!second_peer)
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
result = Curl_cf_socks_proxy_insert_after(
|
||||
cf, data, second_peer, cf->conn->ip_version,
|
||||
cf->conn->socks_proxy.proxytype,
|
||||
cf->conn->socks_proxy.creds);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding SOCKS filter failed -> %d", (int)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
CURL_TRC_CF(data, cf, "added SOCKS filter to %s:%u",
|
||||
second_peer->hostname, second_peer->port);
|
||||
ctx->state = CF_SETUP_CNNCT_SOCKS;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
static CURLcode cf_setup_add_http_proxy(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY &&
|
||||
cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
|
||||
struct Curl_peer *peer = cf->conn->http_proxy.peer;
|
||||
struct Curl_peer *tunnel_peer =
|
||||
Curl_conn_get_destination(cf->conn, cf->sockindex);
|
||||
|
||||
#ifdef USE_SSL
|
||||
if(CURL_PROXY_IS_HTTPS(cf->conn->http_proxy.proxytype) &&
|
||||
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
|
||||
result = Curl_cf_ssl_proxy_insert_after(
|
||||
cf, data, cf->conn->http_proxy.peer);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding SSL filter for HTTP proxy failed -> %d",
|
||||
(int)result);
|
||||
return result;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "added SSL filter for HTTP proxy");
|
||||
}
|
||||
#endif /* USE_SSL */
|
||||
|
||||
result = Curl_cf_http_proxy_insert_after(
|
||||
cf, data, peer, tunnel_peer,
|
||||
ctx->transport, cf->conn->http_proxy.proxytype);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding HTTP proxy tunnel filter failed -> %d",
|
||||
(int)result);
|
||||
return result;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "added HTTP proxy tunnel filter");
|
||||
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif /* !CURL_DISABLE_HTTP */
|
||||
#endif /* CURL_DISABLE_PROXY */
|
||||
|
||||
/* Get the origin curl connects its socket to.
|
||||
* Can be origin or the first proxy. */
|
||||
static struct Curl_peer *conn_get_first_origin(struct connectdata *conn,
|
||||
int sockindex)
|
||||
{
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(conn->socks_proxy.peer)
|
||||
return conn->socks_proxy.peer;
|
||||
if(conn->http_proxy.peer)
|
||||
return conn->http_proxy.peer;
|
||||
#endif
|
||||
return (sockindex == SECONDARYSOCKET) ? conn->origin2 : conn->origin;
|
||||
}
|
||||
|
||||
static CURLcode cf_setup_add_ip_happy(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
|
||||
/* What is the first hop we directly connect to and what transport
|
||||
* do we use for it? Only on the first hop we can do Happy Eyeballs.
|
||||
* first_origin and first_peer differ on --connect-to. */
|
||||
struct Curl_peer *first_origin =
|
||||
conn_get_first_origin(cf->conn, cf->sockindex);
|
||||
struct Curl_peer *first_peer =
|
||||
Curl_conn_get_first_peer(cf->conn, cf->sockindex);
|
||||
struct Curl_peer *tunnel_peer = NULL;
|
||||
uint8_t first_transport = ctx->transport;
|
||||
|
||||
if(!first_peer)
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
|
||||
if(cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
|
||||
first_transport =
|
||||
Curl_http_proxy_transport(cf->conn->http_proxy.proxytype);
|
||||
tunnel_peer = Curl_conn_get_destination(cf->conn, cf->sockindex);
|
||||
if((first_transport == TRNSPRT_QUIC) && cf->conn->socks_proxy.peer) {
|
||||
failf(data, "HTTP/3 proxy not possible via SOCKS");
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
}
|
||||
}
|
||||
#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
|
||||
|
||||
result = cf_ip_happy_insert_after(cf, data, first_origin, first_peer,
|
||||
first_transport,
|
||||
tunnel_peer, ctx->transport);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding happy eyeballs failed -> %d", (int)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
if(tunnel_peer && (first_transport == TRNSPRT_QUIC)) {
|
||||
CURL_TRC_CF(data, cf, "happy eyeballing to HTTP/3 proxy %s:%u",
|
||||
first_peer->hostname, first_peer->port);
|
||||
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
|
||||
}
|
||||
else {
|
||||
CURL_TRC_CF(data, cf, "happy eyeballing to %s %s:%u",
|
||||
tunnel_peer ? "proxy" : "origin",
|
||||
first_peer->hostname, first_peer->port);
|
||||
ctx->state = CF_SETUP_CNNCT_EYEBALLS;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cf_setup_add_origin_filters(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
(void)data; /* not used in all builds */
|
||||
if(ctx->state < CF_SETUP_CNNCT_SSL) {
|
||||
#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3) && \
|
||||
!defined(CURL_DISABLE_PROXY)
|
||||
|
||||
/* Wanting QUIC with an HTTP tunneling filter, we now need to add
|
||||
* the QUIC filter on top. Without tunneling, this has already
|
||||
* happened in the Happy Eyeball filter. */
|
||||
if(ctx->transport == TRNSPRT_QUIC &&
|
||||
cf->conn->http_proxy.peer && !cf->conn->bits.origin_is_proxy) {
|
||||
struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, cf->sockindex);
|
||||
struct Curl_peer *peer =
|
||||
Curl_conn_get_destination(cf->conn, cf->sockindex);
|
||||
|
||||
result = Curl_cf_capsule_insert_after(cf, data);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding capsule filter failed -> %d",
|
||||
(int)result);
|
||||
return result;
|
||||
}
|
||||
result = Curl_cf_quic_insert_after(cf, origin, peer);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding QUIC filter failed -> %d", (int)result);
|
||||
return result;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "added QUIC filter for origin");
|
||||
}
|
||||
else
|
||||
#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 && CURL_DISABLE_PROXY */
|
||||
#ifdef USE_SSL
|
||||
if((ctx->ssl_mode == CURL_CF_SSL_ENABLE ||
|
||||
(ctx->ssl_mode != CURL_CF_SSL_DISABLE &&
|
||||
cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */
|
||||
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(cf->conn->bits.origin_is_proxy) {
|
||||
result = Curl_cf_ssl_proxy_insert_after(cf, data, cf->conn->origin);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* Another FTP quirk: when adding SSL verification, to a DATA
|
||||
* connection, always verify against the control's origin */
|
||||
struct Curl_peer *origin = Curl_conn_get_origin(cf->conn, FIRSTSOCKET);
|
||||
struct Curl_peer *peer =
|
||||
Curl_conn_get_destination(cf->conn, cf->sockindex);
|
||||
result = Curl_cf_ssl_insert_after(cf, data, origin, peer);
|
||||
}
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "adding SSL filter for origin failed -> %d",
|
||||
(int)result);
|
||||
return result;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "added SSL filter for origin");
|
||||
}
|
||||
#endif /* USE_SSL */
|
||||
ctx->state = CF_SETUP_CNNCT_SSL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cf_setup_connect_steps(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
bool *done)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(cf->connected) {
|
||||
*done = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* connect current sub-chain */
|
||||
connect_sub_chain:
|
||||
VERBOSE(Curl_conn_trc_filters(data, cf->sockindex, "cf_setup_connect"));
|
||||
|
||||
if(cf->next && !cf->next->connected) {
|
||||
result = Curl_conn_cf_connect(cf->next, data, done);
|
||||
if(result || !*done)
|
||||
return result;
|
||||
}
|
||||
|
||||
result = cf_setup_add_ip_happy(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
result = cf_setup_add_socks(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
result = cf_setup_add_http_proxy(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
#endif /* !CURL_DISABLE_HTTP */
|
||||
|
||||
result = cf_setup_add_haproxy(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
#endif /* !CURL_DISABLE_PROXY */
|
||||
|
||||
result = cf_setup_add_origin_filters(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
|
||||
ctx->state = CF_SETUP_DONE;
|
||||
cf->connected = TRUE;
|
||||
*done = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
bool *done)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result;
|
||||
|
||||
/* In some situations, a server/proxy may close the connection and
|
||||
* we need to connect again (HTTP/1.x proxy auth, for example).
|
||||
* We used to close the filters and reuse them for another attempt,
|
||||
* however that complicates filter code and it is simpler to tear them
|
||||
* all down and start over. */
|
||||
retry:
|
||||
result = cf_setup_connect_steps(cf, data, done);
|
||||
|
||||
if(result == CURLE_AGAIN) {
|
||||
++ctx->retry_count;
|
||||
if(ctx->retry_count > 5) /* arbitrary limit, better just timeout? */
|
||||
return CURLE_COULDNT_CONNECT;
|
||||
|
||||
CURL_TRC_CF(data, cf, "retrying connect, %d. time", ctx->retry_count);
|
||||
Curl_conn_cf_discard_chain(&cf->next, data);
|
||||
ctx->state = CF_SETUP_INIT;
|
||||
goto retry;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
|
||||
CURL_TRC_CF(data, cf, "destroy");
|
||||
curlx_safefree(ctx);
|
||||
}
|
||||
|
||||
struct Curl_cftype Curl_cft_setup = {
|
||||
"SETUP",
|
||||
CF_TYPE_SETUP,
|
||||
CURL_LOG_LVL_NONE,
|
||||
cf_setup_destroy,
|
||||
cf_setup_connect,
|
||||
Curl_cf_def_shutdown,
|
||||
Curl_cf_def_adjust_pollset,
|
||||
Curl_cf_def_data_pending,
|
||||
Curl_cf_def_send,
|
||||
Curl_cf_def_recv,
|
||||
Curl_cf_def_cntrl,
|
||||
Curl_cf_def_conn_is_alive,
|
||||
Curl_cf_def_conn_keep_alive,
|
||||
Curl_cf_def_query,
|
||||
};
|
||||
|
||||
static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
|
||||
struct Curl_easy *data,
|
||||
uint8_t transport,
|
||||
int ssl_mode)
|
||||
{
|
||||
struct Curl_cfilter *cf = NULL;
|
||||
struct cf_setup_ctx *ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
(void)data;
|
||||
ctx = curlx_calloc(1, sizeof(*ctx));
|
||||
if(!ctx) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
ctx->state = CF_SETUP_INIT;
|
||||
ctx->ssl_mode = ssl_mode;
|
||||
ctx->transport = transport;
|
||||
|
||||
result = Curl_cf_create(&cf, &Curl_cft_setup, ctx);
|
||||
if(result)
|
||||
goto out;
|
||||
ctx = NULL;
|
||||
|
||||
out:
|
||||
*pcf = result ? NULL : cf;
|
||||
if(ctx) {
|
||||
curlx_free(ctx);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_cf_setup_add(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex,
|
||||
uint8_t transport,
|
||||
int ssl_mode)
|
||||
{
|
||||
struct Curl_cfilter *cf;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
result = cf_setup_create(&cf, data, transport, ssl_mode);
|
||||
if(result)
|
||||
goto out;
|
||||
Curl_conn_cf_add(data, conn, sockindex, cf);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
|
||||
struct Curl_easy *data,
|
||||
uint8_t transport,
|
||||
int ssl_mode)
|
||||
{
|
||||
struct Curl_cfilter *cf;
|
||||
CURLcode result;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
result = cf_setup_create(&cf, data, transport, ssl_mode);
|
||||
if(result)
|
||||
goto out;
|
||||
Curl_conn_cf_insert_after(cf_at, cf);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
#ifndef HEADER_CURL_CF_SETUP_H
|
||||
#define HEADER_CURL_CF_SETUP_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "curl_setup.h"
|
||||
|
||||
struct Curl_dns_entry;
|
||||
struct ip_quadruple;
|
||||
struct Curl_peer;
|
||||
struct Curl_str;
|
||||
|
||||
CURLcode Curl_cf_setup_add(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex,
|
||||
uint8_t transport,
|
||||
int ssl_mode);
|
||||
|
||||
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
|
||||
struct Curl_easy *data,
|
||||
uint8_t transport,
|
||||
int ssl_mode);
|
||||
|
||||
extern struct Curl_cftype Curl_cft_setup;
|
||||
|
||||
#endif /* HEADER_CURL_CF_SETUP_H */
|
||||
+265
-160
@@ -26,6 +26,9 @@
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h> /* <netinet/tcp.h> may need it */
|
||||
#endif
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#include <sys/un.h> /* for sockaddr_un */
|
||||
#endif
|
||||
#ifdef HAVE_LINUX_TCP_H
|
||||
#include <linux/tcp.h>
|
||||
#elif defined(HAVE_NETINET_TCP_H)
|
||||
@@ -44,16 +47,17 @@
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_IFADDRS_H
|
||||
#include <ifaddrs.h>
|
||||
#endif
|
||||
#ifdef HAVE_NET_IF_H
|
||||
#include <net/if.h>
|
||||
#endif
|
||||
#ifdef __VMS
|
||||
#include <in.h>
|
||||
#include <inet.h>
|
||||
#endif
|
||||
|
||||
#ifdef __DragonFly__
|
||||
/* Required for __DragonFly_version */
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
|
||||
#include "urldata.h"
|
||||
#include "curl_trc.h"
|
||||
#include "if2ip.h"
|
||||
@@ -63,11 +67,13 @@
|
||||
#include "curl_addrinfo.h"
|
||||
#include "select.h"
|
||||
#include "multiif.h"
|
||||
#include "curlx/inet_ntop.h"
|
||||
#include "curlx/inet_pton.h"
|
||||
#include "progress.h"
|
||||
#include "conncache.h"
|
||||
#include "multihandle.h"
|
||||
#include "rand.h"
|
||||
#include "sockaddr.h"
|
||||
#include "curlx/strdup.h"
|
||||
#include "system_win32.h"
|
||||
#include "curlx/nonblock.h"
|
||||
@@ -77,6 +83,63 @@
|
||||
#include "curlx/strparse.h"
|
||||
|
||||
|
||||
/* retrieves ip address and port from a sockaddr structure. note it calls
|
||||
* curlx_inet_ntop which sets errno on fail, not SOCKERRNO.
|
||||
* @unittest 1607
|
||||
*/
|
||||
UNITTEST bool sockaddr2string(struct sockaddr *sa, curl_socklen_t salen,
|
||||
char *addr, uint16_t *port);
|
||||
UNITTEST bool sockaddr2string(struct sockaddr *sa, curl_socklen_t salen,
|
||||
char *addr, uint16_t *port)
|
||||
{
|
||||
struct sockaddr_in *si = NULL;
|
||||
#ifdef USE_IPV6
|
||||
struct sockaddr_in6 *si6 = NULL;
|
||||
#endif
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
struct sockaddr_un *su = NULL;
|
||||
#else
|
||||
(void)salen;
|
||||
#endif
|
||||
|
||||
switch(sa->sa_family) {
|
||||
case AF_INET:
|
||||
si = (struct sockaddr_in *)(void *)sa;
|
||||
if(curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) {
|
||||
*port = ntohs(si->sin_port);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
#ifdef USE_IPV6
|
||||
case AF_INET6:
|
||||
si6 = (struct sockaddr_in6 *)(void *)sa;
|
||||
if(curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr, MAX_IPADR_LEN)) {
|
||||
*port = ntohs(si6->sin6_port);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
case AF_UNIX:
|
||||
if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) {
|
||||
su = (struct sockaddr_un *)sa;
|
||||
curl_msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
|
||||
}
|
||||
else
|
||||
addr[0] = 0; /* socket with no name */
|
||||
*port = 0;
|
||||
return TRUE;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
addr[0] = '\0';
|
||||
*port = 0;
|
||||
errno = SOCKEAFNOSUPPORT;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void tcpnodelay(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
curl_socket_t sockfd)
|
||||
@@ -86,8 +149,7 @@ static void tcpnodelay(struct Curl_cfilter *cf,
|
||||
int level = IPPROTO_TCP;
|
||||
VERBOSE(char buffer[STRERROR_LEN]);
|
||||
|
||||
if(setsockopt(sockfd, level, TCP_NODELAY,
|
||||
(void *)&onoff, sizeof(onoff)) < 0)
|
||||
if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff, sizeof(onoff)) < 0)
|
||||
CURL_TRC_CF(data, cf, "Could not set TCP_NODELAY: %s",
|
||||
curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
|
||||
#else
|
||||
@@ -97,8 +159,8 @@ static void tcpnodelay(struct Curl_cfilter *cf,
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(USE_WINSOCK) || defined(TCP_KEEPIDLE) || \
|
||||
defined(TCP_KEEPALIVE) || defined(TCP_KEEPALIVE_THRESHOLD) || \
|
||||
#if defined(USE_WINSOCK) || defined(TCP_KEEPIDLE) || \
|
||||
defined(TCP_KEEPALIVE) || defined(TCP_KEEPALIVE_THRESHOLD) || \
|
||||
defined(TCP_KEEPINTVL) || defined(TCP_KEEPALIVE_ABORT_THRESHOLD)
|
||||
#if defined(USE_WINSOCK) || \
|
||||
(defined(__sun) && !defined(TCP_KEEPIDLE)) || \
|
||||
@@ -132,14 +194,15 @@ static void tcpkeepalive(struct Curl_cfilter *cf,
|
||||
VERSION_GREATER_THAN_EQUAL)) {
|
||||
CURL_TRC_CF(data, cf, "Set TCP_KEEP* on fd=%" FMT_SOCKET_T, sockfd);
|
||||
optval = curlx_sltosi(data->set.tcp_keepidle);
|
||||
/* Offered by mingw-w64 v12+. MS SDK 6.0A+. */
|
||||
/* Offered by mingw-w64 v12+, MS SDK 6.0A/VS2008+ */
|
||||
#ifndef TCP_KEEPALIVE
|
||||
#define TCP_KEEPALIVE 3
|
||||
#endif
|
||||
/* Offered by mingw-w64 v12+. MS SDK ~10+/~VS2017+. */
|
||||
/* Offered by mingw-w64 v12+, MS SDK 10.0.15063.0/VS2017 15.1+ */
|
||||
#ifndef TCP_KEEPCNT
|
||||
#define TCP_KEEPCNT 16
|
||||
#endif
|
||||
/* Offered by mingw-w64 v12+, MS SDK 10.0.16299.0/VS2017 15.4+ */
|
||||
#ifndef TCP_KEEPIDLE
|
||||
#define TCP_KEEPIDLE TCP_KEEPALIVE
|
||||
#endif
|
||||
@@ -302,6 +365,49 @@ int Curl_sock_nosigpipe(curl_socket_t sockfd)
|
||||
}
|
||||
#endif /* USE_SO_NOSIGPIPE */
|
||||
|
||||
#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
|
||||
static uint32_t get_scope_id(struct Curl_easy *data,
|
||||
struct sockaddr_in6 *sa6)
|
||||
{
|
||||
uint32_t scope_id = 0;
|
||||
if(data->conn->scope_id)
|
||||
return data->conn->scope_id;
|
||||
/* NOLINTNEXTLINE(clang-analyzer-core.uninitialized.Assign) */
|
||||
scope_id = sa6->sin6_scope_id;
|
||||
if(!scope_id && IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr)) {
|
||||
/* The resolver did not set scope_id for this link-local address.
|
||||
* Try to determine it from the system's network interfaces.
|
||||
* Without a scope_id, connect() to a link-local address fails
|
||||
* with EINVAL on Linux.
|
||||
* NOTE: On multi-homed hosts with several interfaces having
|
||||
* link-local addresses, this picks the first one found, which
|
||||
* may not be the correct outgoing interface. */
|
||||
#if defined(HAVE_GETIFADDRS) && defined(HAVE_NET_IF_H)
|
||||
struct ifaddrs *ifa, *ifa_list;
|
||||
if(getifaddrs(&ifa_list) == 0) {
|
||||
for(ifa = ifa_list; ifa; ifa = ifa->ifa_next) {
|
||||
if(ifa->ifa_addr && ifa->ifa_addr->sa_family == AF_INET6 &&
|
||||
(ifa->ifa_flags & IFF_UP) &&
|
||||
!(ifa->ifa_flags & IFF_LOOPBACK)) {
|
||||
struct sockaddr_in6 *s6 = (void *)ifa->ifa_addr;
|
||||
if(IN6_IS_ADDR_LINKLOCAL(&s6->sin6_addr) && s6->sin6_scope_id) {
|
||||
scope_id = s6->sin6_scope_id;
|
||||
infof(data,
|
||||
"determined scope_id=%lu for link-local address "
|
||||
"from local interface",
|
||||
(unsigned long)scope_id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
freeifaddrs(ifa_list);
|
||||
}
|
||||
#endif /* HAVE_GETIFADDRS && HAVE_NET_IF_H */
|
||||
}
|
||||
return scope_id;
|
||||
}
|
||||
#endif
|
||||
|
||||
static CURLcode socket_open(struct Curl_easy *data,
|
||||
struct Curl_sockaddr_ex *addr,
|
||||
curl_socket_t *sockfd)
|
||||
@@ -371,9 +477,9 @@ static CURLcode socket_open(struct Curl_easy *data,
|
||||
#endif
|
||||
|
||||
#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
|
||||
if(data->conn->scope_id && (addr->family == AF_INET6)) {
|
||||
if(addr->family == AF_INET6) {
|
||||
struct sockaddr_in6 * const sa6 = (void *)&addr->curl_sa_addr;
|
||||
sa6->sin6_scope_id = data->conn->scope_id;
|
||||
sa6->sin6_scope_id = get_scope_id(data, sa6);
|
||||
}
|
||||
#endif
|
||||
return CURLE_OK;
|
||||
@@ -511,8 +617,7 @@ CURLcode Curl_parse_interface(const char *input,
|
||||
++host_part;
|
||||
*host = curlx_memdup0(host_part, len - (host_part - input));
|
||||
if(!*host) {
|
||||
curlx_free(*iface);
|
||||
*iface = NULL;
|
||||
curlx_safefree(*iface);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
return CURLE_OK;
|
||||
@@ -547,7 +652,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
|
||||
const char *host_input = data->set.str[STRING_BINDHOST];
|
||||
const char *iface = iface_input ? iface_input : dev;
|
||||
const char *host = host_input ? host_input : dev;
|
||||
int error;
|
||||
int sockerr;
|
||||
#ifdef IP_BIND_ADDRESS_NO_PORT
|
||||
int on = 1;
|
||||
#endif
|
||||
@@ -610,9 +715,9 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
|
||||
if(iface_input && !host_input) {
|
||||
/* Do not fall back to treating it as a hostname */
|
||||
char buffer[STRERROR_LEN];
|
||||
data->state.os_errno = error = SOCKERRNO;
|
||||
data->state.os_errno = sockerr = SOCKERRNO;
|
||||
failf(data, "Could not bind to interface '%s' with errno %d: %s",
|
||||
iface, error, curlx_strerror(error, buffer, sizeof(buffer)));
|
||||
iface, sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer)));
|
||||
return CURLE_INTERFACE_FAILED;
|
||||
}
|
||||
break;
|
||||
@@ -624,7 +729,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
|
||||
* We now have the numerical IP address in the 'myhost' buffer
|
||||
*/
|
||||
host = myhost;
|
||||
infof(data, "Local Interface %s is ip %s using address family %i",
|
||||
infof(data, "Local Interface %s is ip %s using address family %d",
|
||||
iface, host, af);
|
||||
done = 1;
|
||||
break;
|
||||
@@ -650,7 +755,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
|
||||
int h_af = h->addr->ai_family;
|
||||
/* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
|
||||
Curl_printable_address(h->addr, myhost, sizeof(myhost));
|
||||
infof(data, "Name '%s' family %i resolved to '%s' family %i",
|
||||
infof(data, "Name '%s' family %d resolved to '%s' family %d",
|
||||
host, af, myhost, h_af);
|
||||
Curl_dns_entry_unlink(data, &h); /* this will NULL, potential free h */
|
||||
if(af != h_af) {
|
||||
@@ -714,9 +819,9 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
|
||||
generic resolve error. */
|
||||
char buffer[STRERROR_LEN];
|
||||
data->state.errorbuf = FALSE;
|
||||
data->state.os_errno = error = SOCKERRNO;
|
||||
data->state.os_errno = sockerr = SOCKERRNO;
|
||||
failf(data, "Could not bind to '%s' with errno %d: %s", host,
|
||||
error, curlx_strerror(error, buffer, sizeof(buffer)));
|
||||
sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer)));
|
||||
return CURLE_INTERFACE_FAILED;
|
||||
}
|
||||
}
|
||||
@@ -765,9 +870,9 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
|
||||
}
|
||||
{
|
||||
char buffer[STRERROR_LEN];
|
||||
data->state.os_errno = error = SOCKERRNO;
|
||||
data->state.os_errno = sockerr = SOCKERRNO;
|
||||
failf(data, "bind failed with errno %d: %s",
|
||||
error, curlx_strerror(error, buffer, sizeof(buffer)));
|
||||
sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer)));
|
||||
}
|
||||
|
||||
return CURLE_INTERFACE_FAILED;
|
||||
@@ -777,12 +882,12 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
|
||||
/*
|
||||
* verifyconnect() returns TRUE if the connect really has happened.
|
||||
*/
|
||||
static bool verifyconnect(curl_socket_t sockfd, int *error)
|
||||
static bool verifyconnect(curl_socket_t sockfd, int *psockerr)
|
||||
{
|
||||
bool rc = TRUE;
|
||||
#ifdef SO_ERROR
|
||||
int err = 0;
|
||||
curl_socklen_t errSize = sizeof(err);
|
||||
int sockerr = 0;
|
||||
curl_socklen_t errSize = sizeof(sockerr);
|
||||
|
||||
#ifdef _WIN32
|
||||
/*
|
||||
@@ -803,27 +908,27 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
|
||||
SleepEx(0, FALSE);
|
||||
#endif
|
||||
|
||||
if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
|
||||
err = SOCKERRNO;
|
||||
if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&sockerr, &errSize))
|
||||
sockerr = SOCKERRNO;
|
||||
#if defined(EBADIOCTL) && defined(__minix)
|
||||
/* Minix 3.1.x does not support getsockopt on UDP sockets */
|
||||
if(EBADIOCTL == err) {
|
||||
if(EBADIOCTL == sockerr) {
|
||||
SET_SOCKERRNO(0);
|
||||
err = 0;
|
||||
sockerr = 0;
|
||||
}
|
||||
#endif
|
||||
if((err == 0) || (SOCKEISCONN == err))
|
||||
if((sockerr == 0) || (SOCKEISCONN == sockerr))
|
||||
/* we are connected, awesome! */
|
||||
rc = TRUE;
|
||||
else
|
||||
/* This was not a successful connect */
|
||||
rc = FALSE;
|
||||
if(error)
|
||||
*error = err;
|
||||
if(psockerr)
|
||||
*psockerr = sockerr;
|
||||
#else
|
||||
(void)sockfd;
|
||||
if(error)
|
||||
*error = SOCKERRNO;
|
||||
if(psockerr)
|
||||
*psockerr = SOCKERRNO;
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
@@ -832,38 +937,25 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
|
||||
* Determine the curl code for a socket connect() == -1 with errno.
|
||||
*/
|
||||
static CURLcode socket_connect_result(struct Curl_easy *data,
|
||||
const char *ipaddress, int error)
|
||||
const char *ipaddress, int sockerr)
|
||||
{
|
||||
switch(error) {
|
||||
case SOCKEINPROGRESS:
|
||||
case SOCKEWOULDBLOCK:
|
||||
#ifdef EAGAIN
|
||||
#if (EAGAIN) != (SOCKEWOULDBLOCK)
|
||||
/* On some platforms EAGAIN and EWOULDBLOCK are the
|
||||
* same value, and on others they are different, hence
|
||||
* the odd #if
|
||||
*/
|
||||
case EAGAIN:
|
||||
#endif
|
||||
#endif
|
||||
if(sockerr == SOCKEINPROGRESS || SOCK_EAGAIN(sockerr))
|
||||
return CURLE_OK;
|
||||
|
||||
default:
|
||||
/* unknown error, fallthrough and try another address! */
|
||||
{
|
||||
VERBOSE(char buffer[STRERROR_LEN]);
|
||||
infof(data, "Immediate connect fail for %s: %s", ipaddress,
|
||||
curlx_strerror(error, buffer, sizeof(buffer)));
|
||||
NOVERBOSE((void)ipaddress);
|
||||
}
|
||||
data->state.os_errno = error;
|
||||
/* connect failed */
|
||||
return CURLE_COULDNT_CONNECT;
|
||||
/* unknown error, fallthrough and try another address! */
|
||||
{
|
||||
VERBOSE(char buffer[STRERROR_LEN]);
|
||||
infof(data, "Immediate connect fail for %s: %s", ipaddress,
|
||||
curlx_strerror(sockerr, buffer, sizeof(buffer)));
|
||||
NOVERBOSE((void)ipaddress);
|
||||
}
|
||||
data->state.os_errno = sockerr;
|
||||
/* connect failed */
|
||||
return CURLE_COULDNT_CONNECT;
|
||||
}
|
||||
|
||||
struct cf_socket_ctx {
|
||||
uint8_t transport;
|
||||
struct Curl_peer *peer;
|
||||
struct Curl_sockaddr_ex addr; /* address to connect to */
|
||||
curl_socket_t sock; /* current attempt socket */
|
||||
struct ip_quadruple ip; /* The IP quadruple 2x(addr+port) */
|
||||
@@ -874,13 +966,14 @@ struct cf_socket_ctx {
|
||||
struct curltime last_sndbuf_query_at; /* when SO_SNDBUF last queried */
|
||||
ULONG sndbuf_size; /* the last set SO_SNDBUF size */
|
||||
#endif
|
||||
int error; /* errno of last failure or 0 */
|
||||
int sockerr; /* socket error of last failure or 0 */
|
||||
#ifdef DEBUGBUILD
|
||||
int wblock_percent; /* percent of writes doing EAGAIN */
|
||||
int wpartial_percent; /* percent of bytes written in send */
|
||||
int rblock_percent; /* percent of reads doing EAGAIN */
|
||||
size_t recv_max; /* max enforced read size */
|
||||
#endif
|
||||
uint8_t transport;
|
||||
BIT(got_first_byte); /* if first byte was received */
|
||||
BIT(listening); /* socket is listening */
|
||||
BIT(accepted); /* socket was accepted, not connected */
|
||||
@@ -889,10 +982,12 @@ struct cf_socket_ctx {
|
||||
};
|
||||
|
||||
static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx,
|
||||
struct Curl_peer *peer,
|
||||
struct Curl_sockaddr_ex *addr,
|
||||
uint8_t transport)
|
||||
{
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
Curl_peer_link(&ctx->peer, peer);
|
||||
ctx->sock = CURL_SOCKET_BAD;
|
||||
ctx->transport = transport;
|
||||
ctx->addr = *addr;
|
||||
@@ -929,22 +1024,12 @@ static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx,
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
static void cf_socket_ctx_free(struct cf_socket_ctx *ctx)
|
||||
{
|
||||
struct cf_socket_ctx *ctx = cf->ctx;
|
||||
|
||||
if(ctx && ctx->sock != CURL_SOCKET_BAD) {
|
||||
CURL_TRC_CF(data, cf, "cf_socket_close, fd=%" FMT_SOCKET_T, ctx->sock);
|
||||
if(ctx->sock == cf->conn->sock[cf->sockindex])
|
||||
cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
|
||||
socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
|
||||
ctx->sock = CURL_SOCKET_BAD;
|
||||
ctx->active = FALSE;
|
||||
memset(&ctx->started_at, 0, sizeof(ctx->started_at));
|
||||
memset(&ctx->connected_at, 0, sizeof(ctx->connected_at));
|
||||
if(ctx) {
|
||||
Curl_peer_unlink(&ctx->peer);
|
||||
curlx_free(ctx);
|
||||
}
|
||||
|
||||
cf->connected = FALSE;
|
||||
}
|
||||
|
||||
static CURLcode cf_socket_shutdown(struct Curl_cfilter *cf,
|
||||
@@ -973,10 +1058,16 @@ static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
struct cf_socket_ctx *ctx = cf->ctx;
|
||||
|
||||
cf_socket_close(cf, data);
|
||||
CURL_TRC_CF(data, cf, "destroy");
|
||||
curlx_free(ctx);
|
||||
cf->ctx = NULL;
|
||||
if(ctx) {
|
||||
if(ctx->sock != CURL_SOCKET_BAD) {
|
||||
CURL_TRC_CF(data, cf, "cf_socket_close, fd=%" FMT_SOCKET_T, ctx->sock);
|
||||
if(ctx->sock == cf->conn->sock[cf->sockindex])
|
||||
cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
|
||||
socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
|
||||
}
|
||||
cf_socket_ctx_free(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void set_local_ip(struct Curl_cfilter *cf,
|
||||
@@ -996,12 +1087,12 @@ static void set_local_ip(struct Curl_cfilter *cf,
|
||||
|
||||
memset(&ssloc, 0, sizeof(ssloc));
|
||||
if(getsockname(ctx->sock, (struct sockaddr *)&ssloc, &slen)) {
|
||||
VERBOSE(int error = SOCKERRNO);
|
||||
VERBOSE(int sockerr = SOCKERRNO);
|
||||
infof(data, "getsockname() failed with errno %d: %s",
|
||||
error, curlx_strerror(error, buffer, sizeof(buffer)));
|
||||
sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer)));
|
||||
}
|
||||
else if(!Curl_addr2string((struct sockaddr *)&ssloc, slen,
|
||||
ctx->ip.local_ip, &ctx->ip.local_port)) {
|
||||
else if(!sockaddr2string((struct sockaddr *)&ssloc, slen,
|
||||
ctx->ip.local_ip, &ctx->ip.local_port)) {
|
||||
infof(data, "ssloc inet_ntop() failed with errno %d: %s",
|
||||
errno, curlx_strerror(errno, buffer, sizeof(buffer)));
|
||||
}
|
||||
@@ -1018,12 +1109,15 @@ static CURLcode set_remote_ip(struct Curl_cfilter *cf,
|
||||
|
||||
/* store remote address and port used in this connection attempt */
|
||||
ctx->ip.transport = ctx->transport;
|
||||
if(!Curl_addr2string(&ctx->addr.curl_sa_addr,
|
||||
(curl_socklen_t)ctx->addr.addrlen,
|
||||
ctx->ip.remote_ip, &ctx->ip.remote_port)) {
|
||||
if(!sockaddr2string(&ctx->addr.curl_sa_addr,
|
||||
(curl_socklen_t)ctx->addr.addrlen,
|
||||
ctx->ip.remote_ip, &ctx->ip.remote_port)) {
|
||||
char buffer[STRERROR_LEN];
|
||||
|
||||
ctx->error = errno;
|
||||
/* using bare errno instead of SOCKERRNO is safe here, because
|
||||
sockaddr2string() calls curlx_inet_ntop(), and they both report failures
|
||||
via errno (even on Windows builds). */
|
||||
ctx->sockerr = errno;
|
||||
/* malformed address or bug in inet_ntop, try next address */
|
||||
failf(data, "curl_sa_addr inet_ntop() failed with errno %d: %s",
|
||||
errno, curlx_strerror(errno, buffer, sizeof(buffer)));
|
||||
@@ -1091,7 +1185,20 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
|
||||
(void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||
(void *)&on, sizeof(on));
|
||||
#endif
|
||||
infof(data, " Trying [%s]:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
|
||||
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
|
||||
{
|
||||
struct sockaddr_in6 *sa6 = (void *)&ctx->addr.curl_sa_addr;
|
||||
if(sa6->sin6_scope_id)
|
||||
infof(data, " Trying [%s]:%d scope_id=%lu...",
|
||||
ctx->ip.remote_ip, ctx->ip.remote_port,
|
||||
(unsigned long)sa6->sin6_scope_id);
|
||||
else
|
||||
#endif
|
||||
infof(data, " Trying [%s]:%d...",
|
||||
ctx->ip.remote_ip, ctx->ip.remote_port);
|
||||
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
#endif
|
||||
@@ -1154,7 +1261,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
|
||||
error = curlx_nonblock(ctx->sock, TRUE);
|
||||
if(error < 0) {
|
||||
result = CURLE_UNSUPPORTED_PROTOCOL;
|
||||
ctx->error = SOCKERRNO;
|
||||
ctx->sockerr = SOCKERRNO;
|
||||
goto out;
|
||||
}
|
||||
#else
|
||||
@@ -1164,7 +1271,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
|
||||
error = curlx_nonblock(ctx->sock, TRUE);
|
||||
if(error < 0) {
|
||||
result = CURLE_UNSUPPORTED_PROTOCOL;
|
||||
ctx->error = SOCKERRNO;
|
||||
ctx->sockerr = SOCKERRNO;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -1183,7 +1290,7 @@ out:
|
||||
cf->connected = TRUE;
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" FMT_SOCKET_T,
|
||||
result, ctx->sock);
|
||||
(int)result, ctx->sock);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1257,7 +1364,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
|
||||
|
||||
*done = FALSE; /* a negative world view is best */
|
||||
if(ctx->sock == CURL_SOCKET_BAD) {
|
||||
int error;
|
||||
int sockerr;
|
||||
|
||||
result = cf_socket_open(cf, data);
|
||||
if(result)
|
||||
@@ -1270,12 +1377,13 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
|
||||
|
||||
/* Connect TCP socket */
|
||||
rc = do_connect(cf, data, (bool)cf->conn->bits.tcp_fastopen);
|
||||
error = SOCKERRNO;
|
||||
sockerr = SOCKERRNO;
|
||||
set_local_ip(cf, data);
|
||||
CURL_TRC_CF(data, cf, "local address %s port %d...",
|
||||
ctx->ip.local_ip, ctx->ip.local_port);
|
||||
if(rc == -1) {
|
||||
result = socket_connect_result(data, ctx->ip.remote_ip, error);
|
||||
ctx->sockerr = sockerr;
|
||||
result = socket_connect_result(data, ctx->ip.remote_ip, sockerr);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
@@ -1295,7 +1403,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
|
||||
return CURLE_OK;
|
||||
}
|
||||
else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
|
||||
if(verifyconnect(ctx->sock, &ctx->error)) {
|
||||
if(verifyconnect(ctx->sock, &ctx->sockerr)) {
|
||||
/* we are connected with TCP, awesome! */
|
||||
ctx->connected_at = *Curl_pgrs_now(data);
|
||||
set_local_ip(cf, data);
|
||||
@@ -1307,7 +1415,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
|
||||
}
|
||||
else if(rc & CURL_CSELECT_ERR) {
|
||||
CURL_TRC_CF(data, cf, "poll/select error on fd=%" FMT_SOCKET_T, ctx->sock);
|
||||
(void)verifyconnect(ctx->sock, &ctx->error);
|
||||
(void)verifyconnect(ctx->sock, &ctx->sockerr);
|
||||
result = CURLE_COULDNT_CONNECT;
|
||||
}
|
||||
|
||||
@@ -1315,10 +1423,10 @@ out:
|
||||
if(result) {
|
||||
VERBOSE(char buffer[STRERROR_LEN]);
|
||||
set_local_ip(cf, data);
|
||||
if(ctx->error) {
|
||||
data->state.os_errno = ctx->error;
|
||||
SET_SOCKERRNO(ctx->error);
|
||||
VERBOSE(curlx_strerror(ctx->error, buffer, sizeof(buffer)));
|
||||
if(ctx->sockerr) {
|
||||
data->state.os_errno = ctx->sockerr;
|
||||
SET_SOCKERRNO(ctx->sockerr);
|
||||
VERBOSE(curlx_strerror(ctx->sockerr, buffer, sizeof(buffer)));
|
||||
}
|
||||
else {
|
||||
VERBOSE(curlx_strcopy(buffer, sizeof(buffer), STRCONST("peer closed")));
|
||||
@@ -1330,7 +1438,7 @@ out:
|
||||
infof(data, "connect to %s port %u from %s port %d failed: %s",
|
||||
ctx->ip.remote_ip, ctx->ip.remote_port,
|
||||
ctx->ip.local_ip, ctx->ip.local_port,
|
||||
curlx_strerror(ctx->error, buffer, sizeof(buffer)));
|
||||
curlx_strerror(ctx->sockerr, buffer, sizeof(buffer)));
|
||||
*done = FALSE;
|
||||
}
|
||||
return result;
|
||||
@@ -1369,7 +1477,7 @@ static CURLcode cf_socket_adjust_pollset(struct Curl_cfilter *cf,
|
||||
|
||||
#ifdef USE_WINSOCK
|
||||
|
||||
/* Offered by mingw-w64 v13+. MS SDK 7.0A+. */
|
||||
/* Offered by mingw-w64 v13+, MS SDK 7.0A/VS2010+ */
|
||||
#ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
|
||||
#define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
|
||||
#endif
|
||||
@@ -1442,22 +1550,12 @@ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
|
||||
if(!curlx_sztouz(rv, pnwritten)) {
|
||||
int sockerr = SOCKERRNO;
|
||||
|
||||
if(
|
||||
#ifdef USE_WINSOCK
|
||||
/* This is how Windows does it */
|
||||
(SOCKEWOULDBLOCK == sockerr)
|
||||
#else
|
||||
/* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
|
||||
due to its inability to send off data without blocking. We therefore
|
||||
treat both error codes the same here */
|
||||
(SOCKEWOULDBLOCK == sockerr) ||
|
||||
(EAGAIN == sockerr) || (SOCKEINTR == sockerr) ||
|
||||
(SOCKEINPROGRESS == sockerr)
|
||||
if(SOCK_EAGAIN(sockerr)
|
||||
#ifndef USE_WINSOCK
|
||||
|| (sockerr == SOCKEINTR) || (sockerr == SOCKEINPROGRESS)
|
||||
#endif
|
||||
) {
|
||||
/* EWOULDBLOCK */
|
||||
result = CURLE_AGAIN;
|
||||
result = CURLE_AGAIN; /* EWOULDBLOCK */
|
||||
}
|
||||
else {
|
||||
char buffer[STRERROR_LEN];
|
||||
@@ -1474,7 +1572,7 @@ static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
#endif
|
||||
|
||||
CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, %zu",
|
||||
orig_len, result, *pnwritten);
|
||||
orig_len, (int)result, *pnwritten);
|
||||
cf->conn->sock[cf->sockindex] = fdsave;
|
||||
return result;
|
||||
}
|
||||
@@ -1508,21 +1606,12 @@ static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
|
||||
if(!curlx_sztouz(rv, pnread)) {
|
||||
int sockerr = SOCKERRNO;
|
||||
|
||||
if(
|
||||
#ifdef USE_WINSOCK
|
||||
/* This is how Windows does it */
|
||||
(SOCKEWOULDBLOCK == sockerr)
|
||||
#else
|
||||
/* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
|
||||
due to its inability to send off data without blocking. We therefore
|
||||
treat both error codes the same here */
|
||||
(SOCKEWOULDBLOCK == sockerr) ||
|
||||
(EAGAIN == sockerr) || (SOCKEINTR == sockerr)
|
||||
if(SOCK_EAGAIN(sockerr)
|
||||
#ifndef USE_WINSOCK
|
||||
|| (sockerr == SOCKEINTR)
|
||||
#endif
|
||||
) {
|
||||
/* EWOULDBLOCK */
|
||||
result = CURLE_AGAIN;
|
||||
result = CURLE_AGAIN; /* EWOULDBLOCK */
|
||||
}
|
||||
else {
|
||||
char buffer[STRERROR_LEN];
|
||||
@@ -1533,7 +1622,7 @@ static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
}
|
||||
}
|
||||
|
||||
CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, %zu", len, result, *pnread);
|
||||
CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, %zu", len, (int)result, *pnread);
|
||||
if(!result && !ctx->got_first_byte) {
|
||||
ctx->first_byte_at = *Curl_pgrs_now(data);
|
||||
ctx->got_first_byte = TRUE;
|
||||
@@ -1548,8 +1637,6 @@ static void cf_socket_update_data(struct Curl_cfilter *cf,
|
||||
if(cf->connected && (cf->sockindex == FIRSTSOCKET)) {
|
||||
struct cf_socket_ctx *ctx = cf->ctx;
|
||||
data->info.primary = ctx->ip;
|
||||
/* not sure if this is redundant... */
|
||||
data->info.conn_remote_port = cf->conn->remote_port;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1696,7 +1783,6 @@ struct Curl_cftype Curl_cft_tcp = {
|
||||
CURL_LOG_LVL_NONE,
|
||||
cf_socket_destroy,
|
||||
cf_tcp_connect,
|
||||
cf_socket_close,
|
||||
cf_socket_shutdown,
|
||||
cf_socket_adjust_pollset,
|
||||
Curl_cf_def_data_pending,
|
||||
@@ -1710,17 +1796,24 @@ struct Curl_cftype Curl_cft_tcp = {
|
||||
|
||||
CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
|
||||
struct Curl_easy *data,
|
||||
struct Curl_peer *origin,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t transport_peer,
|
||||
struct connectdata *conn,
|
||||
struct Curl_sockaddr_ex *addr,
|
||||
uint8_t transport)
|
||||
struct Curl_peer *tunnel_peer,
|
||||
uint8_t tunnel_transport)
|
||||
{
|
||||
struct cf_socket_ctx *ctx = NULL;
|
||||
struct Curl_cfilter *cf = NULL;
|
||||
CURLcode result;
|
||||
|
||||
(void)data;
|
||||
(void)origin;
|
||||
(void)conn;
|
||||
DEBUGASSERT(transport == TRNSPRT_TCP);
|
||||
(void)tunnel_peer;
|
||||
(void)tunnel_transport;
|
||||
DEBUGASSERT(transport_peer == TRNSPRT_TCP);
|
||||
if(!addr) {
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto out;
|
||||
@@ -1732,7 +1825,7 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = cf_socket_ctx_init(ctx, addr, transport);
|
||||
result = cf_socket_ctx_init(ctx, peer, addr, transport_peer);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
@@ -1742,7 +1835,7 @@ out:
|
||||
*pcf = (!result) ? cf : NULL;
|
||||
if(result) {
|
||||
curlx_safefree(cf);
|
||||
curlx_safefree(ctx);
|
||||
cf_socket_ctx_free(ctx);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -1837,7 +1930,8 @@ static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
|
||||
if(ctx->sock == CURL_SOCKET_BAD) {
|
||||
result = cf_socket_open(cf, data);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", result);
|
||||
CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d",
|
||||
(int)result);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1862,7 +1956,6 @@ struct Curl_cftype Curl_cft_udp = {
|
||||
CURL_LOG_LVL_NONE,
|
||||
cf_socket_destroy,
|
||||
cf_udp_connect,
|
||||
cf_socket_close,
|
||||
cf_socket_shutdown,
|
||||
cf_socket_adjust_pollset,
|
||||
Curl_cf_def_data_pending,
|
||||
@@ -1876,24 +1969,31 @@ struct Curl_cftype Curl_cft_udp = {
|
||||
|
||||
CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
|
||||
struct Curl_easy *data,
|
||||
struct Curl_peer *origin,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t transport_peer,
|
||||
struct connectdata *conn,
|
||||
struct Curl_sockaddr_ex *addr,
|
||||
uint8_t transport)
|
||||
struct Curl_peer *tunnel_peer,
|
||||
uint8_t tunnel_transport)
|
||||
{
|
||||
struct cf_socket_ctx *ctx = NULL;
|
||||
struct Curl_cfilter *cf = NULL;
|
||||
CURLcode result;
|
||||
|
||||
(void)data;
|
||||
(void)origin;
|
||||
(void)conn;
|
||||
DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC);
|
||||
(void)tunnel_peer;
|
||||
(void)tunnel_transport;
|
||||
DEBUGASSERT(transport_peer == TRNSPRT_UDP || transport_peer == TRNSPRT_QUIC);
|
||||
ctx = curlx_calloc(1, sizeof(*ctx));
|
||||
if(!ctx) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = cf_socket_ctx_init(ctx, addr, transport);
|
||||
result = cf_socket_ctx_init(ctx, peer, addr, transport_peer);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
@@ -1903,7 +2003,7 @@ out:
|
||||
*pcf = (!result) ? cf : NULL;
|
||||
if(result) {
|
||||
curlx_safefree(cf);
|
||||
curlx_safefree(ctx);
|
||||
cf_socket_ctx_free(ctx);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -1916,7 +2016,6 @@ struct Curl_cftype Curl_cft_unix = {
|
||||
CURL_LOG_LVL_NONE,
|
||||
cf_socket_destroy,
|
||||
cf_tcp_connect,
|
||||
cf_socket_close,
|
||||
cf_socket_shutdown,
|
||||
cf_socket_adjust_pollset,
|
||||
Curl_cf_def_data_pending,
|
||||
@@ -1929,25 +2028,32 @@ struct Curl_cftype Curl_cft_unix = {
|
||||
};
|
||||
|
||||
CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
|
||||
struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
struct Curl_sockaddr_ex *addr,
|
||||
uint8_t transport)
|
||||
struct Curl_easy *data,
|
||||
struct Curl_peer *origin,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t transport_peer,
|
||||
struct connectdata *conn,
|
||||
struct Curl_sockaddr_ex *addr,
|
||||
struct Curl_peer *tunnel_peer,
|
||||
uint8_t tunnel_transport)
|
||||
{
|
||||
struct cf_socket_ctx *ctx = NULL;
|
||||
struct Curl_cfilter *cf = NULL;
|
||||
CURLcode result;
|
||||
|
||||
(void)data;
|
||||
(void)origin;
|
||||
(void)conn;
|
||||
DEBUGASSERT(transport == TRNSPRT_UNIX);
|
||||
(void)tunnel_peer;
|
||||
(void)tunnel_transport;
|
||||
DEBUGASSERT(transport_peer == TRNSPRT_UNIX);
|
||||
ctx = curlx_calloc(1, sizeof(*ctx));
|
||||
if(!ctx) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = cf_socket_ctx_init(ctx, addr, transport);
|
||||
result = cf_socket_ctx_init(ctx, peer, addr, transport_peer);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
@@ -1957,7 +2063,7 @@ out:
|
||||
*pcf = (!result) ? cf : NULL;
|
||||
if(result) {
|
||||
curlx_safefree(cf);
|
||||
curlx_safefree(ctx);
|
||||
cf_socket_ctx_free(ctx);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -2005,13 +2111,13 @@ static void cf_tcp_set_accepted_remote_ip(struct Curl_cfilter *cf,
|
||||
plen = sizeof(ssrem);
|
||||
memset(&ssrem, 0, plen);
|
||||
if(getpeername(ctx->sock, (struct sockaddr *)&ssrem, &plen)) {
|
||||
int error = SOCKERRNO;
|
||||
int sockerr = SOCKERRNO;
|
||||
failf(data, "getpeername() failed with errno %d: %s",
|
||||
error, curlx_strerror(error, buffer, sizeof(buffer)));
|
||||
sockerr, curlx_strerror(sockerr, buffer, sizeof(buffer)));
|
||||
return;
|
||||
}
|
||||
if(!Curl_addr2string((struct sockaddr *)&ssrem, plen,
|
||||
ctx->ip.remote_ip, &ctx->ip.remote_port)) {
|
||||
if(!sockaddr2string((struct sockaddr *)&ssrem, plen,
|
||||
ctx->ip.remote_ip, &ctx->ip.remote_port)) {
|
||||
failf(data, "ssrem inet_ntop() failed with errno %d: %s",
|
||||
errno, curlx_strerror(errno, buffer, sizeof(buffer)));
|
||||
return;
|
||||
@@ -2058,7 +2164,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
|
||||
CURL_TRC_CF(data, cf, "Checking for incoming on fd=%" FMT_SOCKET_T
|
||||
" ip=%s:%d", ctx->sock, ctx->ip.local_ip, ctx->ip.local_port);
|
||||
socketstate = SOCKET_READABLE(ctx->sock, 0);
|
||||
CURL_TRC_CF(data, cf, "socket_check -> %x", socketstate);
|
||||
CURL_TRC_CF(data, cf, "socket_check -> %x", (unsigned int)socketstate);
|
||||
switch(socketstate) {
|
||||
case -1: /* error */
|
||||
/* let's die here */
|
||||
@@ -2128,10 +2234,10 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
|
||||
int error = 0;
|
||||
|
||||
/* activate callback for setting socket options */
|
||||
Curl_set_in_callback(data, true);
|
||||
Curl_set_in_callback(data, TRUE);
|
||||
error = data->set.fsockopt(data->set.sockopt_client,
|
||||
ctx->sock, CURLSOCKTYPE_ACCEPT);
|
||||
Curl_set_in_callback(data, false);
|
||||
Curl_set_in_callback(data, FALSE);
|
||||
|
||||
if(error)
|
||||
return CURLE_ABORTED_BY_CALLBACK;
|
||||
@@ -2146,7 +2252,6 @@ struct Curl_cftype Curl_cft_tcp_accept = {
|
||||
CURL_LOG_LVL_NONE,
|
||||
cf_socket_destroy,
|
||||
cf_tcp_accept_connect,
|
||||
cf_socket_close,
|
||||
cf_socket_shutdown,
|
||||
cf_socket_adjust_pollset,
|
||||
Curl_cf_def_data_pending,
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
***************************************************************************/
|
||||
#include "curl_setup.h"
|
||||
|
||||
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
|
||||
|
||||
struct Curl_addrinfo;
|
||||
struct Curl_cfilter;
|
||||
struct Curl_easy;
|
||||
@@ -34,23 +32,6 @@ struct connectdata;
|
||||
struct Curl_sockaddr_ex;
|
||||
struct ip_quadruple;
|
||||
|
||||
/*
|
||||
* The Curl_sockaddr_ex structure is libcurl's external API curl_sockaddr
|
||||
* structure with enough space available to directly hold any
|
||||
* protocol-specific address structures. The variable declared here will be
|
||||
* used to pass / receive data to/from the fopensocket callback if this has
|
||||
* been set, before that, it is initialized from parameters.
|
||||
*/
|
||||
struct Curl_sockaddr_ex {
|
||||
int family;
|
||||
int socktype;
|
||||
int protocol;
|
||||
unsigned int addrlen;
|
||||
union {
|
||||
struct sockaddr sa;
|
||||
struct Curl_sockaddr_storage buf;
|
||||
} addr;
|
||||
};
|
||||
#define curl_sa_addr addr.sa
|
||||
#define curl_sa_addrbuf addr.buf
|
||||
|
||||
@@ -94,9 +75,13 @@ int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
|
||||
*/
|
||||
CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
|
||||
struct Curl_easy *data,
|
||||
struct Curl_peer *origin,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t transport_peer,
|
||||
struct connectdata *conn,
|
||||
struct Curl_sockaddr_ex *addr,
|
||||
uint8_t transport);
|
||||
struct Curl_peer *tunnel_peer,
|
||||
uint8_t tunnel_transport);
|
||||
|
||||
/**
|
||||
* Creates a cfilter that opens a UDP socket to the given address
|
||||
@@ -107,9 +92,13 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
|
||||
*/
|
||||
CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
|
||||
struct Curl_easy *data,
|
||||
struct Curl_peer *origin,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t transport_peer,
|
||||
struct connectdata *conn,
|
||||
struct Curl_sockaddr_ex *addr,
|
||||
uint8_t transport);
|
||||
struct Curl_peer *tunnel_peer,
|
||||
uint8_t tunnel_transport);
|
||||
|
||||
/**
|
||||
* Creates a cfilter that opens a UNIX socket to the given address
|
||||
@@ -120,9 +109,13 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
|
||||
*/
|
||||
CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
|
||||
struct Curl_easy *data,
|
||||
struct Curl_peer *origin,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t transport_peer,
|
||||
struct connectdata *conn,
|
||||
struct Curl_sockaddr_ex *addr,
|
||||
uint8_t transport);
|
||||
struct Curl_peer *tunnel_peer,
|
||||
uint8_t tunnel_transport);
|
||||
|
||||
/**
|
||||
* Creates a cfilter that keeps a listening socket.
|
||||
|
||||
+88
-215
@@ -33,16 +33,26 @@
|
||||
#include "select.h"
|
||||
#include "curlx/strparse.h"
|
||||
|
||||
#ifdef UNITTESTS
|
||||
/* @unittest 2600 */
|
||||
UNITTEST void cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data);
|
||||
UNITTEST void cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data, bool *done)
|
||||
{
|
||||
cf->connected = FALSE;
|
||||
if(cf->next)
|
||||
cf->next->cft->do_close(cf->next, data);
|
||||
CURLcode result;
|
||||
|
||||
if(cf->connected) {
|
||||
*done = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
if(cf->next) {
|
||||
result = cf->next->cft->do_connect(cf->next, data, done);
|
||||
if(result || !*done)
|
||||
return result;
|
||||
}
|
||||
|
||||
cf->connected = TRUE;
|
||||
*done = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data, bool *done)
|
||||
@@ -117,29 +127,28 @@ CURLcode Curl_cf_def_query(struct Curl_cfilter *cf,
|
||||
}
|
||||
|
||||
#ifdef CURLVERBOSE
|
||||
static void conn_trc_filters(struct Curl_easy *data,
|
||||
int sockindex,
|
||||
const char *info)
|
||||
void Curl_conn_trc_filters(struct Curl_easy *data,
|
||||
int sockindex, const char *info)
|
||||
{
|
||||
if(CURL_TRC_M_is_verbose(data) && data->conn) {
|
||||
struct Curl_cfilter *cf = data->conn->cfilter[sockindex];
|
||||
|
||||
if(cf) {
|
||||
struct dynbuf msg;
|
||||
CURLcode result = CURLE_OK;
|
||||
char msg[256], *buf;
|
||||
int blen, n;
|
||||
|
||||
curlx_dyn_init(&msg, 1024);
|
||||
result = curlx_dyn_addf(&msg, "%s [%d]", info, sockindex);
|
||||
for(; cf && !result; cf = cf->next) {
|
||||
result = curlx_dyn_addf(&msg, "[%s%s]",
|
||||
cf->connected ? "" : "!", cf->cft->name);
|
||||
buf = msg;
|
||||
blen = sizeof(msg) - 1;
|
||||
n = curl_msnprintf(buf, blen, "%s [%d]", info, sockindex);
|
||||
buf += n;
|
||||
blen -= n;
|
||||
for(; cf && blen; cf = cf->next) {
|
||||
n = curl_msnprintf(buf, blen, "[%s%s]",
|
||||
cf->connected ? "" : "!", cf->cft->name);
|
||||
buf += n;
|
||||
blen -= n;
|
||||
}
|
||||
if(!result)
|
||||
CURL_TRC_M(data, "%s", curlx_dyn_ptr(&msg));
|
||||
else
|
||||
CURL_TRC_M(data, "%s [%d] error %d tracing chain",
|
||||
info, sockindex, result);
|
||||
curlx_dyn_free(&msg);
|
||||
CURL_TRC_M(data, "%s%s", msg, blen ? "" : "...");
|
||||
}
|
||||
else
|
||||
CURL_TRC_M(data, "%s [%d][-]", info, sockindex);
|
||||
@@ -170,22 +179,11 @@ void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf,
|
||||
void Curl_conn_cf_discard_all(struct Curl_easy *data,
|
||||
struct connectdata *conn, int sockindex)
|
||||
{
|
||||
struct curltime *pt = &conn->shutdown.start[sockindex];
|
||||
memset(pt, 0, sizeof(*pt));
|
||||
Curl_conn_cf_discard_chain(&conn->cfilter[sockindex], data);
|
||||
}
|
||||
|
||||
void Curl_conn_close(struct Curl_easy *data, int sockindex)
|
||||
{
|
||||
struct Curl_cfilter *cf;
|
||||
|
||||
DEBUGASSERT(data->conn);
|
||||
/* it is valid to call that without filters being present */
|
||||
cf = data->conn->cfilter[sockindex];
|
||||
if(cf) {
|
||||
cf->cft->do_close(cf, data);
|
||||
}
|
||||
Curl_shutdown_clear(data, sockindex);
|
||||
}
|
||||
|
||||
CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done)
|
||||
{
|
||||
struct Curl_cfilter *cf;
|
||||
@@ -225,7 +223,7 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done)
|
||||
bool cfdone = FALSE;
|
||||
result = cf->cft->do_shutdown(cf, data, &cfdone);
|
||||
if(result) {
|
||||
CURL_TRC_CF(data, cf, "shut down failed with %d", result);
|
||||
CURL_TRC_CF(data, cf, "shut down failed with %d", (int)result);
|
||||
return result;
|
||||
}
|
||||
else if(!cfdone) {
|
||||
@@ -427,12 +425,6 @@ CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
|
||||
void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
if(cf)
|
||||
cf->cft->do_close(cf, data);
|
||||
}
|
||||
|
||||
CURLcode Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
const uint8_t *buf, size_t len, bool eos,
|
||||
size_t *pnwritten)
|
||||
@@ -452,29 +444,6 @@ CURLcode Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
|
||||
#ifdef CURLVERBOSE
|
||||
static CURLcode cf_verboseconnect(struct Curl_easy *data,
|
||||
struct Curl_cfilter *cf)
|
||||
{
|
||||
if(Curl_trc_is_verbose(data)) {
|
||||
struct ip_quadruple ipquad;
|
||||
bool is_ipv6;
|
||||
CURLcode result;
|
||||
|
||||
result = Curl_conn_cf_get_ip_info(cf, data, &is_ipv6, &ipquad);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
infof(data, "Established %sconnection to %s (%s port %u) from %s port %u ",
|
||||
(cf->sockindex == SECONDARYSOCKET) ? "2nd " : "",
|
||||
CURL_CONN_HOST_DISPNAME(data->conn),
|
||||
ipquad.remote_ip, ipquad.remote_port,
|
||||
ipquad.local_ip, ipquad.local_port);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
static CURLcode cf_cntrl_all(struct connectdata *conn,
|
||||
struct Curl_easy *data,
|
||||
bool ignore_result,
|
||||
@@ -492,36 +461,14 @@ static CURLcode cf_cntrl_all(struct connectdata *conn,
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cf_cntrl_update_info(struct Curl_easy *data,
|
||||
struct connectdata *conn)
|
||||
void Curl_conn_cntrl_update_info(struct Curl_easy *data,
|
||||
struct connectdata *conn)
|
||||
{
|
||||
cf_cntrl_all(conn, data, TRUE, CF_CTRL_CONN_INFO_UPDATE, 0, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update connection statistics
|
||||
*/
|
||||
static void conn_report_connect_stats(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
if(cf) {
|
||||
struct curltime connected;
|
||||
struct curltime appconnected;
|
||||
|
||||
memset(&connected, 0, sizeof(connected));
|
||||
cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected);
|
||||
if(connected.tv_sec || connected.tv_usec)
|
||||
Curl_pgrsTimeWas(data, TIMER_CONNECT, connected);
|
||||
|
||||
memset(&appconnected, 0, sizeof(appconnected));
|
||||
cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected);
|
||||
if(appconnected.tv_sec || appconnected.tv_usec)
|
||||
Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected);
|
||||
}
|
||||
}
|
||||
|
||||
static void conn_remove_setup_filters(struct Curl_easy *data,
|
||||
int sockindex)
|
||||
void Curl_conn_remove_setup_filters(struct Curl_easy *data,
|
||||
int sockindex)
|
||||
{
|
||||
struct Curl_cfilter **anchor = &data->conn->cfilter[sockindex];
|
||||
while(*anchor) {
|
||||
@@ -538,123 +485,11 @@ static void conn_remove_setup_filters(struct Curl_easy *data,
|
||||
}
|
||||
}
|
||||
|
||||
CURLcode Curl_conn_connect(struct Curl_easy *data,
|
||||
int sockindex,
|
||||
bool blocking,
|
||||
bool *done)
|
||||
{
|
||||
#define CF_CONN_NUM_POLLS_ON_STACK 5
|
||||
struct pollfd a_few_on_stack[CF_CONN_NUM_POLLS_ON_STACK];
|
||||
struct easy_pollset ps;
|
||||
struct curl_pollfds cpfds;
|
||||
struct Curl_cfilter *cf;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(data->conn);
|
||||
if(!CONN_SOCK_IDX_VALID(sockindex))
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
|
||||
if(data->conn->scheme->flags & PROTOPT_NONETWORK) {
|
||||
*done = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
cf = data->conn->cfilter[sockindex];
|
||||
if(!cf) {
|
||||
*done = FALSE;
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
|
||||
*done = (bool)cf->connected;
|
||||
if(*done)
|
||||
return CURLE_OK;
|
||||
|
||||
Curl_pollset_init(&ps);
|
||||
Curl_pollfds_init(&cpfds, a_few_on_stack, CF_CONN_NUM_POLLS_ON_STACK);
|
||||
while(!*done) {
|
||||
if(Curl_conn_needs_flush(data, sockindex)) {
|
||||
DEBUGF(infof(data, "Curl_conn_connect(index=%d), flush", sockindex));
|
||||
result = Curl_conn_flush(data, sockindex);
|
||||
if(result && (result != CURLE_AGAIN))
|
||||
return result;
|
||||
}
|
||||
|
||||
result = cf->cft->do_connect(cf, data, done);
|
||||
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=%d) -> %d, done=%d",
|
||||
blocking, result, *done);
|
||||
if(!result && *done) {
|
||||
/* Now that the complete filter chain is connected, let all filters
|
||||
* persist information at the connection. E.g. cf-socket sets the
|
||||
* socket and ip related information. */
|
||||
cf_cntrl_update_info(data, data->conn);
|
||||
conn_report_connect_stats(cf, data);
|
||||
data->conn->keepalive = *Curl_pgrs_now(data);
|
||||
VERBOSE(result = cf_verboseconnect(data, cf));
|
||||
VERBOSE(conn_trc_filters(data, sockindex, "connected"));
|
||||
conn_remove_setup_filters(data, sockindex);
|
||||
VERBOSE(conn_trc_filters(data, sockindex, "reduced to"));
|
||||
goto out;
|
||||
}
|
||||
else if(result) {
|
||||
CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d", result);
|
||||
VERBOSE(conn_trc_filters(data, sockindex, "failed to connect"));
|
||||
conn_report_connect_stats(cf, data);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(!blocking)
|
||||
goto out;
|
||||
else {
|
||||
/* check allowed time left */
|
||||
const timediff_t timeout_ms = Curl_timeleft_ms(data);
|
||||
curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
|
||||
int rc;
|
||||
|
||||
if(timeout_ms < 0) {
|
||||
/* no need to continue if time already is up */
|
||||
failf(data, "connect timeout");
|
||||
result = CURLE_OPERATION_TIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), do poll");
|
||||
Curl_pollset_reset(&ps);
|
||||
Curl_pollfds_reset(&cpfds);
|
||||
/* In general, we want to send after connect, wait on that. */
|
||||
if(sockfd != CURL_SOCKET_BAD)
|
||||
result = Curl_pollset_set_out_only(data, &ps, sockfd);
|
||||
if(!result)
|
||||
result = Curl_conn_adjust_pollset(data, data->conn, &ps);
|
||||
if(result)
|
||||
goto out;
|
||||
result = Curl_pollfds_add_ps(&cpfds, &ps);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
rc = Curl_poll(cpfds.pfds, cpfds.n,
|
||||
CURLMIN(timeout_ms, (cpfds.n ? 1000 : 10)));
|
||||
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), Curl_poll() -> %d",
|
||||
rc);
|
||||
if(rc < 0) {
|
||||
result = CURLE_COULDNT_CONNECT;
|
||||
goto out;
|
||||
}
|
||||
/* continue iterating */
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
Curl_pollset_cleanup(&ps);
|
||||
Curl_pollfds_cleanup(&cpfds);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Curl_conn_is_setup(struct connectdata *conn, int sockindex)
|
||||
{
|
||||
if(!CONN_SOCK_IDX_VALID(sockindex))
|
||||
return FALSE;
|
||||
return (conn->cfilter[sockindex] != NULL);
|
||||
return !!conn->cfilter[sockindex];
|
||||
}
|
||||
|
||||
bool Curl_conn_is_connected(struct connectdata *conn, int sockindex)
|
||||
@@ -688,10 +523,37 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
static bool cf_is_tunneling(struct Curl_cfilter *cf)
|
||||
{
|
||||
for(; cf; cf = cf->next) {
|
||||
if((cf->cft->flags & CF_TYPE_PROXY))
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex)
|
||||
{
|
||||
if(!CONN_SOCK_IDX_VALID(sockindex))
|
||||
return FALSE;
|
||||
return conn ? cf_is_tunneling(conn->cfilter[sockindex]) : FALSE;
|
||||
}
|
||||
#else
|
||||
bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex)
|
||||
{
|
||||
(void)conn;
|
||||
(void)sockindex;
|
||||
return FALSE;
|
||||
}
|
||||
#endif /* CURL_DISABLE_PROXY */
|
||||
|
||||
static bool cf_is_ssl(struct Curl_cfilter *cf)
|
||||
{
|
||||
for(; cf; cf = cf->next) {
|
||||
if(cf->cft->flags & CF_TYPE_SSL)
|
||||
/* A tunneling proxy does not offer end2end encryption, even if
|
||||
* it does SSL itself (e.g. QUIC H3 proxy) */
|
||||
if((cf->cft->flags & CF_TYPE_SSL) && !(cf->cft->flags & CF_TYPE_PROXY))
|
||||
return TRUE;
|
||||
if(cf->cft->flags & CF_TYPE_IP_CONNECT)
|
||||
return FALSE;
|
||||
@@ -890,8 +752,19 @@ CURLcode Curl_conn_adjust_pollset(struct Curl_easy *data,
|
||||
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(conn);
|
||||
for(i = 0; (i < 2) && !result; ++i) {
|
||||
result = Curl_conn_cf_adjust_pollset(conn->cfilter[i], data, ps);
|
||||
/* During connect time, connection filters may add sockets to the pollset
|
||||
* even when the transfer neither wants to send nor receive. And those
|
||||
* sockets, when having events, are served.
|
||||
* Once connected however, a transfer that neither wants to send nor receive
|
||||
* will never call the connection filters. Any sockets added by the filters
|
||||
* will not change state and POLLIN/POLLOUT events will trigger forever,
|
||||
* making us busy loop. See #21671 */
|
||||
if(ps->n || !Curl_conn_is_connected(conn, FIRSTSOCKET) ||
|
||||
(conn->cfilter[SECONDARYSOCKET] &&
|
||||
!Curl_conn_is_connected(conn, SECONDARYSOCKET))) {
|
||||
for(i = 0; (i < 2) && !result && conn; ++i) {
|
||||
result = Curl_conn_cf_adjust_pollset(conn->cfilter[i], data, ps);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -953,8 +826,8 @@ void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex,
|
||||
&portarg, CURL_UNCONST(phost))) {
|
||||
/* Everything connected or query unsuccessful, the overall
|
||||
* connection's destination is the answer */
|
||||
*phost = data->conn->host.name;
|
||||
portarg = data->conn->remote_port;
|
||||
*phost = data->conn->origin->hostname;
|
||||
portarg = data->conn->origin->port;
|
||||
}
|
||||
if(pport)
|
||||
*pport = portarg;
|
||||
@@ -1018,8 +891,8 @@ const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct Curl_sockaddr_ex *
|
||||
cf_get_remote_addr(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
static const struct Curl_sockaddr_ex *cf_get_remote_addr(
|
||||
struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
const struct Curl_sockaddr_ex *remote_addr = NULL;
|
||||
if(cf &&
|
||||
@@ -1057,8 +930,8 @@ curl_socket_t Curl_conn_get_first_socket(struct Curl_easy *data)
|
||||
return data->conn->sock[FIRSTSOCKET];
|
||||
}
|
||||
|
||||
const struct Curl_sockaddr_ex *
|
||||
Curl_conn_get_remote_addr(struct Curl_easy *data, int sockindex)
|
||||
const struct Curl_sockaddr_ex *Curl_conn_get_remote_addr(
|
||||
struct Curl_easy *data, int sockindex)
|
||||
{
|
||||
struct Curl_cfilter *cf =
|
||||
(data->conn && CONN_SOCK_IDX_VALID(sockindex)) ?
|
||||
|
||||
@@ -39,10 +39,6 @@ struct curl_tlssessioninfo;
|
||||
typedef void Curl_cft_destroy_this(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data);
|
||||
|
||||
/* Callback to close the connection immediately. */
|
||||
typedef void Curl_cft_close(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data);
|
||||
|
||||
/* Callback to close the connection filter gracefully, non-blocking.
|
||||
* Implementations MUST NOT chain calls to cf->next.
|
||||
*/
|
||||
@@ -218,7 +214,6 @@ struct Curl_cftype {
|
||||
int log_level; /* log level for such filters */
|
||||
Curl_cft_destroy_this *destroy; /* destroy resources of this cf */
|
||||
Curl_cft_connect *do_connect; /* establish connection */
|
||||
Curl_cft_close *do_close; /* close conn */
|
||||
Curl_cft_shutdown *do_shutdown; /* shutdown conn */
|
||||
Curl_cft_adjust_pollset *adjust_pollset; /* adjust transfer poll set */
|
||||
Curl_cft_data_pending *has_data_pending; /* conn has data pending */
|
||||
@@ -270,6 +265,8 @@ CURLcode Curl_cf_def_query(struct Curl_cfilter *cf,
|
||||
int query, int *pres1, void *pres2);
|
||||
CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data, bool *done);
|
||||
CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data, bool *done);
|
||||
|
||||
/**
|
||||
* Create a new filter instance, unattached to the filter chain.
|
||||
@@ -322,7 +319,6 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data,
|
||||
CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
bool *done);
|
||||
void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data);
|
||||
CURLcode Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
const uint8_t *buf, size_t len, bool eos,
|
||||
size_t *pnwritten);
|
||||
@@ -364,16 +360,6 @@ bool Curl_conn_cf_wants_httpsrr(struct Curl_cfilter *cf,
|
||||
#define CURL_CF_SSL_DISABLE 0
|
||||
#define CURL_CF_SSL_ENABLE 1
|
||||
|
||||
/**
|
||||
* Bring the filter chain at `sockindex` for connection `data->conn` into
|
||||
* connected state. Which will set `*done` to TRUE.
|
||||
* This can be called on an already connected chain with no side effects.
|
||||
* When not `blocking`, calls may return without error and `*done != TRUE`,
|
||||
* while the individual filters negotiated the connection.
|
||||
*/
|
||||
CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex,
|
||||
bool blocking, bool *done);
|
||||
|
||||
/**
|
||||
* Check if a filter chain at `sockindex` for connection `conn` exists.
|
||||
*/
|
||||
@@ -399,6 +385,10 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex);
|
||||
*/
|
||||
bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex);
|
||||
|
||||
/* Determine if the connection has one or more proxy filters.
|
||||
* e.g. is tunneling. */
|
||||
bool Curl_conn_is_tunneling(struct connectdata *conn, int sockindex);
|
||||
|
||||
/*
|
||||
* Fill `info` with information about the TLS instance securing the connection
|
||||
* when available, otherwise e.g. when Curl_conn_is_ssl() is FALSE, return
|
||||
@@ -433,11 +423,11 @@ unsigned char Curl_conn_get_transport(struct Curl_easy *data,
|
||||
const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data,
|
||||
struct connectdata *conn);
|
||||
|
||||
/**
|
||||
* Close the filter chain at `sockindex` for connection `data->conn`.
|
||||
* Filters remain in place and may be connected again afterwards.
|
||||
*/
|
||||
void Curl_conn_close(struct Curl_easy *data, int sockindex);
|
||||
void Curl_conn_cntrl_update_info(struct Curl_easy *data,
|
||||
struct connectdata *conn);
|
||||
|
||||
void Curl_conn_remove_setup_filters(struct Curl_easy *data,
|
||||
int sockindex);
|
||||
|
||||
/**
|
||||
* Shutdown the connection at `sockindex` non-blocking, using timeout
|
||||
@@ -582,7 +572,7 @@ CURLcode Curl_conn_keep_alive(struct Curl_easy *data,
|
||||
* Get the remote hostname and port that the connection is currently
|
||||
* talking to (or will talk to).
|
||||
* Once connected or before connect starts,
|
||||
* it is `conn->host.name` and `conn->remote_port`.
|
||||
* it is `conn->origin->hostname` and `conn->origin->port`.
|
||||
* During connect, when tunneling proxies are involved (http or socks),
|
||||
* it will be the name and port the proxy currently negotiates with.
|
||||
*/
|
||||
@@ -604,6 +594,11 @@ int Curl_conn_get_stream_error(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex);
|
||||
|
||||
#ifdef CURLVERBOSE
|
||||
void Curl_conn_trc_filters(struct Curl_easy *data,
|
||||
int sockindex, const char *info);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Get the index of the given socket in the connection's sockets.
|
||||
* Useful in calling `Curl_conn_send()/Curl_conn_recv()` with the
|
||||
@@ -664,7 +659,7 @@ struct cf_call_data {
|
||||
#define CF_DATA_SAVE(save, cf, data) \
|
||||
do { \
|
||||
(save) = CF_CTX_CALL_DATA(cf); \
|
||||
DEBUGASSERT((save).data == NULL || (save).depth > 0); \
|
||||
DEBUGASSERT(!(save).data || (save).depth > 0); \
|
||||
CF_CTX_CALL_DATA(cf).depth++; \
|
||||
CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)CURL_UNCONST(data); \
|
||||
} while(0)
|
||||
@@ -672,7 +667,7 @@ struct cf_call_data {
|
||||
#define CF_DATA_RESTORE(cf, save) \
|
||||
do { \
|
||||
DEBUGASSERT(CF_CTX_CALL_DATA(cf).depth == (save).depth + 1); \
|
||||
DEBUGASSERT((save).data == NULL || (save).depth > 0); \
|
||||
DEBUGASSERT(!(save).data || (save).depth > 0); \
|
||||
CF_CTX_CALL_DATA(cf) = (save); \
|
||||
} while(0)
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ void Curl_cpool_init(struct cpool *cpool,
|
||||
|
||||
cpool->idata = idata;
|
||||
cpool->share = share;
|
||||
cpool->initialised = TRUE;
|
||||
cpool->initialized = TRUE;
|
||||
}
|
||||
|
||||
/* Return the "first" connection in the pool or NULL. */
|
||||
@@ -230,7 +230,7 @@ static void cpool_discard_conn(struct cpool *cpool,
|
||||
|
||||
void Curl_cpool_destroy(struct cpool *cpool)
|
||||
{
|
||||
if(cpool && cpool->initialised && cpool->idata) {
|
||||
if(cpool && cpool->initialized && cpool->idata) {
|
||||
struct connectdata *conn;
|
||||
struct Curl_sigpipe_ctx pipe_ctx;
|
||||
|
||||
@@ -305,9 +305,9 @@ static struct cpool_bundle *cpool_add_bundle(struct cpool *cpool,
|
||||
return bundle;
|
||||
}
|
||||
|
||||
static struct connectdata *
|
||||
cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle,
|
||||
const struct curltime *pnow)
|
||||
static struct connectdata *cpool_bundle_get_oldest_idle(
|
||||
struct cpool_bundle *bundle,
|
||||
const struct curltime *pnow)
|
||||
{
|
||||
struct Curl_llist_node *curr;
|
||||
timediff_t highscore = -1;
|
||||
|
||||
@@ -56,7 +56,7 @@ struct cpool {
|
||||
struct Curl_easy *idata; /* internal handle for maintenance */
|
||||
struct Curl_share *share; /* != NULL if pool belongs to share */
|
||||
BIT(locked);
|
||||
BIT(initialised);
|
||||
BIT(initialized);
|
||||
};
|
||||
|
||||
/* Init the pool, pass multi only if pool is owned by it.
|
||||
|
||||
+201
-350
@@ -23,51 +23,20 @@
|
||||
***************************************************************************/
|
||||
#include "curl_setup.h"
|
||||
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h> /* <netinet/tcp.h> may need it */
|
||||
#endif
|
||||
#ifdef HAVE_SYS_UN_H
|
||||
#include <sys/un.h> /* for sockaddr_un */
|
||||
#endif
|
||||
#ifdef HAVE_LINUX_TCP_H
|
||||
#include <linux/tcp.h>
|
||||
#elif defined(HAVE_NETINET_TCP_H)
|
||||
#include <netinet/tcp.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
#include <sys/ioctl.h>
|
||||
#endif
|
||||
#ifdef HAVE_NETDB_H
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#ifdef __VMS
|
||||
#include <in.h>
|
||||
#include <inet.h>
|
||||
#endif
|
||||
|
||||
#include "urldata.h"
|
||||
#include "curl_trc.h"
|
||||
#include "strerror.h"
|
||||
#include "cfilters.h"
|
||||
#include "connect.h"
|
||||
#include "cf-dns.h"
|
||||
#include "cf-haproxy.h"
|
||||
#include "cf-https-connect.h"
|
||||
#include "cf-ip-happy.h"
|
||||
#include "cf-socket.h"
|
||||
#include "cf-setup.h"
|
||||
#include "multiif.h"
|
||||
#include "curlx/inet_ntop.h"
|
||||
#include "curlx/strparse.h"
|
||||
#include "vtls/vtls.h" /* for vtls cfilters */
|
||||
#include "progress.h"
|
||||
#include "conncache.h"
|
||||
#include "multihandle.h"
|
||||
#include "http_proxy.h"
|
||||
#include "socks.h"
|
||||
#include "select.h"
|
||||
#include "curlx/strparse.h"
|
||||
|
||||
#if !defined(CURL_DISABLE_ALTSVC) || defined(USE_HTTPSRR)
|
||||
|
||||
@@ -210,59 +179,6 @@ bool Curl_shutdown_started(struct Curl_easy *data, int sockindex)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* retrieves ip address and port from a sockaddr structure. note it calls
|
||||
curlx_inet_ntop which sets errno on fail, not SOCKERRNO. */
|
||||
bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
|
||||
char *addr, uint16_t *port)
|
||||
{
|
||||
struct sockaddr_in *si = NULL;
|
||||
#ifdef USE_IPV6
|
||||
struct sockaddr_in6 *si6 = NULL;
|
||||
#endif
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
struct sockaddr_un *su = NULL;
|
||||
#else
|
||||
(void)salen;
|
||||
#endif
|
||||
|
||||
switch(sa->sa_family) {
|
||||
case AF_INET:
|
||||
si = (struct sockaddr_in *)(void *)sa;
|
||||
if(curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) {
|
||||
*port = ntohs(si->sin_port);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
#ifdef USE_IPV6
|
||||
case AF_INET6:
|
||||
si6 = (struct sockaddr_in6 *)(void *)sa;
|
||||
if(curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr, MAX_IPADR_LEN)) {
|
||||
*port = ntohs(si6->sin6_port);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
case AF_UNIX:
|
||||
if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) {
|
||||
su = (struct sockaddr_un *)sa;
|
||||
curl_msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
|
||||
}
|
||||
else
|
||||
addr[0] = 0; /* socket with no name */
|
||||
*port = 0;
|
||||
return TRUE;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
addr[0] = '\0';
|
||||
*port = 0;
|
||||
errno = SOCKEAFNOSUPPORT;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Used to extract socket and connectdata struct for the most recent
|
||||
* transfer on the given Curl_easy.
|
||||
@@ -325,267 +241,22 @@ void Curl_conncontrol(struct connectdata *conn,
|
||||
}
|
||||
}
|
||||
|
||||
typedef enum {
|
||||
CF_SETUP_INIT,
|
||||
CF_SETUP_CNNCT_EYEBALLS,
|
||||
CF_SETUP_CNNCT_SOCKS,
|
||||
CF_SETUP_CNNCT_HTTP_PROXY,
|
||||
CF_SETUP_CNNCT_HAPROXY,
|
||||
CF_SETUP_CNNCT_SSL,
|
||||
CF_SETUP_DONE
|
||||
} cf_setup_state;
|
||||
|
||||
struct cf_setup_ctx {
|
||||
cf_setup_state state;
|
||||
int ssl_mode;
|
||||
uint8_t transport;
|
||||
};
|
||||
|
||||
static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data,
|
||||
bool *done)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(cf->connected) {
|
||||
*done = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* connect current sub-chain */
|
||||
connect_sub_chain:
|
||||
|
||||
if(cf->next && !cf->next->connected) {
|
||||
result = Curl_conn_cf_connect(cf->next, data, done);
|
||||
if(result || !*done)
|
||||
return result;
|
||||
}
|
||||
|
||||
if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
|
||||
result = cf_ip_happy_insert_after(cf, data, ctx->transport);
|
||||
if(result)
|
||||
return result;
|
||||
ctx->state = CF_SETUP_CNNCT_EYEBALLS;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
}
|
||||
|
||||
/* sub-chain connected, do we need to add more? */
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) {
|
||||
/* for the secondary socket (FTP), use the "connect to host"
|
||||
* but ignore the "connect to port" (use the secondary port)
|
||||
*/
|
||||
const char *hostname =
|
||||
cf->conn->bits.httpproxy ?
|
||||
cf->conn->http_proxy.host.name :
|
||||
cf->conn->bits.conn_to_host ?
|
||||
cf->conn->conn_to_host.name :
|
||||
cf->sockindex == SECONDARYSOCKET ?
|
||||
cf->conn->secondaryhostname : cf->conn->host.name;
|
||||
uint16_t port =
|
||||
cf->conn->bits.httpproxy ? cf->conn->http_proxy.port :
|
||||
cf->sockindex == SECONDARYSOCKET ? cf->conn->secondary_port :
|
||||
cf->conn->bits.conn_to_port ? cf->conn->conn_to_port :
|
||||
cf->conn->remote_port;
|
||||
const char *user = cf->conn->socks_proxy.user;
|
||||
const char *passwd = cf->conn->socks_proxy.passwd;
|
||||
|
||||
result = Curl_cf_socks_proxy_insert_after(
|
||||
cf, data, hostname, port, cf->conn->ip_version,
|
||||
cf->conn->socks_proxy.proxytype, user, passwd);
|
||||
if(result)
|
||||
return result;
|
||||
ctx->state = CF_SETUP_CNNCT_SOCKS;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
}
|
||||
|
||||
if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) {
|
||||
#ifdef USE_SSL
|
||||
if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype) &&
|
||||
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
|
||||
result = Curl_cf_ssl_proxy_insert_after(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
#endif /* USE_SSL */
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
if(cf->conn->bits.tunnel_proxy) {
|
||||
result = Curl_cf_http_proxy_insert_after(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
#endif /* !CURL_DISABLE_HTTP */
|
||||
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
}
|
||||
#endif /* !CURL_DISABLE_PROXY */
|
||||
|
||||
if(ctx->state < CF_SETUP_CNNCT_HAPROXY) {
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(data->set.haproxyprotocol) {
|
||||
if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
|
||||
failf(data, "haproxy protocol not supported with SSL "
|
||||
"encryption in place (QUIC?)");
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
}
|
||||
result = Curl_cf_haproxy_insert_after(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
#endif /* !CURL_DISABLE_PROXY */
|
||||
ctx->state = CF_SETUP_CNNCT_HAPROXY;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
}
|
||||
|
||||
if(ctx->state < CF_SETUP_CNNCT_SSL) {
|
||||
#ifdef USE_SSL
|
||||
if((ctx->ssl_mode == CURL_CF_SSL_ENABLE ||
|
||||
(ctx->ssl_mode != CURL_CF_SSL_DISABLE &&
|
||||
cf->conn->scheme->flags & PROTOPT_SSL)) && /* we want SSL */
|
||||
!Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
|
||||
result = Curl_cf_ssl_insert_after(cf, data);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
#endif /* USE_SSL */
|
||||
ctx->state = CF_SETUP_CNNCT_SSL;
|
||||
if(!cf->next || !cf->next->connected)
|
||||
goto connect_sub_chain;
|
||||
}
|
||||
|
||||
ctx->state = CF_SETUP_DONE;
|
||||
cf->connected = TRUE;
|
||||
*done = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void cf_setup_close(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
|
||||
CURL_TRC_CF(data, cf, "close");
|
||||
cf->connected = FALSE;
|
||||
ctx->state = CF_SETUP_INIT;
|
||||
|
||||
if(cf->next) {
|
||||
cf->next->cft->do_close(cf->next, data);
|
||||
Curl_conn_cf_discard_chain(&cf->next, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
struct cf_setup_ctx *ctx = cf->ctx;
|
||||
|
||||
CURL_TRC_CF(data, cf, "destroy");
|
||||
curlx_safefree(ctx);
|
||||
}
|
||||
|
||||
struct Curl_cftype Curl_cft_setup = {
|
||||
"SETUP",
|
||||
CF_TYPE_SETUP,
|
||||
CURL_LOG_LVL_NONE,
|
||||
cf_setup_destroy,
|
||||
cf_setup_connect,
|
||||
cf_setup_close,
|
||||
Curl_cf_def_shutdown,
|
||||
Curl_cf_def_adjust_pollset,
|
||||
Curl_cf_def_data_pending,
|
||||
Curl_cf_def_send,
|
||||
Curl_cf_def_recv,
|
||||
Curl_cf_def_cntrl,
|
||||
Curl_cf_def_conn_is_alive,
|
||||
Curl_cf_def_conn_keep_alive,
|
||||
Curl_cf_def_query,
|
||||
};
|
||||
|
||||
static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
|
||||
struct Curl_easy *data,
|
||||
uint8_t transport,
|
||||
int ssl_mode)
|
||||
{
|
||||
struct Curl_cfilter *cf = NULL;
|
||||
struct cf_setup_ctx *ctx;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
(void)data;
|
||||
ctx = curlx_calloc(1, sizeof(*ctx));
|
||||
if(!ctx) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
ctx->state = CF_SETUP_INIT;
|
||||
ctx->ssl_mode = ssl_mode;
|
||||
ctx->transport = transport;
|
||||
|
||||
result = Curl_cf_create(&cf, &Curl_cft_setup, ctx);
|
||||
if(result)
|
||||
goto out;
|
||||
ctx = NULL;
|
||||
|
||||
out:
|
||||
*pcf = result ? NULL : cf;
|
||||
if(ctx) {
|
||||
curlx_free(ctx);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode cf_setup_add(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex,
|
||||
uint8_t transport,
|
||||
int ssl_mode)
|
||||
{
|
||||
struct Curl_cfilter *cf;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
result = cf_setup_create(&cf, data, transport, ssl_mode);
|
||||
if(result)
|
||||
goto out;
|
||||
Curl_conn_cf_add(data, conn, sockindex, cf);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
|
||||
struct Curl_easy *data,
|
||||
uint8_t transport,
|
||||
int ssl_mode)
|
||||
{
|
||||
struct Curl_cfilter *cf;
|
||||
CURLcode result;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
result = cf_setup_create(&cf, data, transport, ssl_mode);
|
||||
if(result)
|
||||
goto out;
|
||||
Curl_conn_cf_insert_after(cf_at, cf);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_conn_setup(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex,
|
||||
struct Curl_dns_entry *dns,
|
||||
int ssl_mode)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct Curl_peer *peer = Curl_conn_get_first_peer(conn, sockindex);
|
||||
uint8_t dns_queries;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(conn->scheme);
|
||||
DEBUGASSERT(!conn->cfilter[sockindex]);
|
||||
|
||||
if(!peer)
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
if(!conn->cfilter[sockindex] &&
|
||||
conn->scheme->protocol == CURLPROTO_HTTPS) {
|
||||
@@ -598,8 +269,8 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
|
||||
|
||||
/* Still no cfilter set, apply default. */
|
||||
if(!conn->cfilter[sockindex]) {
|
||||
result = cf_setup_add(data, conn, sockindex,
|
||||
conn->transport_wanted, ssl_mode);
|
||||
result = Curl_cf_setup_add(data, conn, sockindex,
|
||||
conn->transport_wanted, ssl_mode);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
@@ -609,28 +280,179 @@ CURLcode Curl_conn_setup(struct Curl_easy *data,
|
||||
if(sockindex == FIRSTSOCKET)
|
||||
dns_queries |= CURL_DNSQ_HTTPS;
|
||||
#endif
|
||||
result = Curl_cf_dns_add(data, conn, sockindex, dns_queries,
|
||||
conn->transport_wanted, dns);
|
||||
result = Curl_cf_dns_add(data, conn, sockindex, peer, dns_queries,
|
||||
conn->transport_wanted);
|
||||
DEBUGASSERT(conn->cfilter[sockindex]);
|
||||
out:
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
const char *Curl_conn_get_unix_path(struct connectdata *conn)
|
||||
#ifdef CURLVERBOSE
|
||||
static CURLcode conn_connect_trace(struct Curl_easy *data,
|
||||
struct Curl_cfilter *cf)
|
||||
{
|
||||
const char *unix_path = conn->unix_domain_socket;
|
||||
if(Curl_trc_is_verbose(data)) {
|
||||
struct ip_quadruple ipquad;
|
||||
bool is_ipv6;
|
||||
CURLcode result;
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(!unix_path && conn->bits.proxy && conn->socks_proxy.host.name &&
|
||||
!strncmp(UNIX_SOCKET_PREFIX "/",
|
||||
conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
|
||||
unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
|
||||
result = Curl_conn_cf_get_ip_info(cf, data, &is_ipv6, &ipquad);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
infof(data, "Established %sconnection to %s (%s port %u) from %s port %u ",
|
||||
(cf->sockindex == SECONDARYSOCKET) ? "2nd " : "",
|
||||
CURL_CONN_HOST_DISPNAME(data->conn),
|
||||
ipquad.remote_ip, ipquad.remote_port,
|
||||
ipquad.local_ip, ipquad.local_port);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
return unix_path;
|
||||
/**
|
||||
* Update connection statistics
|
||||
*/
|
||||
static void conn_report_connect_stats(struct Curl_cfilter *cf,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
if(cf) {
|
||||
struct curltime connected;
|
||||
struct curltime appconnected;
|
||||
|
||||
memset(&connected, 0, sizeof(connected));
|
||||
cf->cft->query(cf, data, CF_QUERY_TIMER_CONNECT, NULL, &connected);
|
||||
if(connected.tv_sec || connected.tv_usec)
|
||||
Curl_pgrsTimeWas(data, TIMER_CONNECT, connected);
|
||||
|
||||
memset(&appconnected, 0, sizeof(appconnected));
|
||||
cf->cft->query(cf, data, CF_QUERY_TIMER_APPCONNECT, NULL, &appconnected);
|
||||
if(appconnected.tv_sec || appconnected.tv_usec)
|
||||
Curl_pgrsTimeWas(data, TIMER_APPCONNECT, appconnected);
|
||||
}
|
||||
}
|
||||
|
||||
CURLcode Curl_conn_connect(struct Curl_easy *data,
|
||||
int sockindex,
|
||||
bool blocking,
|
||||
bool *done)
|
||||
{
|
||||
#define CF_CONN_NUM_POLLS_ON_STACK 5
|
||||
struct pollfd a_few_on_stack[CF_CONN_NUM_POLLS_ON_STACK];
|
||||
struct easy_pollset ps;
|
||||
struct curl_pollfds cpfds;
|
||||
struct Curl_cfilter *cf;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
DEBUGASSERT(data->conn);
|
||||
if(!CONN_SOCK_IDX_VALID(sockindex))
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
|
||||
if(data->conn->scheme->flags & PROTOPT_NONETWORK) {
|
||||
*done = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
cf = data->conn->cfilter[sockindex];
|
||||
if(!cf) {
|
||||
*done = FALSE;
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
|
||||
*done = (bool)cf->connected;
|
||||
if(*done)
|
||||
return CURLE_OK;
|
||||
|
||||
Curl_pollset_init(&ps);
|
||||
Curl_pollfds_init(&cpfds, a_few_on_stack, CF_CONN_NUM_POLLS_ON_STACK);
|
||||
while(!*done) {
|
||||
if(Curl_conn_needs_flush(data, sockindex)) {
|
||||
DEBUGF(infof(data, "Curl_conn_connect(index=%d), flush", sockindex));
|
||||
result = Curl_conn_flush(data, sockindex);
|
||||
if(result && (result != CURLE_AGAIN))
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = cf->cft->do_connect(cf, data, done);
|
||||
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=%d) -> %d, done=%d",
|
||||
blocking, (int)result, *done);
|
||||
if(!result && *done) {
|
||||
/* A final sanity check on connection security */
|
||||
if((data->state.origin->scheme->flags & PROTOPT_SSL) &&
|
||||
(sockindex == FIRSTSOCKET) &&
|
||||
!Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) {
|
||||
DEBUGASSERT(0);
|
||||
failf(data, "transfer requires SSL, but not connected via SSL");
|
||||
result = CURLE_FAILED_INIT;
|
||||
goto out;
|
||||
}
|
||||
/* Now that the complete filter chain is connected, let all filters
|
||||
* persist information at the connection. E.g. cf-socket sets the
|
||||
* socket and ip related information. */
|
||||
Curl_conn_cntrl_update_info(data, data->conn);
|
||||
conn_report_connect_stats(cf, data);
|
||||
data->conn->keepalive = *Curl_pgrs_now(data);
|
||||
VERBOSE(result = conn_connect_trace(data, cf));
|
||||
VERBOSE(Curl_conn_trc_filters(data, sockindex, "connected"));
|
||||
Curl_conn_remove_setup_filters(data, sockindex);
|
||||
VERBOSE(Curl_conn_trc_filters(data, sockindex, "reduced to"));
|
||||
goto out;
|
||||
}
|
||||
else if(result) {
|
||||
CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d",
|
||||
(int)result);
|
||||
VERBOSE(Curl_conn_trc_filters(data, sockindex, "failed to connect"));
|
||||
conn_report_connect_stats(cf, data);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(!blocking)
|
||||
goto out;
|
||||
else {
|
||||
/* check allowed time left */
|
||||
const timediff_t timeout_ms = Curl_timeleft_ms(data);
|
||||
curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
|
||||
int rc;
|
||||
|
||||
if(timeout_ms < 0) {
|
||||
/* no need to continue if time already is up */
|
||||
failf(data, "connect timeout");
|
||||
result = CURLE_OPERATION_TIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), do poll");
|
||||
Curl_pollset_reset(&ps);
|
||||
Curl_pollfds_reset(&cpfds);
|
||||
/* In general, we want to send after connect, wait on that. */
|
||||
if(sockfd != CURL_SOCKET_BAD)
|
||||
result = Curl_pollset_set_out_only(data, &ps, sockfd);
|
||||
if(!result)
|
||||
result = Curl_conn_adjust_pollset(data, data->conn, &ps);
|
||||
if(result)
|
||||
goto out;
|
||||
result = Curl_pollfds_add_ps(&cpfds, &ps);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
rc = Curl_poll(cpfds.pfds, cpfds.n,
|
||||
CURLMIN(timeout_ms, (cpfds.n ? 1000 : 10)));
|
||||
CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), Curl_poll() -> %d",
|
||||
rc);
|
||||
if(rc < 0) {
|
||||
result = CURLE_COULDNT_CONNECT;
|
||||
goto out;
|
||||
}
|
||||
/* continue iterating */
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
Curl_pollset_cleanup(&ps);
|
||||
Curl_pollfds_cleanup(&cpfds);
|
||||
return result;
|
||||
}
|
||||
#endif /* USE_UNIX_SOCKETS */
|
||||
|
||||
void Curl_conn_set_multiplex(struct connectdata *conn)
|
||||
{
|
||||
@@ -641,3 +463,32 @@ void Curl_conn_set_multiplex(struct connectdata *conn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Curl_peer *Curl_conn_get_origin(struct connectdata *conn,
|
||||
int sockindex)
|
||||
{
|
||||
return (sockindex == SECONDARYSOCKET) ?
|
||||
conn->origin2 : conn->origin;
|
||||
}
|
||||
|
||||
struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn,
|
||||
int sockindex)
|
||||
{
|
||||
return (sockindex == SECONDARYSOCKET) ?
|
||||
(conn->via_peer2 ? conn->via_peer2 : conn->origin2) :
|
||||
(conn->via_peer ? conn->via_peer : conn->origin);
|
||||
}
|
||||
|
||||
struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn,
|
||||
int sockindex)
|
||||
{
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(conn->socks_proxy.peer)
|
||||
return conn->socks_proxy.peer;
|
||||
if(conn->http_proxy.peer)
|
||||
return conn->http_proxy.peer;
|
||||
#endif
|
||||
return (sockindex == SECONDARYSOCKET) ?
|
||||
(conn->via_peer2 ? conn->via_peer2 : conn->origin2) :
|
||||
(conn->via_peer ? conn->via_peer : conn->origin);
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@
|
||||
#include "hostip.h"
|
||||
#include "curlx/timeval.h"
|
||||
|
||||
struct Curl_dns_entry;
|
||||
struct ip_quadruple;
|
||||
struct Curl_peer;
|
||||
struct Curl_str;
|
||||
|
||||
enum alpnid Curl_alpn2alpnid(const unsigned char *name, size_t len);
|
||||
@@ -71,9 +70,6 @@ bool Curl_shutdown_started(struct Curl_easy *data, int sockindex);
|
||||
curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
|
||||
struct connectdata **connp);
|
||||
|
||||
bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
|
||||
char *addr, uint16_t *port);
|
||||
|
||||
/*
|
||||
* Curl_conncontrol() marks the end of a connection/stream. The 'ctrl'
|
||||
* argument specifies if it is the end of a connection or a stream.
|
||||
@@ -86,9 +82,9 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
|
||||
* when the connection will close.
|
||||
*/
|
||||
|
||||
#define CONNCTRL_KEEP 0 /* undo a marked closure */
|
||||
#define CONNCTRL_KEEP 0 /* undo a marked closure */
|
||||
#define CONNCTRL_CONNECTION 1
|
||||
#define CONNCTRL_STREAM 2
|
||||
#define CONNCTRL_STREAM 2
|
||||
|
||||
void Curl_conncontrol(struct connectdata *conn,
|
||||
int ctrl
|
||||
@@ -99,42 +95,50 @@ void Curl_conncontrol(struct connectdata *conn,
|
||||
|
||||
#if defined(DEBUGBUILD) && defined(CURLVERBOSE)
|
||||
#define streamclose(x, y) Curl_conncontrol(x, CONNCTRL_STREAM, y)
|
||||
#define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION, y)
|
||||
#define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP, y)
|
||||
#define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION, y)
|
||||
#define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP, y)
|
||||
#else /* !DEBUGBUILD || !CURLVERBOSE */
|
||||
#define streamclose(x, y) Curl_conncontrol(x, CONNCTRL_STREAM)
|
||||
#define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION)
|
||||
#define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP)
|
||||
#define connclose(x, y) Curl_conncontrol(x, CONNCTRL_CONNECTION)
|
||||
#define connkeep(x, y) Curl_conncontrol(x, CONNCTRL_KEEP)
|
||||
#endif
|
||||
|
||||
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
|
||||
struct Curl_easy *data,
|
||||
uint8_t transport,
|
||||
int ssl_mode);
|
||||
|
||||
/**
|
||||
* Setup the cfilters at `sockindex` in connection `conn`.
|
||||
* If no filter chain is installed yet, inspects the configuration
|
||||
* in `data` and `conn? to install a suitable filter chain.
|
||||
* in `data` and `conn` to install a suitable filter chain.
|
||||
*/
|
||||
CURLcode Curl_conn_setup(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
int sockindex,
|
||||
struct Curl_dns_entry *dns,
|
||||
int ssl_mode);
|
||||
|
||||
/**
|
||||
* Bring the filter chain at `sockindex` for connection `data->conn` into
|
||||
* connected state. Which will set `*done` to TRUE.
|
||||
* This can be called on an already connected chain with no side effects.
|
||||
* When not `blocking`, calls may return without error and `*done != TRUE`,
|
||||
* while the individual filters negotiated the connection.
|
||||
*/
|
||||
CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex,
|
||||
bool blocking, bool *done);
|
||||
|
||||
/* Set conn to allow multiplexing. */
|
||||
void Curl_conn_set_multiplex(struct connectdata *conn);
|
||||
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
#define UNIX_SOCKET_PREFIX "localhost"
|
||||
#endif
|
||||
const char *Curl_conn_get_unix_path(struct connectdata *conn);
|
||||
#else
|
||||
#define Curl_conn_get_unix_path(c) NULL
|
||||
#endif
|
||||
/* Get the origin peer at sockindex. */
|
||||
struct Curl_peer *Curl_conn_get_origin(struct connectdata *conn,
|
||||
int sockindex);
|
||||
|
||||
extern struct Curl_cftype Curl_cft_setup;
|
||||
/* Get the peer the connection actually connects to at sockindex.
|
||||
* Often the same as "origin", but can be redirected via "connect-to"
|
||||
* or "alt-svc". May tunnel through proxies. */
|
||||
struct Curl_peer *Curl_conn_get_destination(struct connectdata *conn,
|
||||
int sockindex);
|
||||
|
||||
/* Get the peer curl connects its socket to.
|
||||
* Can be origin, "connect-to" or the first proxy. */
|
||||
struct Curl_peer *Curl_conn_get_first_peer(struct connectdata *conn,
|
||||
int sockindex);
|
||||
|
||||
#endif /* HEADER_CURL_CONNECT_H */
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include <zstd.h>
|
||||
#endif
|
||||
|
||||
#include "connect.h"
|
||||
#include "sendf.h"
|
||||
#include "curl_trc.h"
|
||||
#include "content_encoding.h"
|
||||
@@ -88,7 +89,7 @@ static voidpf zalloc_cb(voidpf opaque, unsigned int items, unsigned int size)
|
||||
{
|
||||
(void)opaque;
|
||||
/* not a typo, keep it curlx_calloc() */
|
||||
return (voidpf)curlx_calloc(items, size);
|
||||
return curlx_calloc(items, size);
|
||||
}
|
||||
|
||||
static void zfree_cb(voidpf opaque, voidpf ptr)
|
||||
@@ -100,8 +101,7 @@ static void zfree_cb(voidpf opaque, voidpf ptr)
|
||||
static CURLcode process_zlib_error(struct Curl_easy *data, z_stream *z)
|
||||
{
|
||||
if(z->msg)
|
||||
failf(data, "Error while processing content unencoding: %s",
|
||||
z->msg);
|
||||
failf(data, "Error while processing content unencoding: %s", z->msg);
|
||||
else
|
||||
failf(data, "Error while processing content unencoding: "
|
||||
"Unknown failure within decompression software.");
|
||||
@@ -154,6 +154,7 @@ static CURLcode inflate_stream(struct Curl_easy *data,
|
||||
z_const Bytef *orig_in = z->next_in;
|
||||
bool done = FALSE;
|
||||
CURLcode result = CURLE_OK; /* Curl_client_write status */
|
||||
int i = 0;
|
||||
|
||||
/* Check state. */
|
||||
if(zp->zlib_init != ZLIB_INIT &&
|
||||
@@ -164,9 +165,18 @@ static CURLcode inflate_stream(struct Curl_easy *data,
|
||||
/* because the buffer size is fixed, iteratively decompress and transfer to
|
||||
the client via next_write function. */
|
||||
while(!done) {
|
||||
int status; /* zlib status */
|
||||
int status; /* zlib status */
|
||||
done = TRUE;
|
||||
|
||||
if(++i > (1024 * 1024 / DECOMPRESS_BUFFER_SIZE)) {
|
||||
/* check every MB of output if we are not exceeding time limit */
|
||||
i = 0;
|
||||
if(Curl_timeleft_ms(data) < 0) {
|
||||
failf(data, "Operation timed out while decoding payload");
|
||||
return exit_zlib(data, z, &zp->zlib_init, CURLE_OPERATION_TIMEDOUT);
|
||||
}
|
||||
}
|
||||
|
||||
/* (re)set buffer for decompressed output for every iteration */
|
||||
z->next_out = (Bytef *)zp->buffer;
|
||||
z->avail_out = DECOMPRESS_BUFFER_SIZE;
|
||||
@@ -176,7 +186,7 @@ static CURLcode inflate_stream(struct Curl_easy *data,
|
||||
/* Flush output data if some. */
|
||||
if(z->avail_out != DECOMPRESS_BUFFER_SIZE) {
|
||||
if(status == Z_OK || status == Z_STREAM_END) {
|
||||
zp->zlib_init = started; /* Data started. */
|
||||
zp->zlib_init = started; /* Data started. */
|
||||
result = Curl_cwriter_write(data, writer->next, type, zp->buffer,
|
||||
DECOMPRESS_BUFFER_SIZE - z->avail_out);
|
||||
if(result) {
|
||||
@@ -210,7 +220,7 @@ static CURLcode inflate_stream(struct Curl_easy *data,
|
||||
done = FALSE;
|
||||
break;
|
||||
}
|
||||
zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */
|
||||
zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */
|
||||
}
|
||||
result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z));
|
||||
break;
|
||||
@@ -224,7 +234,7 @@ static CURLcode inflate_stream(struct Curl_easy *data,
|
||||
again. If we are in a state that would wrongly allow restart in raw mode
|
||||
at the next call, assume output has already started. */
|
||||
if(nread && zp->zlib_init == ZLIB_INIT)
|
||||
zp->zlib_init = started; /* Cannot restart anymore. */
|
||||
zp->zlib_init = started; /* Cannot restart anymore. */
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -234,7 +244,7 @@ static CURLcode deflate_do_init(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer)
|
||||
{
|
||||
struct zlib_writer *zp = (struct zlib_writer *)writer;
|
||||
z_stream *z = &zp->z; /* zlib state structure */
|
||||
z_stream *z = &zp->z; /* zlib state structure */
|
||||
|
||||
/* Initialize zlib */
|
||||
z->zalloc = (alloc_func)zalloc_cb;
|
||||
@@ -251,7 +261,7 @@ static CURLcode deflate_do_write(struct Curl_easy *data,
|
||||
const char *buf, size_t nbytes)
|
||||
{
|
||||
struct zlib_writer *zp = (struct zlib_writer *)writer;
|
||||
z_stream *z = &zp->z; /* zlib state structure */
|
||||
z_stream *z = &zp->z; /* zlib state structure */
|
||||
|
||||
if(!(type & CLIENTWRITE_BODY) || !nbytes)
|
||||
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
|
||||
@@ -271,7 +281,7 @@ static void deflate_do_close(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer)
|
||||
{
|
||||
struct zlib_writer *zp = (struct zlib_writer *)writer;
|
||||
z_stream *z = &zp->z; /* zlib state structure */
|
||||
z_stream *z = &zp->z; /* zlib state structure */
|
||||
|
||||
exit_zlib(data, z, &zp->zlib_init, CURLE_OK);
|
||||
}
|
||||
@@ -293,7 +303,7 @@ static CURLcode gzip_do_init(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer)
|
||||
{
|
||||
struct zlib_writer *zp = (struct zlib_writer *)writer;
|
||||
z_stream *z = &zp->z; /* zlib state structure */
|
||||
z_stream *z = &zp->z; /* zlib state structure */
|
||||
|
||||
/* Initialize zlib */
|
||||
z->zalloc = (alloc_func)zalloc_cb;
|
||||
@@ -311,7 +321,7 @@ static CURLcode gzip_do_write(struct Curl_easy *data,
|
||||
const char *buf, size_t nbytes)
|
||||
{
|
||||
struct zlib_writer *zp = (struct zlib_writer *)writer;
|
||||
z_stream *z = &zp->z; /* zlib state structure */
|
||||
z_stream *z = &zp->z; /* zlib state structure */
|
||||
|
||||
if(!(type & CLIENTWRITE_BODY) || !nbytes)
|
||||
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
|
||||
@@ -332,7 +342,7 @@ static void gzip_do_close(struct Curl_easy *data,
|
||||
struct Curl_cwriter *writer)
|
||||
{
|
||||
struct zlib_writer *zp = (struct zlib_writer *)writer;
|
||||
z_stream *z = &zp->z; /* zlib state structure */
|
||||
z_stream *z = &zp->z; /* zlib state structure */
|
||||
|
||||
exit_zlib(data, z, &zp->zlib_init, CURLE_OK);
|
||||
}
|
||||
@@ -353,7 +363,7 @@ static const struct Curl_cwtype gzip_encoding = {
|
||||
struct brotli_writer {
|
||||
struct Curl_cwriter super;
|
||||
char buffer[DECOMPRESS_BUFFER_SIZE];
|
||||
BrotliDecoderState *br; /* State structure for brotli. */
|
||||
BrotliDecoderState *br; /* State structure for brotli. */
|
||||
};
|
||||
|
||||
static CURLcode brotli_map_error(BrotliDecoderErrorCode be)
|
||||
@@ -412,15 +422,26 @@ static CURLcode brotli_do_write(struct Curl_easy *data,
|
||||
size_t dstleft;
|
||||
CURLcode result = CURLE_OK;
|
||||
BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
|
||||
int i = 0;
|
||||
|
||||
if(!(type & CLIENTWRITE_BODY) || !nbytes)
|
||||
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
|
||||
|
||||
if(!bp->br)
|
||||
return CURLE_WRITE_ERROR; /* Stream already ended. */
|
||||
return CURLE_WRITE_ERROR; /* Stream already ended. */
|
||||
|
||||
while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) &&
|
||||
result == CURLE_OK) {
|
||||
|
||||
if(++i > (1024 * 1024 / DECOMPRESS_BUFFER_SIZE)) {
|
||||
/* check every MB of output if we are not exceeding time limit */
|
||||
i = 0;
|
||||
if(Curl_timeleft_ms(data) < 0) {
|
||||
failf(data, "Operation timed out while decoding payload");
|
||||
return CURLE_OPERATION_TIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
dst = (uint8_t *)bp->buffer;
|
||||
dstleft = DECOMPRESS_BUFFER_SIZE;
|
||||
r = BrotliDecoderDecompressStream(bp->br,
|
||||
@@ -473,7 +494,7 @@ static const struct Curl_cwtype brotli_encoding = {
|
||||
/* Zstd writer. */
|
||||
struct zstd_writer {
|
||||
struct Curl_cwriter super;
|
||||
ZSTD_DStream *zds; /* State structure for zstd. */
|
||||
ZSTD_DStream *zds; /* State structure for zstd. */
|
||||
char buffer[DECOMPRESS_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
@@ -520,6 +541,7 @@ static CURLcode zstd_do_write(struct Curl_easy *data,
|
||||
ZSTD_inBuffer in;
|
||||
ZSTD_outBuffer out;
|
||||
size_t errorCode;
|
||||
int i = 0;
|
||||
|
||||
if(!(type & CLIENTWRITE_BODY) || !nbytes)
|
||||
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
|
||||
@@ -529,6 +551,15 @@ static CURLcode zstd_do_write(struct Curl_easy *data,
|
||||
in.size = nbytes;
|
||||
|
||||
for(;;) {
|
||||
if(++i > (1024 * 1024 / DECOMPRESS_BUFFER_SIZE)) {
|
||||
/* check every MB of output if we are not exceeding time limit */
|
||||
i = 0;
|
||||
if(Curl_timeleft_ms(data) < 0) {
|
||||
failf(data, "Operation timed out while decoding payload");
|
||||
return CURLE_OPERATION_TIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
out.pos = 0;
|
||||
out.dst = zp->buffer;
|
||||
out.size = DECOMPRESS_BUFFER_SIZE;
|
||||
@@ -750,24 +781,14 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) {
|
||||
failf(data, "Reject response due to more than %d content encodings",
|
||||
MAX_ENCODE_STACK);
|
||||
if(Curl_cwriter_count(data, phase) >= MAX_ENCODE_STACK) {
|
||||
failf(data, "Reject response exceeding limit of %d %s encodings",
|
||||
MAX_ENCODE_STACK,
|
||||
is_transfer ? "transfer" : "content");
|
||||
return CURLE_BAD_CONTENT_ENCODING;
|
||||
}
|
||||
|
||||
cwt = find_unencode_writer(name, namelen, phase);
|
||||
if(cwt && is_chunked && Curl_cwriter_get_by_type(data, cwt)) {
|
||||
/* A 'chunked' transfer encoding has already been added.
|
||||
* Ignore duplicates. See #13451.
|
||||
* Also RFC 9112, ch. 6.1:
|
||||
* "A sender MUST NOT apply the chunked transfer coding more than
|
||||
* once to a message body."
|
||||
*/
|
||||
CURL_TRC_WRITE(data, "ignoring duplicate 'chunked' decoder");
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
if(is_transfer && !is_chunked &&
|
||||
Curl_cwriter_get_by_name(data, "chunked")) {
|
||||
/* RFC 9112, ch. 6.1:
|
||||
@@ -782,20 +803,31 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
|
||||
"Transfer-Encoding");
|
||||
return CURLE_BAD_CONTENT_ENCODING;
|
||||
}
|
||||
if(cwt && is_chunked && Curl_cwriter_get_by_type(data, cwt)) {
|
||||
/* A 'chunked' transfer encoding has already been added.
|
||||
* Ignore duplicates. See #13451.
|
||||
* Also RFC 9112, ch. 6.1:
|
||||
* "A sender MUST NOT apply the chunked transfer coding more than
|
||||
* once to a message body."
|
||||
*/
|
||||
CURL_TRC_WRITE(data, "ignoring duplicate 'chunked' decoder");
|
||||
}
|
||||
else {
|
||||
if(!cwt)
|
||||
cwt = &error_writer; /* Defer error at use. */
|
||||
|
||||
if(!cwt)
|
||||
cwt = &error_writer; /* Defer error at use. */
|
||||
result = Curl_cwriter_create(&writer, data, cwt, phase);
|
||||
CURL_TRC_WRITE(data, "added %s decoder %s -> %d",
|
||||
is_transfer ? "transfer" : "content", cwt->name,
|
||||
(int)result);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = Curl_cwriter_create(&writer, data, cwt, phase);
|
||||
CURL_TRC_WRITE(data, "added %s decoder %s -> %d",
|
||||
is_transfer ? "transfer" : "content", cwt->name, result);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
result = Curl_cwriter_add(data, writer);
|
||||
if(result) {
|
||||
Curl_cwriter_free(data, writer);
|
||||
return result;
|
||||
result = Curl_cwriter_add(data, writer);
|
||||
if(result) {
|
||||
Curl_cwriter_free(data, writer);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
if(is_chunked)
|
||||
has_chunked = TRUE;
|
||||
|
||||
+250
-210
@@ -29,6 +29,7 @@
|
||||
#include "cookie.h"
|
||||
#include "psl.h"
|
||||
#include "curl_trc.h"
|
||||
#include "transfer.h"
|
||||
#include "slist.h"
|
||||
#include "curl_share.h"
|
||||
#include "strcase.h"
|
||||
@@ -71,7 +72,7 @@ static void freecookie(struct Cookie *co, bool maintoo)
|
||||
}
|
||||
|
||||
static bool cookie_tailmatch(const char *cookie_domain,
|
||||
size_t cookie_domain_len,
|
||||
const size_t cookie_domain_len,
|
||||
const char *hostname)
|
||||
{
|
||||
size_t hostname_len = strlen(hostname);
|
||||
@@ -250,19 +251,11 @@ static char *sanitize_cookie_path(const char *cookie_path, size_t len)
|
||||
/*
|
||||
* strstore
|
||||
*
|
||||
* A thin wrapper around strdup which ensures that any memory allocated at
|
||||
* *str will be freed before the string allocated by strdup is stored there.
|
||||
* The intended usecase is repeated assignments to the same variable during
|
||||
* parsing in a last-wins scenario. The caller is responsible for checking
|
||||
* for OOM errors.
|
||||
* A thin wrapper around curlx_memdup0().
|
||||
*/
|
||||
static CURLcode strstore(char **str, const char *newstr, size_t len)
|
||||
{
|
||||
DEBUGASSERT(str);
|
||||
if(!len) {
|
||||
len++;
|
||||
newstr = "";
|
||||
}
|
||||
*str = curlx_memdup0(newstr, len);
|
||||
if(!*str)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
@@ -354,9 +347,9 @@ static bool bad_domain(const char *domain, size_t len)
|
||||
static bool invalid_octets(const char *ptr, size_t len)
|
||||
{
|
||||
const unsigned char *p = (const unsigned char *)ptr;
|
||||
/* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */
|
||||
/* Reject all bytes \x01 - \x1f + \x7f */
|
||||
while(len && *p) {
|
||||
if(((*p != 9) && (*p < 0x20)) || (*p == 0x7f))
|
||||
if((*p < 0x20) || (*p == 0x7f))
|
||||
return TRUE;
|
||||
p++;
|
||||
len--;
|
||||
@@ -377,7 +370,7 @@ static bool invalid_octets(const char *ptr, size_t len)
|
||||
|
||||
#define COOKIE_PIECES 4 /* the list above */
|
||||
|
||||
static CURLcode storecookie(struct Cookie *co, struct Curl_str *cp,
|
||||
static CURLcode storecookie(struct Cookie *co, const struct Curl_str *cp,
|
||||
const char *path, const char *domain)
|
||||
{
|
||||
CURLcode result;
|
||||
@@ -421,19 +414,189 @@ static CURLcode storecookie(struct Cookie *co, struct Curl_str *cp,
|
||||
return result;
|
||||
}
|
||||
|
||||
/* this function return errors on OOM etc, not on plain cookie format
|
||||
problems */
|
||||
static CURLcode parse_cookie_header(
|
||||
struct Curl_easy *data,
|
||||
struct Cookie *co,
|
||||
struct CookieInfo *ci,
|
||||
bool *okay, /* if the cookie was fine */
|
||||
const char *ptr,
|
||||
const char *domain, /* default domain */
|
||||
const char *path, /* full path used when this cookie is
|
||||
set, used to get default path for
|
||||
the cookie unless set */
|
||||
bool secure) /* TRUE if connection is over secure origin */
|
||||
/*
|
||||
* Parse the first name/value pair of the cookie header, which is the actual
|
||||
* cookie name and value.
|
||||
*/
|
||||
static bool parse_first_pair(struct Curl_easy *data, struct Cookie *co,
|
||||
struct Curl_str *cookie,
|
||||
struct Curl_str *name,
|
||||
struct Curl_str *val,
|
||||
bool sep)
|
||||
{
|
||||
/* The first name/value pair is the actual cookie name */
|
||||
if(!sep || !curlx_strlen(name)) {
|
||||
infof(data, "invalid cookie, dropped");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for too long individual name or contents. Chrome and Firefox
|
||||
* support 4095 or 4096 bytes combo
|
||||
*/
|
||||
if((curlx_strlen(name) + curlx_strlen(val)) > MAX_NAME) {
|
||||
infof(data, "oversized cookie dropped, name/val %zu + %zu bytes",
|
||||
curlx_strlen(name), curlx_strlen(val));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Check if we have a reserved prefix set. */
|
||||
if(!strncmp("__Secure-", curlx_str(name), 9))
|
||||
co->prefix_secure = TRUE;
|
||||
else if(!strncmp("__Host-", curlx_str(name), 7))
|
||||
co->prefix_host = TRUE;
|
||||
|
||||
cookie[COOKIE_NAME] = *name;
|
||||
cookie[COOKIE_VALUE] = *val;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool parse_flag(struct Curl_easy *data, struct Cookie *co,
|
||||
const struct CookieInfo *ci,
|
||||
struct Curl_str *name, bool secure)
|
||||
{
|
||||
/*
|
||||
* secure cookies are only allowed to be set when the connection is
|
||||
* using a secure protocol, or when the cookie is being set by
|
||||
* reading from file
|
||||
*/
|
||||
if(curlx_str_casecompare(name, "secure")) {
|
||||
if(secure || !ci->running)
|
||||
co->secure = TRUE;
|
||||
else {
|
||||
infof(data, "skipped cookie because not 'secure'");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else if(curlx_str_casecompare(name, "httponly"))
|
||||
co->httponly = TRUE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool parse_domain(struct Curl_easy *data, struct Cookie *co,
|
||||
struct Curl_str *cookie_domain,
|
||||
struct Curl_str *val,
|
||||
const char **domainp)
|
||||
{
|
||||
bool is_ip;
|
||||
const char *domain = *domainp;
|
||||
const char *v = curlx_str(val);
|
||||
/*
|
||||
* Now, we make sure that our host is within the given domain, or
|
||||
* the given domain is not valid and thus cannot be set.
|
||||
*/
|
||||
|
||||
if('.' == *v)
|
||||
curlx_str_nudge(val, 1);
|
||||
|
||||
#ifndef USE_LIBPSL
|
||||
/*
|
||||
* Without PSL we do not know when the incoming cookie is set on a
|
||||
* TLD or otherwise "protected" suffix. To reduce risk, we require a
|
||||
* dot OR the exact hostname being "localhost".
|
||||
*/
|
||||
if(bad_domain(curlx_str(val), curlx_strlen(val))) {
|
||||
*domainp = ":";
|
||||
domain = ":";
|
||||
}
|
||||
#endif
|
||||
|
||||
is_ip = Curl_host_is_ipnum(domain ? domain : curlx_str(val));
|
||||
|
||||
if(!domain ||
|
||||
(is_ip &&
|
||||
!strncmp(curlx_str(val), domain, curlx_strlen(val)) &&
|
||||
(curlx_strlen(val) == strlen(domain))) ||
|
||||
(!is_ip && cookie_tailmatch(curlx_str(val),
|
||||
curlx_strlen(val), domain))) {
|
||||
*cookie_domain = *val;
|
||||
if(!is_ip)
|
||||
co->tailmatch = TRUE; /* we always do that if the domain name was
|
||||
given */
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* We did not get a tailmatch and then the attempted set domain is
|
||||
* not a domain to which the current host belongs. Mark as bad.
|
||||
*/
|
||||
infof(data, "skipped cookie with bad tailmatch domain: %s",
|
||||
curlx_str(val));
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void parse_maxage(struct Cookie *co, struct Curl_str *val,
|
||||
time_t *nowp)
|
||||
{
|
||||
int rc;
|
||||
const char *maxage = curlx_str(val);
|
||||
if(*maxage == '\"')
|
||||
maxage++;
|
||||
rc = curlx_str_number(&maxage, &co->expires, CURL_OFF_T_MAX);
|
||||
if(!*nowp)
|
||||
*nowp = time(NULL);
|
||||
switch(rc) {
|
||||
case STRE_OVERFLOW:
|
||||
/* overflow, used max value */
|
||||
co->expires = CURL_OFF_T_MAX;
|
||||
break;
|
||||
default:
|
||||
/* negative or otherwise bad, expire */
|
||||
co->expires = 1;
|
||||
break;
|
||||
case STRE_OK:
|
||||
if(!co->expires)
|
||||
co->expires = 1; /* expire now */
|
||||
else if(CURL_OFF_T_MAX - *nowp < co->expires)
|
||||
/* would overflow */
|
||||
co->expires = CURL_OFF_T_MAX;
|
||||
else
|
||||
co->expires += *nowp;
|
||||
break;
|
||||
}
|
||||
cap_expires(*nowp, co);
|
||||
}
|
||||
|
||||
static void parse_expires(struct Cookie *co, struct Curl_str *val,
|
||||
time_t *nowp)
|
||||
{
|
||||
/*
|
||||
* Let max-age have priority.
|
||||
*
|
||||
* If the date cannot get parsed for whatever reason, the cookie
|
||||
* will be treated as a session cookie
|
||||
*/
|
||||
if(!co->expires && (curlx_strlen(val) < MAX_DATE_LENGTH)) {
|
||||
char dbuf[MAX_DATE_LENGTH + 1];
|
||||
time_t date = 0;
|
||||
memcpy(dbuf, curlx_str(val), curlx_strlen(val));
|
||||
dbuf[curlx_strlen(val)] = 0;
|
||||
if(!Curl_getdate_capped(dbuf, &date)) {
|
||||
if(!date)
|
||||
date++;
|
||||
co->expires = (curl_off_t)date;
|
||||
}
|
||||
else
|
||||
co->expires = 0;
|
||||
if(!*nowp)
|
||||
*nowp = time(NULL);
|
||||
cap_expires(*nowp, co);
|
||||
}
|
||||
}
|
||||
|
||||
/* this function returns errors on OOM etc, not for cookie format problems */
|
||||
static CURLcode
|
||||
parse_cookie_header(struct Curl_easy *data,
|
||||
struct Cookie *co,
|
||||
const struct CookieInfo *ci,
|
||||
bool *okay, /* if the cookie was fine */
|
||||
const char *ptr, /* the header */
|
||||
const char *domain, /* default domain */
|
||||
/* full path used when this cookie is set */
|
||||
const char *path,
|
||||
bool secure_origin)
|
||||
{
|
||||
/* This line was read off an HTTP-header */
|
||||
time_t now = 0;
|
||||
@@ -449,22 +612,25 @@ static CURLcode parse_cookie_header(
|
||||
memset(cookie, 0, sizeof(cookie));
|
||||
do {
|
||||
struct Curl_str name;
|
||||
struct Curl_str val;
|
||||
|
||||
/* we have a <name>=<value> pair or a stand-alone word here */
|
||||
if(!curlx_str_cspn(&ptr, &name, ";\t\r\n=")) {
|
||||
struct Curl_str val;
|
||||
bool sep = FALSE;
|
||||
curlx_str_trimblanks(&name);
|
||||
|
||||
if(invalid_octets(curlx_str(&name), curlx_strlen(&name))) {
|
||||
infof(data, "invalid octets in name, cookie dropped");
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
if(!curlx_str_single(&ptr, '=')) {
|
||||
sep = TRUE; /* a '=' was used */
|
||||
if(!curlx_str_cspn(&ptr, &val, ";\r\n"))
|
||||
curlx_str_trimblanks(&val);
|
||||
|
||||
/* Reject cookies with a TAB inside the value */
|
||||
if(curlx_strlen(&val) &&
|
||||
memchr(curlx_str(&val), '\t', curlx_strlen(&val))) {
|
||||
infof(data, "cookie contains TAB, dropping");
|
||||
if(invalid_octets(curlx_str(&val), curlx_strlen(&val))) {
|
||||
infof(data, "invalid octets in value, cookie dropped");
|
||||
return CURLE_OK;
|
||||
}
|
||||
}
|
||||
@@ -472,167 +638,23 @@ static CURLcode parse_cookie_header(
|
||||
curlx_str_init(&val);
|
||||
|
||||
if(!curlx_strlen(&cookie[COOKIE_NAME])) {
|
||||
/* The first name/value pair is the actual cookie name */
|
||||
if(!sep ||
|
||||
/* Bad name/value pair. */
|
||||
invalid_octets(curlx_str(&name), curlx_strlen(&name)) ||
|
||||
invalid_octets(curlx_str(&val), curlx_strlen(&val)) ||
|
||||
!curlx_strlen(&name)) {
|
||||
infof(data, "invalid octets in name/value, cookie dropped");
|
||||
if(!parse_first_pair(data, co, cookie, &name, &val, sep))
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for too long individual name or contents, or too long
|
||||
* combination of name + contents. Chrome and Firefox support 4095 or
|
||||
* 4096 bytes combo
|
||||
*/
|
||||
if(curlx_strlen(&name) >= (MAX_NAME - 1) ||
|
||||
curlx_strlen(&val) >= (MAX_NAME - 1) ||
|
||||
((curlx_strlen(&name) + curlx_strlen(&val)) > MAX_NAME)) {
|
||||
infof(data, "oversized cookie dropped, name/val %zu + %zu bytes",
|
||||
curlx_strlen(&name), curlx_strlen(&val));
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* Check if we have a reserved prefix set. */
|
||||
if(!strncmp("__Secure-", curlx_str(&name), 9))
|
||||
co->prefix_secure = TRUE;
|
||||
else if(!strncmp("__Host-", curlx_str(&name), 7))
|
||||
co->prefix_host = TRUE;
|
||||
|
||||
cookie[COOKIE_NAME] = name;
|
||||
cookie[COOKIE_VALUE] = val;
|
||||
}
|
||||
else if(!sep) {
|
||||
/*
|
||||
* this is a "<name>" with no content
|
||||
*/
|
||||
|
||||
/*
|
||||
* secure cookies are only allowed to be set when the connection is
|
||||
* using a secure protocol, or when the cookie is being set by
|
||||
* reading from file
|
||||
*/
|
||||
if(curlx_str_casecompare(&name, "secure")) {
|
||||
if(secure || !ci->running)
|
||||
co->secure = TRUE;
|
||||
else {
|
||||
infof(data, "skipped cookie because not 'secure'");
|
||||
return CURLE_OK;
|
||||
}
|
||||
}
|
||||
else if(curlx_str_casecompare(&name, "httponly"))
|
||||
co->httponly = TRUE;
|
||||
}
|
||||
else if(curlx_str_casecompare(&name, "path")) {
|
||||
cookie[COOKIE_PATH] = val;
|
||||
}
|
||||
else if(curlx_str_casecompare(&name, "domain") && curlx_strlen(&val)) {
|
||||
bool is_ip;
|
||||
const char *v = curlx_str(&val);
|
||||
/*
|
||||
* Now, we make sure that our host is within the given domain, or
|
||||
* the given domain is not valid and thus cannot be set.
|
||||
*/
|
||||
|
||||
if('.' == *v)
|
||||
curlx_str_nudge(&val, 1);
|
||||
|
||||
#ifndef USE_LIBPSL
|
||||
/*
|
||||
* Without PSL we do not know when the incoming cookie is set on a
|
||||
* TLD or otherwise "protected" suffix. To reduce risk, we require a
|
||||
* dot OR the exact hostname being "localhost".
|
||||
*/
|
||||
if(bad_domain(curlx_str(&val), curlx_strlen(&val)))
|
||||
domain = ":";
|
||||
#endif
|
||||
|
||||
is_ip = Curl_host_is_ipnum(domain ? domain : curlx_str(&val));
|
||||
|
||||
if(!domain ||
|
||||
(is_ip &&
|
||||
!strncmp(curlx_str(&val), domain, curlx_strlen(&val)) &&
|
||||
(curlx_strlen(&val) == strlen(domain))) ||
|
||||
(!is_ip && cookie_tailmatch(curlx_str(&val),
|
||||
curlx_strlen(&val), domain))) {
|
||||
cookie[COOKIE_DOMAIN] = val;
|
||||
if(!is_ip)
|
||||
co->tailmatch = TRUE; /* we always do that if the domain name was
|
||||
given */
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* We did not get a tailmatch and then the attempted set domain is
|
||||
* not a domain to which the current host belongs. Mark as bad.
|
||||
*/
|
||||
infof(data, "skipped cookie with bad tailmatch domain: %s",
|
||||
curlx_str(&val));
|
||||
if(!parse_flag(data, co, ci, &name, secure_origin))
|
||||
return CURLE_OK;
|
||||
}
|
||||
}
|
||||
else if(curlx_str_casecompare(&name, "max-age") && curlx_strlen(&val)) {
|
||||
/*
|
||||
* Defined in RFC2109:
|
||||
*
|
||||
* Optional. The Max-Age attribute defines the lifetime of the
|
||||
* cookie, in seconds. The delta-seconds value is a decimal non-
|
||||
* negative integer. After delta-seconds seconds elapse, the
|
||||
* client should discard the cookie. A value of zero means the
|
||||
* cookie should be discarded immediately.
|
||||
*/
|
||||
int rc;
|
||||
const char *maxage = curlx_str(&val);
|
||||
if(*maxage == '\"')
|
||||
maxage++;
|
||||
rc = curlx_str_number(&maxage, &co->expires, CURL_OFF_T_MAX);
|
||||
if(!now)
|
||||
now = time(NULL);
|
||||
switch(rc) {
|
||||
case STRE_OVERFLOW:
|
||||
/* overflow, used max value */
|
||||
co->expires = CURL_OFF_T_MAX;
|
||||
break;
|
||||
default:
|
||||
/* negative or otherwise bad, expire */
|
||||
co->expires = 1;
|
||||
break;
|
||||
case STRE_OK:
|
||||
if(!co->expires)
|
||||
co->expires = 1; /* expire now */
|
||||
else if(CURL_OFF_T_MAX - now < co->expires)
|
||||
/* would overflow */
|
||||
co->expires = CURL_OFF_T_MAX;
|
||||
else
|
||||
co->expires += now;
|
||||
break;
|
||||
}
|
||||
cap_expires(now, co);
|
||||
}
|
||||
else if(curlx_str_casecompare(&name, "expires") && curlx_strlen(&val) &&
|
||||
!co->expires && (curlx_strlen(&val) < MAX_DATE_LENGTH)) {
|
||||
/*
|
||||
* Let max-age have priority.
|
||||
*
|
||||
* If the date cannot get parsed for whatever reason, the cookie
|
||||
* will be treated as a session cookie
|
||||
*/
|
||||
char dbuf[MAX_DATE_LENGTH + 1];
|
||||
time_t date = 0;
|
||||
memcpy(dbuf, curlx_str(&val), curlx_strlen(&val));
|
||||
dbuf[curlx_strlen(&val)] = 0;
|
||||
if(!Curl_getdate_capped(dbuf, &date)) {
|
||||
if(!date)
|
||||
date++;
|
||||
co->expires = (curl_off_t)date;
|
||||
}
|
||||
else
|
||||
co->expires = 0;
|
||||
if(!now)
|
||||
now = time(NULL);
|
||||
cap_expires(now, co);
|
||||
else if(curlx_str_casecompare(&name, "path"))
|
||||
cookie[COOKIE_PATH] = val;
|
||||
else if(curlx_str_casecompare(&name, "domain") && curlx_strlen(&val)) {
|
||||
if(!parse_domain(data, co, &cookie[COOKIE_DOMAIN], &val, &domain))
|
||||
return CURLE_OK;
|
||||
}
|
||||
else if(curlx_str_casecompare(&name, "max-age") && curlx_strlen(&val))
|
||||
parse_maxage(co, &val, &now);
|
||||
else if(curlx_str_casecompare(&name, "expires") && curlx_strlen(&val))
|
||||
parse_expires(co, &val, &now);
|
||||
}
|
||||
} while(!curlx_str_single(&ptr, ';'));
|
||||
|
||||
@@ -646,11 +668,10 @@ static CURLcode parse_cookie_header(
|
||||
}
|
||||
|
||||
static CURLcode parse_netscape(struct Cookie *co,
|
||||
struct CookieInfo *ci,
|
||||
const struct CookieInfo *ci,
|
||||
bool *okay,
|
||||
const char *lineptr,
|
||||
bool secure) /* TRUE if connection is over
|
||||
secure origin */
|
||||
bool secure_origin)
|
||||
{
|
||||
/*
|
||||
* This line is NOT an HTTP header style line, we do offer support for
|
||||
@@ -667,7 +688,7 @@ static CURLcode parse_netscape(struct Cookie *co,
|
||||
* Firefox's cookie files, they are prefixed #HttpOnly_ and the rest
|
||||
* remains as usual, so we skip 10 characters of the line.
|
||||
*/
|
||||
if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
|
||||
if(!strncmp(lineptr, "#HttpOnly_", 10)) {
|
||||
lineptr += 10;
|
||||
co->httponly = TRUE;
|
||||
}
|
||||
@@ -723,7 +744,7 @@ static CURLcode parse_netscape(struct Cookie *co,
|
||||
case 3:
|
||||
co->secure = FALSE;
|
||||
if(curl_strnequal(ptr, "TRUE", len)) {
|
||||
if(secure || ci->running)
|
||||
if(secure_origin || ci->running)
|
||||
co->secure = TRUE;
|
||||
else
|
||||
return CURLE_OK;
|
||||
@@ -738,10 +759,12 @@ static CURLcode parse_netscape(struct Cookie *co,
|
||||
if(!co->name)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
else {
|
||||
/* For Netscape file format cookies we check prefix on the name */
|
||||
if(curl_strnequal("__Secure-", co->name, 9))
|
||||
/* For Netscape file format cookies we check prefix on the name.
|
||||
These prefixes are matched case sensitively, same as on the
|
||||
header path and as the 6265bis document specifies. */
|
||||
if(!strncmp("__Secure-", co->name, 9))
|
||||
co->prefix_secure = TRUE;
|
||||
else if(curl_strnequal("__Host-", co->name, 7))
|
||||
else if(!strncmp("__Host-", co->name, 7))
|
||||
co->prefix_host = TRUE;
|
||||
}
|
||||
break;
|
||||
@@ -765,12 +788,20 @@ static CURLcode parse_netscape(struct Cookie *co,
|
||||
/* we did not find the sufficient number of fields */
|
||||
return CURLE_OK;
|
||||
|
||||
/* Reject control octets in the name or value, matching the filtering done
|
||||
for cookies set over HTTP. A cookie loaded from a file is later sent in
|
||||
request headers, so the same bytes that make a server reject a request
|
||||
must not slip in through the file. */
|
||||
if(invalid_octets(co->name, strlen(co->name)) ||
|
||||
invalid_octets(co->value, strlen(co->value)))
|
||||
return CURLE_OK;
|
||||
|
||||
*okay = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static bool is_public_suffix(struct Curl_easy *data,
|
||||
struct Cookie *co,
|
||||
const struct Cookie *co,
|
||||
const char *domain)
|
||||
{
|
||||
#ifdef USE_LIBPSL
|
||||
@@ -787,12 +818,21 @@ static bool is_public_suffix(struct Curl_easy *data,
|
||||
char lcookie[256];
|
||||
size_t dlen = strlen(domain);
|
||||
size_t clen = strlen(co->domain);
|
||||
|
||||
/* trim trailing dots */
|
||||
if(dlen && (domain[dlen - 1] == '.'))
|
||||
dlen--;
|
||||
if(clen && (co->domain[clen - 1] == '.'))
|
||||
clen--;
|
||||
|
||||
if((dlen < sizeof(lcase)) && (clen < sizeof(lcookie))) {
|
||||
const psl_ctx_t *psl = Curl_psl_use(data);
|
||||
if(psl) {
|
||||
/* the PSL check requires lowercase domain name and pattern */
|
||||
Curl_strntolower(lcase, domain, dlen + 1);
|
||||
Curl_strntolower(lcookie, co->domain, clen + 1);
|
||||
Curl_strntolower(lcase, domain, dlen);
|
||||
lcase[dlen] = 0;
|
||||
Curl_strntolower(lcookie, co->domain, clen);
|
||||
lcookie[clen] = 0;
|
||||
acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie);
|
||||
Curl_psl_release(data);
|
||||
}
|
||||
@@ -819,7 +859,7 @@ static bool is_public_suffix(struct Curl_easy *data,
|
||||
/* returns TRUE when replaced */
|
||||
static bool replace_existing(struct Curl_easy *data,
|
||||
struct Cookie *co,
|
||||
struct CookieInfo *ci,
|
||||
const struct CookieInfo *ci,
|
||||
bool secure,
|
||||
bool *replacep)
|
||||
{
|
||||
@@ -834,8 +874,10 @@ static bool replace_existing(struct Curl_easy *data,
|
||||
bool matching_domains = FALSE;
|
||||
|
||||
if(clist->domain && co->domain) {
|
||||
if(curl_strequal(clist->domain, co->domain))
|
||||
/* The domains are identical */
|
||||
if(cookie_tailmatch(clist->domain, strlen(clist->domain),
|
||||
co->domain) ||
|
||||
cookie_tailmatch(co->domain, strlen(co->domain), clist->domain))
|
||||
/* The existing one is a tail of the new or vice versa */
|
||||
matching_domains = TRUE;
|
||||
}
|
||||
else if(!clist->domain && !co->domain)
|
||||
@@ -862,7 +904,7 @@ static bool replace_existing(struct Curl_easy *data,
|
||||
else
|
||||
cllen = strlen(clist->path);
|
||||
|
||||
if(curl_strnequal(clist->path, co->path, cllen)) {
|
||||
if(!strncmp(clist->path, co->path, cllen)) {
|
||||
infof(data, "cookie '%s' for domain '%s' dropped, would "
|
||||
"overlay an existing cookie", co->name, co->domain);
|
||||
return FALSE;
|
||||
@@ -886,7 +928,7 @@ static bool replace_existing(struct Curl_easy *data,
|
||||
/* the domains were identical */
|
||||
|
||||
if(clist->path && co->path &&
|
||||
!curl_strequal(clist->path, co->path))
|
||||
strcmp(clist->path, co->path))
|
||||
replace_old = FALSE;
|
||||
else if(!clist->path != !co->path)
|
||||
replace_old = FALSE;
|
||||
@@ -1152,7 +1194,7 @@ static CURLcode cookie_load(struct Curl_easy *data, const char *file,
|
||||
curlx_fclose(handle);
|
||||
}
|
||||
data->state.cookie_engine = TRUE;
|
||||
ci->running = TRUE; /* now, we are running */
|
||||
ci->running = TRUE; /* now, we are running */
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1237,9 +1279,9 @@ static int cookie_sort_ct(const void *p1, const void *p2)
|
||||
return (c2->creationtime > c1->creationtime) ? 1 : -1;
|
||||
}
|
||||
|
||||
bool Curl_secure_context(struct connectdata *conn, const char *host)
|
||||
bool Curl_secure_context(struct Curl_easy *data, const char *host)
|
||||
{
|
||||
return conn->scheme->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS) ||
|
||||
return Curl_xfer_is_secure(data) ||
|
||||
curl_strequal("localhost", host) ||
|
||||
!strcmp(host, "127.0.0.1") ||
|
||||
!strcmp(host, "::1");
|
||||
@@ -1249,15 +1291,13 @@ bool Curl_secure_context(struct connectdata *conn, const char *host)
|
||||
* Curl_cookie_getlist
|
||||
*
|
||||
* For a given host and path, return a linked list of cookies that the client
|
||||
* should send to the server if used now. The secure boolean informs the cookie
|
||||
* if a secure connection is achieved or not.
|
||||
* should send to the server if used now.
|
||||
*
|
||||
* It shall only return cookies that have not expired.
|
||||
*
|
||||
* 'okay' is TRUE when there is a list returned.
|
||||
*/
|
||||
CURLcode Curl_cookie_getlist(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
bool *okay,
|
||||
const char *host,
|
||||
struct Curl_llist *list)
|
||||
@@ -1266,7 +1306,7 @@ CURLcode Curl_cookie_getlist(struct Curl_easy *data,
|
||||
const bool is_ip = Curl_host_is_ipnum(host);
|
||||
const size_t myhash = cookiehash(host);
|
||||
struct Curl_llist_node *n;
|
||||
const bool secure = Curl_secure_context(conn, host);
|
||||
const bool secure = Curl_secure_context(data, host);
|
||||
struct CookieInfo *ci = data->cookies;
|
||||
const char *path = data->state.up.path;
|
||||
CURLcode result = CURLE_OK;
|
||||
@@ -1561,7 +1601,7 @@ error:
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct curl_slist *cookie_list(struct Curl_easy *data)
|
||||
static struct curl_slist *cookie_list(const struct Curl_easy *data)
|
||||
{
|
||||
struct curl_slist *list = NULL;
|
||||
struct curl_slist *beg;
|
||||
|
||||
@@ -109,7 +109,7 @@ struct connectdata;
|
||||
* are only used if the header boolean is TRUE.
|
||||
*/
|
||||
|
||||
bool Curl_secure_context(struct connectdata *conn, const char *host);
|
||||
bool Curl_secure_context(struct Curl_easy *data, const char *host);
|
||||
CURLcode Curl_cookie_add(struct Curl_easy *data,
|
||||
struct CookieInfo *ci,
|
||||
bool httpheader,
|
||||
@@ -118,7 +118,7 @@ CURLcode Curl_cookie_add(struct Curl_easy *data,
|
||||
const char *domain,
|
||||
const char *path,
|
||||
bool secure) WARN_UNUSED_RESULT;
|
||||
CURLcode Curl_cookie_getlist(struct Curl_easy *data, struct connectdata *conn,
|
||||
CURLcode Curl_cookie_getlist(struct Curl_easy *data,
|
||||
bool *okay, const char *host,
|
||||
struct Curl_llist *list) WARN_UNUSED_RESULT;
|
||||
void Curl_cookie_clearall(struct CookieInfo *ci);
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
#include "curl_setup.h"
|
||||
|
||||
#include <stddef.h> /* for offsetof() */
|
||||
|
||||
#include "creds.h"
|
||||
#include "curl_trc.h"
|
||||
#include "strcase.h"
|
||||
#include "urldata.h"
|
||||
|
||||
|
||||
CURLcode Curl_creds_create(const char *user,
|
||||
const char *passwd,
|
||||
const char *oauth_bearer,
|
||||
const char *sasl_authzid,
|
||||
const char *sasl_service,
|
||||
uint8_t source,
|
||||
struct Curl_creds **pcreds)
|
||||
{
|
||||
struct Curl_creds *creds = NULL;
|
||||
size_t ulen = user ? strlen(user) : 0;
|
||||
size_t plen = passwd ? strlen(passwd) : 0;
|
||||
size_t olen = oauth_bearer ? strlen(oauth_bearer) : 0;
|
||||
size_t salen = sasl_authzid ? strlen(sasl_authzid) : 0;
|
||||
size_t sslen = sasl_service ? strlen(sasl_service) : 0;
|
||||
char *s, *buf;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
Curl_creds_unlink(pcreds);
|
||||
|
||||
/* Everything empty/NULL, this is the NULL credential */
|
||||
if(!user && !passwd && !olen && !salen && !sslen)
|
||||
goto out;
|
||||
|
||||
if((ulen > CURL_MAX_INPUT_LENGTH) ||
|
||||
(plen > CURL_MAX_INPUT_LENGTH) ||
|
||||
(olen > CURL_MAX_INPUT_LENGTH) ||
|
||||
(salen > CURL_MAX_INPUT_LENGTH) ||
|
||||
(sslen > CURL_MAX_INPUT_LENGTH)) {
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* null-terminator for user already part of struct */
|
||||
creds = curlx_calloc(1, sizeof(*creds) +
|
||||
ulen + plen + 1 + olen + 1 + salen + 1 + sslen + 1);
|
||||
if(!creds) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
creds->refcount = 1;
|
||||
creds->source = source;
|
||||
/* Some compilers try to be too smart about our dynamic struct size */
|
||||
buf = ((char *)creds) + offsetof(struct Curl_creds, buf);
|
||||
creds->user = s = buf;
|
||||
if(ulen)
|
||||
memcpy(s, user, ulen + 1);
|
||||
creds->passwd = s = buf + ulen + 1;
|
||||
if(plen)
|
||||
memcpy(s, passwd, plen + 1);
|
||||
creds->oauth_bearer = s = buf + ulen + 1 + plen + 1;
|
||||
if(olen)
|
||||
memcpy(s, oauth_bearer, olen + 1);
|
||||
creds->sasl_authzid = s = buf + ulen + 1 + plen + 1 + olen + 1;
|
||||
if(salen)
|
||||
memcpy(s, sasl_authzid, salen + 1);
|
||||
creds->sasl_service = s = buf + ulen + 1 + plen + 1 + olen + 1 + salen + 1;
|
||||
if(sslen)
|
||||
memcpy(s, sasl_service, sslen + 1);
|
||||
|
||||
out:
|
||||
if(!result)
|
||||
*pcreds = creds;
|
||||
else
|
||||
Curl_creds_unlink(&creds);
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_creds_merge(const char *user,
|
||||
const char *passwd,
|
||||
struct Curl_creds *creds_in,
|
||||
uint8_t source,
|
||||
struct Curl_creds **pcreds_out)
|
||||
{
|
||||
struct Curl_creds *creds_out = NULL;
|
||||
CURLcode result;
|
||||
|
||||
if(!creds_in) {
|
||||
result = Curl_creds_create(user, passwd, NULL, NULL, NULL,
|
||||
source, &creds_out);
|
||||
}
|
||||
else {
|
||||
result = Curl_creds_create(user ? user : Curl_creds_user(creds_in),
|
||||
passwd ? passwd : Curl_creds_passwd(creds_in),
|
||||
Curl_creds_oauth_bearer(creds_in),
|
||||
Curl_creds_sasl_authzid(creds_in),
|
||||
Curl_creds_sasl_service(creds_in),
|
||||
source, &creds_out);
|
||||
}
|
||||
Curl_creds_link(pcreds_out, creds_out);
|
||||
Curl_creds_unlink(&creds_out);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Curl_creds_link(struct Curl_creds **pdest, struct Curl_creds *src)
|
||||
{
|
||||
if(*pdest != src) {
|
||||
Curl_creds_unlink(pdest);
|
||||
*pdest = src;
|
||||
if(src) {
|
||||
DEBUGASSERT(src->refcount < UINT32_MAX);
|
||||
src->refcount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Curl_creds_unlink(struct Curl_creds **pcreds)
|
||||
{
|
||||
if(*pcreds) {
|
||||
struct Curl_creds *creds = *pcreds;
|
||||
|
||||
DEBUGASSERT(creds->refcount);
|
||||
*pcreds = NULL;
|
||||
if(creds->refcount)
|
||||
creds->refcount--;
|
||||
if(!creds->refcount) {
|
||||
curlx_free(creds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Curl_creds_same(struct Curl_creds *c1, struct Curl_creds *c2)
|
||||
{
|
||||
return (c1 == c2) ||
|
||||
(c1 && c2 &&
|
||||
!Curl_timestrcmp(c1->user, c2->user) &&
|
||||
!Curl_timestrcmp(c1->passwd, c2->passwd) &&
|
||||
!Curl_timestrcmp(c1->oauth_bearer, c2->oauth_bearer) &&
|
||||
!Curl_timestrcmp(c1->sasl_authzid, c2->sasl_authzid) &&
|
||||
!Curl_timestrcmp(c1->sasl_service, c2->sasl_service));
|
||||
}
|
||||
|
||||
bool Curl_creds_equal(struct Curl_creds *c1, struct Curl_creds *c2)
|
||||
{
|
||||
return Curl_creds_same(c1, c2) &&
|
||||
((c1 == c2) || (c1 && c2 && (c1->source == c2->source)));
|
||||
}
|
||||
|
||||
#ifdef CURLVERBOSE
|
||||
void Curl_creds_trace(struct Curl_easy *data, struct Curl_creds *creds,
|
||||
const char *msg)
|
||||
{
|
||||
if(creds) {
|
||||
CURL_TRC_M(data, "%s: user=%s, passwd=%s, "
|
||||
"sasl_authzid=%s, oauth_bearer=%s, source=%d",
|
||||
msg,
|
||||
Curl_creds_user(creds),
|
||||
Curl_creds_has_passwd(creds) ? "***" : "",
|
||||
Curl_creds_sasl_authzid(creds),
|
||||
Curl_creds_has_oauth_bearer(creds) ? "***" : "",
|
||||
creds->source);
|
||||
}
|
||||
else
|
||||
CURL_TRC_M(data, "%s: -", msg);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,91 @@
|
||||
#ifndef HEADER_CURL_CREDS_H
|
||||
#define HEADER_CURL_CREDS_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* Project ___| | | | _ \| |
|
||||
* / __| | | | |_) | |
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
*
|
||||
* This software is licensed as described in the file COPYING, which
|
||||
* you should have received as part of this distribution. The terms
|
||||
* are also available at https://curl.se/docs/copyright.html.
|
||||
*
|
||||
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
||||
* copies of the Software, and permit persons to whom the Software is
|
||||
* furnished to do so, under the terms of the COPYING file.
|
||||
*
|
||||
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
||||
* KIND, either express or implied.
|
||||
*
|
||||
* SPDX-License-Identifier: curl
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
struct Curl_easy;
|
||||
|
||||
#define CREDS_NONE 0 /* used for default username/passwd */
|
||||
#define CREDS_URL 1 /* username/passwd from URL */
|
||||
#define CREDS_OPTION 2 /* username/passwd set with a CURLOPT_ */
|
||||
#define CREDS_NETRC 3 /* username/passwd found in netrc */
|
||||
|
||||
struct Curl_creds {
|
||||
const char *user; /* non-NULL, maybe empty string */
|
||||
const char *passwd; /* non-NULL, maybe empty string */
|
||||
const char *oauth_bearer; /* non-NULL, maybe empty string */
|
||||
const char *sasl_authzid; /* non-NULL, maybe empty string */
|
||||
const char *sasl_service; /* non-NULL, maybe empty string */
|
||||
uint32_t refcount;
|
||||
uint8_t source; /* CREDS_* value */
|
||||
char buf[1];
|
||||
};
|
||||
|
||||
CURLcode Curl_creds_create(const char *user,
|
||||
const char *passwd,
|
||||
const char *oauth_bearer,
|
||||
const char *sasl_authzid,
|
||||
const char *sasl_service,
|
||||
uint8_t source,
|
||||
struct Curl_creds **pcreds);
|
||||
|
||||
/* Create credentials by overriding `user` and/or `passwd` in `creds_in` */
|
||||
CURLcode Curl_creds_merge(const char *user,
|
||||
const char *passwd,
|
||||
struct Curl_creds *creds_in,
|
||||
uint8_t source,
|
||||
struct Curl_creds **pcreds_out);
|
||||
|
||||
/* Unlink any creds in `*pdest`, assign src, increase src
|
||||
* refcount when not NULL. */
|
||||
void Curl_creds_link(struct Curl_creds **pdest, struct Curl_creds *src);
|
||||
|
||||
/* Drop a reference, creds may be passed as NULL */
|
||||
void Curl_creds_unlink(struct Curl_creds **pcreds);
|
||||
|
||||
/* TRUE if both creds are NULL or have same values, except source. */
|
||||
bool Curl_creds_same(struct Curl_creds *c1, struct Curl_creds *c2);
|
||||
|
||||
/* TRUE if both creds are NULL or have all values equal. */
|
||||
bool Curl_creds_equal(struct Curl_creds *c1, struct Curl_creds *c2);
|
||||
|
||||
/* Provides properties for creds or, if creds is NULL, the empty string */
|
||||
#define Curl_creds_has_user(c) ((c) && (c)->user[0])
|
||||
#define Curl_creds_has_passwd(c) ((c) && (c)->passwd[0])
|
||||
#define Curl_creds_has_user_or_pass(c) \
|
||||
((c) && ((c)->user[0] || (c)->passwd[0]))
|
||||
#define Curl_creds_has_oauth_bearer(c) ((c) && (c)->oauth_bearer[0])
|
||||
#define Curl_creds_has_sasl_service(c) ((c) && (c)->sasl_service[0])
|
||||
#define Curl_creds_user(c) ((c) ? (c)->user : "")
|
||||
#define Curl_creds_passwd(c) ((c) ? (c)->passwd : "")
|
||||
#define Curl_creds_oauth_bearer(c) ((c) ? (c)->oauth_bearer : "")
|
||||
#define Curl_creds_sasl_authzid(c) ((c) ? (c)->sasl_authzid : "")
|
||||
#define Curl_creds_sasl_service(c) ((c) ? (c)->sasl_service : "")
|
||||
|
||||
#ifdef CURLVERBOSE
|
||||
void Curl_creds_trace(struct Curl_easy *data, struct Curl_creds *creds,
|
||||
const char *msg);
|
||||
#endif
|
||||
|
||||
#endif /* HEADER_CURL_CREDS_H */
|
||||
@@ -150,8 +150,8 @@ void Curl_cshutdn_terminate(struct Curl_easy *data,
|
||||
CURL_TRC_M(admin, "[SHUTDOWN] %sclosing connection #%" FMT_OFF_T,
|
||||
conn->bits.shutdown_filters ? "" : "force ",
|
||||
conn->connection_id);
|
||||
Curl_conn_close(admin, SECONDARYSOCKET);
|
||||
Curl_conn_close(admin, FIRSTSOCKET);
|
||||
Curl_conn_cf_discard_all(admin, conn, SECONDARYSOCKET);
|
||||
Curl_conn_cf_discard_all(admin, conn, FIRSTSOCKET);
|
||||
Curl_detach_connection(admin);
|
||||
|
||||
if(data->multi)
|
||||
@@ -322,14 +322,14 @@ int Curl_cshutdn_init(struct cshutdn *cshutdn,
|
||||
DEBUGASSERT(multi);
|
||||
cshutdn->multi = multi;
|
||||
Curl_llist_init(&cshutdn->list, NULL);
|
||||
cshutdn->initialised = TRUE;
|
||||
cshutdn->initialized = TRUE;
|
||||
return 0; /* good */
|
||||
}
|
||||
|
||||
void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
|
||||
struct Curl_easy *data)
|
||||
{
|
||||
if(cshutdn->initialised && data) {
|
||||
if(cshutdn->initialized && data) {
|
||||
int timeout_ms = 0;
|
||||
/* for testing, run graceful shutdown */
|
||||
#ifdef DEBUGBUILD
|
||||
|
||||
@@ -54,7 +54,7 @@ void Curl_cshutdn_terminate(struct Curl_easy *data,
|
||||
struct cshutdn {
|
||||
struct Curl_llist list; /* connections being shut down */
|
||||
struct Curl_multi *multi; /* the multi owning this */
|
||||
BIT(initialised);
|
||||
BIT(initialized);
|
||||
};
|
||||
|
||||
/* Init as part of the given multi handle. */
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "curl_addrinfo.h"
|
||||
#include "fake_addrinfo.h"
|
||||
#include "curlx/inet_pton.h"
|
||||
#include "curlx/strparse.h"
|
||||
|
||||
/*
|
||||
* Curl_freeaddrinfo()
|
||||
@@ -59,7 +60,7 @@
|
||||
* any function call which actually allocates a Curl_addrinfo struct.
|
||||
*/
|
||||
|
||||
#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
|
||||
#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
|
||||
defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
|
||||
/* workaround icc 9.1 optimizer issue */
|
||||
# define vqualifier volatile
|
||||
@@ -113,7 +114,7 @@ int Curl_getaddrinfo_ex(const char *nodename,
|
||||
|
||||
/* traverse the addrinfo list */
|
||||
|
||||
for(ai = aihead; ai != NULL; ai = ai->ai_next) {
|
||||
for(ai = aihead; ai; ai = ai->ai_next) {
|
||||
size_t namelen = ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0;
|
||||
/* ignore elements with unsupported address family,
|
||||
settle family-specific sockaddr structure size. */
|
||||
@@ -256,7 +257,7 @@ struct Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port)
|
||||
/* no input == no output! */
|
||||
return NULL;
|
||||
|
||||
DEBUGASSERT((he->h_name != NULL) && (he->h_addr_list != NULL));
|
||||
DEBUGASSERT(he->h_name && he->h_addr_list);
|
||||
|
||||
for(i = 0; (curr = he->h_addr_list[i]) != NULL; i++) {
|
||||
size_t ss_size;
|
||||
@@ -443,6 +444,48 @@ bool Curl_is_ipaddr(const char *address)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool Curl_looks_like_ipv6(const char *s, size_t len, bool maybe_url_encoded,
|
||||
struct Curl_str *host, struct Curl_str *zone)
|
||||
{
|
||||
const char *zonep = NULL;
|
||||
size_t i = 0, hlen = 0, zlen = 0;
|
||||
|
||||
if(host)
|
||||
memset(host, 0, sizeof(*host));
|
||||
if(zone)
|
||||
memset(zone, 0, sizeof(*zone));
|
||||
|
||||
for(i = 0; i < len; ++i, ++hlen) {
|
||||
if(!s[i] || !(ISXDIGIT(s[i]) || (s[i] == ':') || (s[i] == '.')))
|
||||
break;
|
||||
}
|
||||
|
||||
if((i < len) && (s[i] == '%')) { /* address followed by a zone? */
|
||||
i += 1;
|
||||
if(maybe_url_encoded && !strncmp("25", s + i, 2))
|
||||
i += 2;
|
||||
zonep = s + i;
|
||||
for(; i < len; ++i, ++zlen) {
|
||||
/* Allow unreserved characters as defined in RFC 3986 */
|
||||
if(!s[i] || !(ISALPHA(s[i]) || ISXDIGIT(s[i]) || (s[i] == '-') ||
|
||||
(s[i] == '.') || (s[i] == '_') || (s[i] == '~')))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(i != len)
|
||||
return FALSE; /* invalid chars in zone */
|
||||
if(host && hlen) {
|
||||
host->str = s;
|
||||
host->len = hlen;
|
||||
}
|
||||
if(zone && zlen) {
|
||||
zone->str = zonep;
|
||||
zone->len = zlen;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
/**
|
||||
* Given a path to a Unix domain socket, return a newly allocated Curl_addrinfo
|
||||
@@ -560,7 +603,7 @@ int curl_dbg_getaddrinfo(const char *hostname,
|
||||
|
||||
#if defined(HAVE_GETADDRINFO) && defined(USE_RESOLVE_ON_IPS)
|
||||
/*
|
||||
* Work-arounds the sin6_port is always zero bug on iOS 9.3.2 and macOS
|
||||
* Works around the sin6_port is always zero bug on iOS 9.3.2 and macOS
|
||||
* 10.11.5.
|
||||
*/
|
||||
void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port)
|
||||
@@ -570,7 +613,7 @@ void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port)
|
||||
#ifdef USE_IPV6
|
||||
struct sockaddr_in6 *addr6;
|
||||
#endif
|
||||
for(ca = addrinfo; ca != NULL; ca = ca->ai_next) {
|
||||
for(ca = addrinfo; ca; ca = ca->ai_next) {
|
||||
switch(ca->ai_family) {
|
||||
case AF_INET:
|
||||
addr = (void *)ca->ai_addr; /* storage area for this info */
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
# include <inet.h>
|
||||
#endif
|
||||
|
||||
struct Curl_str;
|
||||
|
||||
/*
|
||||
* Curl_addrinfo is our internal struct definition that we use to allow
|
||||
* consistent internal handling of this data. We use this even when the system
|
||||
@@ -73,6 +75,9 @@ struct Curl_addrinfo *Curl_he2ai(const struct hostent *he, int port);
|
||||
|
||||
bool Curl_is_ipv4addr(const char *address);
|
||||
bool Curl_is_ipaddr(const char *address);
|
||||
bool Curl_looks_like_ipv6(const char *s, size_t len, bool maybe_url_encoded,
|
||||
struct Curl_str *host, struct Curl_str *zone);
|
||||
|
||||
CURLcode Curl_str2addr(const char *dotted, uint16_t port,
|
||||
struct Curl_addrinfo **addrp);
|
||||
|
||||
|
||||
@@ -227,6 +227,12 @@
|
||||
/* Define to 1 if you have the `opendir' function. */
|
||||
#cmakedefine HAVE_OPENDIR 1
|
||||
|
||||
/* Define to 1 if you have the memset_explicit (C23) function. */
|
||||
#cmakedefine HAVE_MEMSET_EXPLICIT 1
|
||||
|
||||
/* Define to 1 if you have the memset_s (C11) function. */
|
||||
#cmakedefine HAVE_MEMSET_S 1
|
||||
|
||||
/* Define to 1 if you have the fcntl function. */
|
||||
#cmakedefine HAVE_FCNTL 1
|
||||
|
||||
@@ -248,7 +254,7 @@
|
||||
/* Define to 1 if you have a working getaddrinfo function. */
|
||||
#cmakedefine HAVE_GETADDRINFO 1
|
||||
|
||||
/* Define to 1 if the getaddrinfo function is threadsafe. */
|
||||
/* Define to 1 if the getaddrinfo function is thread-safe. */
|
||||
#cmakedefine HAVE_GETADDRINFO_THREADSAFE 1
|
||||
|
||||
/* Define to 1 if you have the `geteuid' function. */
|
||||
@@ -713,6 +719,9 @@ ${SIZEOF_TIME_T_CODE}
|
||||
/* if libuv is in use */
|
||||
#cmakedefine USE_LIBUV 1
|
||||
|
||||
/* if HTTP/3 proxy support is available */
|
||||
#cmakedefine USE_PROXY_HTTP3 1
|
||||
|
||||
/* Define to 1 if you have the <uv.h> header file. */
|
||||
#cmakedefine HAVE_UV_H 1
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
*
|
||||
* Parameters:
|
||||
*
|
||||
* buf [in] - A pointer to a 2 byte buffer.
|
||||
* buf [in] - A pointer to a 2-byte buffer.
|
||||
*
|
||||
* Returns the integer.
|
||||
*/
|
||||
@@ -53,7 +53,7 @@ unsigned short Curl_read16_le(const unsigned char *buf)
|
||||
*
|
||||
* Parameters:
|
||||
*
|
||||
* buf [in] - A pointer to a 4 byte buffer.
|
||||
* buf [in] - A pointer to a 4-byte buffer.
|
||||
*
|
||||
* Returns the integer.
|
||||
*/
|
||||
@@ -72,7 +72,7 @@ unsigned int Curl_read32_le(const unsigned char *buf)
|
||||
*
|
||||
* Parameters:
|
||||
*
|
||||
* buf [in] - A pointer to a 2 byte buffer.
|
||||
* buf [in] - A pointer to a 2-byte buffer.
|
||||
*
|
||||
* Returns the integer.
|
||||
*/
|
||||
|
||||
@@ -96,25 +96,25 @@ static int parsekeyword(const unsigned char **pattern, unsigned char *charset)
|
||||
#undef KEYLEN
|
||||
|
||||
*pattern = p; /* move caller's pattern pointer */
|
||||
if(strcmp(keyword, "digit") == 0)
|
||||
if(!strcmp(keyword, "digit"))
|
||||
charset[CURLFNM_DIGIT] = 1;
|
||||
else if(strcmp(keyword, "alnum") == 0)
|
||||
else if(!strcmp(keyword, "alnum"))
|
||||
charset[CURLFNM_ALNUM] = 1;
|
||||
else if(strcmp(keyword, "alpha") == 0)
|
||||
else if(!strcmp(keyword, "alpha"))
|
||||
charset[CURLFNM_ALPHA] = 1;
|
||||
else if(strcmp(keyword, "xdigit") == 0)
|
||||
else if(!strcmp(keyword, "xdigit"))
|
||||
charset[CURLFNM_XDIGIT] = 1;
|
||||
else if(strcmp(keyword, "print") == 0)
|
||||
else if(!strcmp(keyword, "print"))
|
||||
charset[CURLFNM_PRINT] = 1;
|
||||
else if(strcmp(keyword, "graph") == 0)
|
||||
else if(!strcmp(keyword, "graph"))
|
||||
charset[CURLFNM_GRAPH] = 1;
|
||||
else if(strcmp(keyword, "space") == 0)
|
||||
else if(!strcmp(keyword, "space"))
|
||||
charset[CURLFNM_SPACE] = 1;
|
||||
else if(strcmp(keyword, "blank") == 0)
|
||||
else if(!strcmp(keyword, "blank"))
|
||||
charset[CURLFNM_BLANK] = 1;
|
||||
else if(strcmp(keyword, "upper") == 0)
|
||||
else if(!strcmp(keyword, "upper"))
|
||||
charset[CURLFNM_UPPER] = 1;
|
||||
else if(strcmp(keyword, "lower") == 0)
|
||||
else if(!strcmp(keyword, "lower"))
|
||||
charset[CURLFNM_LOWER] = 1;
|
||||
else
|
||||
return SETCHARSET_FAIL;
|
||||
|
||||
@@ -42,13 +42,13 @@
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#define PATHSEP "\\"
|
||||
#define PATHSEP "\\"
|
||||
#define IS_SEP(x) (((x) == '/') || ((x) == '\\'))
|
||||
#elif defined(MSDOS) || defined(OS2)
|
||||
#define PATHSEP "\\"
|
||||
#define PATHSEP "\\"
|
||||
#define IS_SEP(x) ((x) == '\\')
|
||||
#else
|
||||
#define PATHSEP "/"
|
||||
#define PATHSEP "/"
|
||||
#define IS_SEP(x) ((x) == '/')
|
||||
#endif
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ static OM_uint32 stub_gss_init_sec_context(
|
||||
used = curl_msnprintf(token, length, "%s:%.*s:%d:", creds,
|
||||
(int)target_desc.length,
|
||||
(const char *)target_desc.value,
|
||||
ctx->sent);
|
||||
(int)ctx->sent);
|
||||
|
||||
gss_release_buffer(&minor_status, &target_desc);
|
||||
}
|
||||
@@ -302,8 +302,7 @@ static OM_uint32 stub_gss_delete_sec_context(
|
||||
return GSS_S_FAILURE;
|
||||
}
|
||||
|
||||
curlx_free(*context);
|
||||
*context = NULL;
|
||||
curlx_safefree(*context);
|
||||
*min = 0;
|
||||
|
||||
return GSS_S_COMPLETE;
|
||||
|
||||
@@ -24,26 +24,26 @@
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \
|
||||
!defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \
|
||||
#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \
|
||||
!defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \
|
||||
defined(USE_LIBSSH2) || defined(USE_SSL)
|
||||
|
||||
#define HMAC_MD5_LENGTH 16
|
||||
|
||||
typedef CURLcode (*HMAC_hinit)(void *context);
|
||||
typedef void (*HMAC_hupdate)(void *context,
|
||||
const unsigned char *data,
|
||||
unsigned int len);
|
||||
typedef void (*HMAC_hfinal)(unsigned char *result, void *context);
|
||||
typedef void (*HMAC_hupdate)(void *context,
|
||||
const unsigned char *data,
|
||||
unsigned int len);
|
||||
typedef void (*HMAC_hfinal)(unsigned char *result, void *context);
|
||||
|
||||
/* Per-hash function HMAC parameters. */
|
||||
struct HMAC_params {
|
||||
HMAC_hinit hinit; /* Initialize context procedure. */
|
||||
HMAC_hupdate hupdate; /* Update context with data. */
|
||||
HMAC_hfinal hfinal; /* Get final result procedure. */
|
||||
unsigned int ctxtsize; /* Context structure size. */
|
||||
unsigned int maxkeylen; /* Maximum key length (bytes). */
|
||||
unsigned int resultlen; /* Result length (bytes). */
|
||||
HMAC_hinit hinit; /* Initialize context procedure. */
|
||||
HMAC_hupdate hupdate; /* Update context with data. */
|
||||
HMAC_hfinal hfinal; /* Get final result procedure. */
|
||||
unsigned int ctxtsize; /* Context structure size. */
|
||||
unsigned int maxkeylen; /* Maximum key length (bytes). */
|
||||
unsigned int resultlen; /* Result length (bytes). */
|
||||
};
|
||||
|
||||
/* HMAC computation context. */
|
||||
@@ -66,7 +66,6 @@ CURLcode Curl_hmacit(const struct HMAC_params *hashparams,
|
||||
const unsigned char *key, const size_t keylen,
|
||||
const unsigned char *data, size_t datalen,
|
||||
unsigned char *output);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* HEADER_CURL_HMAC_H */
|
||||
|
||||
@@ -38,16 +38,16 @@ typedef void (*Curl_MD5_update_func)(void *context,
|
||||
typedef void (*Curl_MD5_final_func)(unsigned char *result, void *context);
|
||||
|
||||
struct MD5_params {
|
||||
Curl_MD5_init_func md5_init_func; /* Initialize context procedure */
|
||||
Curl_MD5_update_func md5_update_func; /* Update context with data */
|
||||
Curl_MD5_final_func md5_final_func; /* Get final result procedure */
|
||||
unsigned int md5_ctxtsize; /* Context structure size */
|
||||
unsigned int md5_resultlen; /* Result length (bytes) */
|
||||
Curl_MD5_init_func md5_init_func; /* Initialize context procedure */
|
||||
Curl_MD5_update_func md5_update_func; /* Update context with data */
|
||||
Curl_MD5_final_func md5_final_func; /* Get final result procedure */
|
||||
unsigned int md5_ctxtsize; /* Context structure size */
|
||||
unsigned int md5_resultlen; /* Result length (bytes) */
|
||||
};
|
||||
|
||||
struct MD5_context {
|
||||
const struct MD5_params *md5_hash; /* Hash function definition */
|
||||
void *md5_hashctx; /* Hash function context */
|
||||
const struct MD5_params *md5_hash; /* Hash function definition */
|
||||
void *md5_hashctx; /* Hash function context */
|
||||
};
|
||||
|
||||
extern const struct MD5_params Curl_DIGEST_MD5;
|
||||
|
||||
@@ -49,6 +49,13 @@
|
||||
in NTLM type-3 messages.
|
||||
*/
|
||||
|
||||
#ifdef USE_GNUTLS
|
||||
#include <nettle/version.h>
|
||||
#if NETTLE_VERSION_MAJOR < 4
|
||||
#define USE_GNUTLS_DES
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT)
|
||||
|
||||
# include <openssl/des.h>
|
||||
@@ -63,7 +70,7 @@
|
||||
# include <wolfssl/wolfcrypt/des3.h>
|
||||
# define USE_WOLFSSL_DES
|
||||
|
||||
#elif defined(USE_GNUTLS)
|
||||
#elif defined(USE_GNUTLS_DES)
|
||||
# include <nettle/des.h>
|
||||
# define USE_CURL_DES_SET_ODD_PARITY
|
||||
#elif defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB)
|
||||
@@ -175,7 +182,7 @@ static void setup_des_key(const unsigned char *key_56, Des *des)
|
||||
wc_Des_SetKey(des, key, NULL, 0);
|
||||
}
|
||||
|
||||
#elif defined(USE_GNUTLS)
|
||||
#elif defined(USE_GNUTLS_DES)
|
||||
static void setup_des_key(const unsigned char *key_56, struct des_ctx *des)
|
||||
{
|
||||
char key[8];
|
||||
@@ -244,6 +251,7 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out,
|
||||
char key[8];
|
||||
} blob;
|
||||
DWORD len = 8;
|
||||
BOOL res;
|
||||
|
||||
/* Acquire the crypto provider */
|
||||
if(!CryptAcquireContext(&hprov, NULL, NULL, PROV_RSA_FULL,
|
||||
@@ -273,19 +281,19 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out,
|
||||
memcpy(out, in, 8);
|
||||
|
||||
/* Perform the encryption */
|
||||
CryptEncrypt(hkey, 0, FALSE, 0, out, &len, len);
|
||||
res = CryptEncrypt(hkey, 0, FALSE, 0, out, &len, len);
|
||||
|
||||
CryptDestroyKey(hkey);
|
||||
CryptReleaseContext(hprov, 0);
|
||||
|
||||
return TRUE;
|
||||
return res;
|
||||
}
|
||||
|
||||
#endif /* crypto backends */
|
||||
|
||||
/*
|
||||
* takes a 21 byte array and treats it as 3 56-bit DES keys. The
|
||||
* 8 byte plaintext is encrypted with each key and the resulting 24
|
||||
* takes a 21-byte array and treats it as 3 56-bit DES keys. The
|
||||
* 8-byte plaintext is encrypted with each key and the resulting 24
|
||||
* bytes are stored in the results array.
|
||||
*/
|
||||
void Curl_ntlm_core_lm_resp(const unsigned char *keys,
|
||||
@@ -314,7 +322,7 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys,
|
||||
wc_Des_EcbEncrypt(&des, results + 8, plaintext, DES_KEY_SIZE);
|
||||
setup_des_key(keys + 14, &des);
|
||||
wc_Des_EcbEncrypt(&des, results + 16, plaintext, DES_KEY_SIZE);
|
||||
#elif defined(USE_GNUTLS)
|
||||
#elif defined(USE_GNUTLS_DES)
|
||||
struct des_ctx des;
|
||||
setup_des_key(keys, &des);
|
||||
des_encrypt(&des, 8, results, plaintext);
|
||||
@@ -367,7 +375,7 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password,
|
||||
wc_Des_EcbEncrypt(&des, lmbuffer, magic, DES_KEY_SIZE);
|
||||
setup_des_key(pw + 7, &des);
|
||||
wc_Des_EcbEncrypt(&des, lmbuffer + 8, magic, DES_KEY_SIZE);
|
||||
#elif defined(USE_GNUTLS)
|
||||
#elif defined(USE_GNUTLS_DES)
|
||||
struct des_ctx des;
|
||||
setup_des_key(pw, &des);
|
||||
des_encrypt(&des, 8, lmbuffer, magic);
|
||||
@@ -625,7 +633,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(const unsigned char *ntlmv2hash,
|
||||
*
|
||||
* ntlmv2hash [in] - The NTLMv2 hash (16 bytes)
|
||||
* challenge_client [in] - The client nonce (8 bytes)
|
||||
* challenge_client [in] - The server challenge (8 bytes)
|
||||
* challenge_server [in] - The server challenge (8 bytes)
|
||||
* lmresp [out] - The LMv2 response (24 bytes)
|
||||
*
|
||||
* Returns CURLE_OK on success.
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
struct ntlmdata;
|
||||
|
||||
/* Helpers to generate function byte arguments in little endian order */
|
||||
#define SHORTPAIR(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff))
|
||||
#define SHORTPAIR(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff))
|
||||
#define LONGQUARTET(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)), \
|
||||
((int)(((x) >> 16) & 0xff)), ((int)(((x) >> 24) & 0xff))
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#include "curl_setup.h"
|
||||
|
||||
#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
|
||||
!defined(CURL_DISABLE_POP3) || \
|
||||
!defined(CURL_DISABLE_POP3) || \
|
||||
(!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP))
|
||||
|
||||
#include "urldata.h"
|
||||
@@ -276,7 +276,7 @@ static CURLcode build_message(struct SASL *sasl, struct bufref *msg)
|
||||
bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data)
|
||||
{
|
||||
/* Have credentials been provided? */
|
||||
if(data->conn->user[0])
|
||||
if(data->conn->creds)
|
||||
return TRUE;
|
||||
|
||||
/* EXTERNAL can authenticate without a username and/or password */
|
||||
@@ -299,13 +299,15 @@ struct sasl_ctx {
|
||||
|
||||
static bool sasl_choose_external(struct Curl_easy *data, struct sasl_ctx *sctx)
|
||||
{
|
||||
if((sctx->enabledmechs & SASL_MECH_EXTERNAL) && !sctx->conn->passwd[0]) {
|
||||
if((sctx->enabledmechs & SASL_MECH_EXTERNAL) &&
|
||||
!Curl_creds_has_passwd(sctx->conn->creds)) {
|
||||
sctx->mech = SASL_MECH_STRING_EXTERNAL;
|
||||
sctx->state1 = SASL_EXTERNAL;
|
||||
sctx->sasl->authused = SASL_MECH_EXTERNAL;
|
||||
|
||||
if(sctx->sasl->force_ir || data->set.sasl_ir)
|
||||
Curl_auth_create_external_message(sctx->conn->user, &sctx->resp);
|
||||
Curl_auth_create_external_message(
|
||||
Curl_creds_user(sctx->conn->creds), &sctx->resp);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
@@ -316,10 +318,9 @@ static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx)
|
||||
{
|
||||
if((sctx->enabledmechs & SASL_MECH_GSSAPI) &&
|
||||
Curl_auth_is_gssapi_supported() &&
|
||||
Curl_auth_user_contains_domain(sctx->conn->user)) {
|
||||
const char *service = data->set.str[STRING_SERVICE_NAME] ?
|
||||
data->set.str[STRING_SERVICE_NAME] :
|
||||
sctx->sasl->params->service;
|
||||
Curl_auth_user_contains_domain(sctx->conn->creds)) {
|
||||
const char *service = Curl_creds_has_sasl_service(sctx->conn->creds) ?
|
||||
Curl_creds_sasl_service(sctx->conn->creds) : sctx->sasl->params->service;
|
||||
|
||||
sctx->sasl->mutual_auth = FALSE;
|
||||
sctx->mech = SASL_MECH_STRING_GSSAPI;
|
||||
@@ -330,9 +331,9 @@ static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx)
|
||||
if(sctx->sasl->force_ir || data->set.sasl_ir) {
|
||||
struct kerberos5data *krb5 = Curl_auth_krb5_get(sctx->conn);
|
||||
sctx->result = !krb5 ? CURLE_OUT_OF_MEMORY :
|
||||
Curl_auth_create_gssapi_user_message(data, sctx->conn->user,
|
||||
sctx->conn->passwd,
|
||||
service, sctx->conn->host.name,
|
||||
Curl_auth_create_gssapi_user_message(data, sctx->conn->creds,
|
||||
service,
|
||||
sctx->conn->origin->hostname,
|
||||
(bool)sctx->sasl->mutual_auth,
|
||||
NULL, krb5, &sctx->resp);
|
||||
}
|
||||
@@ -374,8 +375,7 @@ static bool sasl_choose_gsasl(struct Curl_easy *data, struct sasl_ctx *sctx)
|
||||
Curl_bufref_init(&nullmsg);
|
||||
sctx->state1 = SASL_GSASL;
|
||||
sctx->state2 = SASL_GSASL;
|
||||
sctx->result = Curl_auth_gsasl_start(data, sctx->conn->user,
|
||||
sctx->conn->passwd, gsasl);
|
||||
sctx->result = Curl_auth_gsasl_start(data, sctx->conn->creds, gsasl);
|
||||
if(!sctx->result && (sctx->sasl->force_ir || data->set.sasl_ir))
|
||||
sctx->result = Curl_auth_gsasl_token(data, &nullmsg, gsasl, &sctx->resp);
|
||||
return TRUE;
|
||||
@@ -411,9 +411,8 @@ static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx)
|
||||
{
|
||||
if((sctx->enabledmechs & SASL_MECH_NTLM) &&
|
||||
Curl_auth_is_ntlm_supported()) {
|
||||
const char *service = data->set.str[STRING_SERVICE_NAME] ?
|
||||
data->set.str[STRING_SERVICE_NAME] :
|
||||
sctx->sasl->params->service;
|
||||
const char *service = Curl_creds_has_sasl_service(sctx->conn->creds) ?
|
||||
Curl_creds_sasl_service(sctx->conn->creds) : sctx->sasl->params->service;
|
||||
const char *hostname;
|
||||
|
||||
Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, NULL);
|
||||
@@ -426,9 +425,7 @@ static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx)
|
||||
if(sctx->sasl->force_ir || data->set.sasl_ir) {
|
||||
struct ntlmdata *ntlm = Curl_auth_ntlm_get(sctx->conn, FALSE);
|
||||
sctx->result = !ntlm ? CURLE_OUT_OF_MEMORY :
|
||||
Curl_auth_create_ntlm_type1_message(data,
|
||||
sctx->conn->user,
|
||||
sctx->conn->passwd,
|
||||
Curl_auth_create_ntlm_type1_message(data, sctx->conn->creds,
|
||||
service, hostname,
|
||||
ntlm, &sctx->resp);
|
||||
}
|
||||
@@ -440,11 +437,8 @@ static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx)
|
||||
|
||||
static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx)
|
||||
{
|
||||
const char *oauth_bearer =
|
||||
(!data->state.this_is_a_follow || data->set.allow_auth_to_other_hosts) ?
|
||||
data->set.str[STRING_BEARER] : NULL;
|
||||
|
||||
if(oauth_bearer && (sctx->enabledmechs & SASL_MECH_OAUTHBEARER)) {
|
||||
if(Curl_creds_has_oauth_bearer(data->state.creds) &&
|
||||
(sctx->enabledmechs & SASL_MECH_OAUTHBEARER)) {
|
||||
const char *hostname;
|
||||
int port;
|
||||
Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port);
|
||||
@@ -456,9 +450,8 @@ static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx)
|
||||
|
||||
if(sctx->sasl->force_ir || data->set.sasl_ir)
|
||||
sctx->result =
|
||||
Curl_auth_create_oauth_bearer_message(sctx->conn->user,
|
||||
hostname, port,
|
||||
oauth_bearer, &sctx->resp);
|
||||
Curl_auth_create_oauth_bearer_message(sctx->conn->creds,
|
||||
hostname, port, &sctx->resp);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
@@ -466,19 +459,15 @@ static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx)
|
||||
|
||||
static bool sasl_choose_oauth2(struct Curl_easy *data, struct sasl_ctx *sctx)
|
||||
{
|
||||
const char *oauth_bearer =
|
||||
(!data->state.this_is_a_follow || data->set.allow_auth_to_other_hosts) ?
|
||||
data->set.str[STRING_BEARER] : NULL;
|
||||
|
||||
if(oauth_bearer && (sctx->enabledmechs & SASL_MECH_XOAUTH2)) {
|
||||
if(Curl_creds_has_oauth_bearer(sctx->conn->creds) &&
|
||||
(sctx->enabledmechs & SASL_MECH_XOAUTH2)) {
|
||||
sctx->mech = SASL_MECH_STRING_XOAUTH2;
|
||||
sctx->state1 = SASL_OAUTH2;
|
||||
sctx->sasl->authused = SASL_MECH_XOAUTH2;
|
||||
|
||||
if(sctx->sasl->force_ir || data->set.sasl_ir)
|
||||
sctx->result = Curl_auth_create_xoauth_bearer_message(sctx->conn->user,
|
||||
oauth_bearer,
|
||||
&sctx->resp);
|
||||
sctx->result = Curl_auth_create_xoauth_bearer_message(
|
||||
sctx->conn->creds, &sctx->resp);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
@@ -493,9 +482,7 @@ static bool sasl_choose_plain(struct Curl_easy *data, struct sasl_ctx *sctx)
|
||||
|
||||
if(sctx->sasl->force_ir || data->set.sasl_ir)
|
||||
sctx->result =
|
||||
Curl_auth_create_plain_message(sctx->conn->sasl_authzid,
|
||||
sctx->conn->user, sctx->conn->passwd,
|
||||
&sctx->resp);
|
||||
Curl_auth_create_plain_message(sctx->conn->creds, &sctx->resp);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
@@ -510,7 +497,8 @@ static bool sasl_choose_login(struct Curl_easy *data, struct sasl_ctx *sctx)
|
||||
sctx->sasl->authused = SASL_MECH_LOGIN;
|
||||
|
||||
if(sctx->sasl->force_ir || data->set.sasl_ir)
|
||||
Curl_auth_create_login_message(sctx->conn->user, &sctx->resp);
|
||||
Curl_auth_create_login_message(
|
||||
Curl_creds_user(sctx->conn->creds), &sctx->resp);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
@@ -599,13 +587,6 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
|
||||
struct bufref resp;
|
||||
const char *hostname;
|
||||
int port;
|
||||
#if defined(USE_KERBEROS5) || defined(USE_NTLM) || \
|
||||
!defined(CURL_DISABLE_DIGEST_AUTH)
|
||||
const char *service = data->set.str[STRING_SERVICE_NAME] ?
|
||||
data->set.str[STRING_SERVICE_NAME] :
|
||||
sasl->params->service;
|
||||
#endif
|
||||
const char *oauth_bearer = data->set.str[STRING_BEARER];
|
||||
struct bufref serverdata;
|
||||
|
||||
Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port);
|
||||
@@ -633,18 +614,17 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
|
||||
*progress = SASL_DONE;
|
||||
return result;
|
||||
case SASL_PLAIN:
|
||||
result = Curl_auth_create_plain_message(conn->sasl_authzid,
|
||||
conn->user, conn->passwd, &resp);
|
||||
result = Curl_auth_create_plain_message(conn->creds, &resp);
|
||||
break;
|
||||
case SASL_LOGIN:
|
||||
Curl_auth_create_login_message(conn->user, &resp);
|
||||
Curl_auth_create_login_message(Curl_creds_user(conn->creds), &resp);
|
||||
newstate = SASL_LOGIN_PASSWD;
|
||||
break;
|
||||
case SASL_LOGIN_PASSWD:
|
||||
Curl_auth_create_login_message(conn->passwd, &resp);
|
||||
Curl_auth_create_login_message(Curl_creds_passwd(conn->creds), &resp);
|
||||
break;
|
||||
case SASL_EXTERNAL:
|
||||
Curl_auth_create_external_message(conn->user, &resp);
|
||||
Curl_auth_create_external_message(Curl_creds_user(conn->creds), &resp);
|
||||
break;
|
||||
#ifdef USE_GSASL
|
||||
case SASL_GSASL:
|
||||
@@ -662,15 +642,16 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
|
||||
case SASL_CRAMMD5:
|
||||
result = get_server_message(sasl, data, &serverdata);
|
||||
if(!result)
|
||||
result = Curl_auth_create_cram_md5_message(&serverdata, conn->user,
|
||||
conn->passwd, &resp);
|
||||
result = Curl_auth_create_cram_md5_message(&serverdata, conn->creds,
|
||||
&resp);
|
||||
break;
|
||||
case SASL_DIGESTMD5:
|
||||
result = get_server_message(sasl, data, &serverdata);
|
||||
if(!result)
|
||||
result = Curl_auth_create_digest_md5_message(data, &serverdata,
|
||||
conn->user, conn->passwd,
|
||||
service, &resp);
|
||||
conn->creds,
|
||||
sasl->params->service,
|
||||
&resp);
|
||||
if(!result && (sasl->params->flags & SASL_FLAG_BASE64))
|
||||
newstate = SASL_DIGESTMD5_RESP;
|
||||
break;
|
||||
@@ -684,9 +665,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
|
||||
/* Create the type-1 message */
|
||||
struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, FALSE);
|
||||
result = !ntlm ? CURLE_OUT_OF_MEMORY :
|
||||
Curl_auth_create_ntlm_type1_message(data,
|
||||
conn->user, conn->passwd,
|
||||
service, hostname,
|
||||
Curl_auth_create_ntlm_type1_message(data, conn->creds,
|
||||
sasl->params->service, hostname,
|
||||
ntlm, &resp);
|
||||
newstate = SASL_NTLM_TYPE2MSG;
|
||||
break;
|
||||
@@ -699,9 +679,8 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
|
||||
if(!result)
|
||||
result = Curl_auth_decode_ntlm_type2_message(data, &serverdata, ntlm);
|
||||
if(!result)
|
||||
result = Curl_auth_create_ntlm_type3_message(data, conn->user,
|
||||
conn->passwd, ntlm,
|
||||
&resp);
|
||||
result = Curl_auth_create_ntlm_type3_message(data, conn->creds,
|
||||
ntlm, &resp);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
@@ -710,8 +689,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
|
||||
case SASL_GSSAPI: {
|
||||
struct kerberos5data *krb5 = Curl_auth_krb5_get(conn);
|
||||
result = !krb5 ? CURLE_OUT_OF_MEMORY :
|
||||
Curl_auth_create_gssapi_user_message(data, conn->user, conn->passwd,
|
||||
service, conn->host.name,
|
||||
Curl_auth_create_gssapi_user_message(data, conn->creds,
|
||||
sasl->params->service,
|
||||
conn->origin->hostname,
|
||||
(bool)sasl->mutual_auth, NULL,
|
||||
krb5, &resp);
|
||||
newstate = SASL_GSSAPI_TOKEN;
|
||||
@@ -726,7 +706,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
|
||||
else if(sasl->mutual_auth) {
|
||||
/* Decode the user token challenge and create the optional response
|
||||
message */
|
||||
result = Curl_auth_create_gssapi_user_message(data, NULL, NULL,
|
||||
result = Curl_auth_create_gssapi_user_message(data, NULL,
|
||||
NULL, NULL,
|
||||
(bool)sasl->mutual_auth,
|
||||
&serverdata,
|
||||
@@ -735,10 +715,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
|
||||
}
|
||||
else
|
||||
/* Decode the security challenge and create the response message */
|
||||
result = Curl_auth_create_gssapi_security_message(data,
|
||||
conn->sasl_authzid,
|
||||
&serverdata,
|
||||
krb5, &resp);
|
||||
result = Curl_auth_create_gssapi_security_message(
|
||||
data, Curl_creds_sasl_authzid(conn->creds), &serverdata,
|
||||
krb5, &resp);
|
||||
}
|
||||
break;
|
||||
case SASL_GSSAPI_NO_DATA:
|
||||
@@ -749,10 +728,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
|
||||
if(!krb5)
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
else
|
||||
result = Curl_auth_create_gssapi_security_message(data,
|
||||
conn->sasl_authzid,
|
||||
&serverdata,
|
||||
krb5, &resp);
|
||||
result = Curl_auth_create_gssapi_security_message(
|
||||
data, Curl_creds_sasl_authzid(conn->creds), &serverdata,
|
||||
krb5, &resp);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
@@ -760,18 +738,16 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
|
||||
case SASL_OAUTH2:
|
||||
/* Create the authorization message */
|
||||
if(sasl->authused == SASL_MECH_OAUTHBEARER) {
|
||||
result = Curl_auth_create_oauth_bearer_message(conn->user,
|
||||
result = Curl_auth_create_oauth_bearer_message(conn->creds,
|
||||
hostname,
|
||||
port,
|
||||
oauth_bearer,
|
||||
&resp);
|
||||
|
||||
/* Failures maybe sent by the server as continuations for OAUTHBEARER */
|
||||
newstate = SASL_OAUTH2_RESP;
|
||||
}
|
||||
else
|
||||
result = Curl_auth_create_xoauth_bearer_message(conn->user,
|
||||
oauth_bearer,
|
||||
result = Curl_auth_create_xoauth_bearer_message(conn->creds,
|
||||
&resp);
|
||||
break;
|
||||
|
||||
@@ -861,7 +837,7 @@ static void sasl_unchosen(struct Curl_easy *data, unsigned short mech,
|
||||
else {
|
||||
if(param_missing)
|
||||
infof(data, "SASL: %s is missing %s", mname, param_missing);
|
||||
if(!data->conn->user[0])
|
||||
if(!Curl_creds_has_user(data->conn->creds))
|
||||
infof(data, "SASL: %s is missing username", mname);
|
||||
}
|
||||
}
|
||||
@@ -903,7 +879,8 @@ CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data)
|
||||
"auth mechanisms");
|
||||
else {
|
||||
infof(data, "SASL: no auth mechanism offered could be selected");
|
||||
if((enabledmechs & SASL_MECH_EXTERNAL) && data->conn->passwd[0])
|
||||
if((enabledmechs & SASL_MECH_EXTERNAL) &&
|
||||
Curl_creds_has_passwd(data->conn->creds))
|
||||
infof(data, "SASL: auth EXTERNAL not chosen with password");
|
||||
sasl_unchosen(data, SASL_MECH_GSSAPI, enabledmechs,
|
||||
CURL_SASL_KERBEROS5, Curl_auth_is_gssapi_supported(), NULL);
|
||||
@@ -918,10 +895,10 @@ CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data)
|
||||
sasl_unchosen(data, SASL_MECH_NTLM, enabledmechs,
|
||||
CURL_SASL_NTLM, Curl_auth_is_ntlm_supported(), NULL);
|
||||
sasl_unchosen(data, SASL_MECH_OAUTHBEARER, enabledmechs, TRUE, TRUE,
|
||||
data->set.str[STRING_BEARER] ?
|
||||
Curl_creds_has_oauth_bearer(data->conn->creds) ?
|
||||
NULL : "CURLOPT_XOAUTH2_BEARER");
|
||||
sasl_unchosen(data, SASL_MECH_XOAUTH2, enabledmechs, TRUE, TRUE,
|
||||
data->set.str[STRING_BEARER] ?
|
||||
Curl_creds_has_oauth_bearer(data->conn->creds) ?
|
||||
NULL : "CURLOPT_XOAUTH2_BEARER");
|
||||
}
|
||||
#endif /* CURLVERBOSE */
|
||||
|
||||
@@ -223,7 +223,7 @@
|
||||
/* please, do it beyond the point further indicated in this file. */
|
||||
/* ================================================================ */
|
||||
|
||||
/* Give calloc a chance to be dragging in early, so we do not redefine */
|
||||
/* Give calloc a chance to be included early, so we do not redefine */
|
||||
#ifdef HAVE_THREADS_POSIX
|
||||
# include <pthread.h>
|
||||
#endif
|
||||
@@ -465,7 +465,7 @@
|
||||
# undef HAVE_FCNTL
|
||||
# undef HAVE_FCNTL_O_NONBLOCK
|
||||
# else
|
||||
/* use libc networking and hence close() and fnctl() */
|
||||
/* use libc networking and hence close() and fcntl() */
|
||||
# undef HAVE_CLOSESOCKET_CAMEL
|
||||
# undef HAVE_IOCTLSOCKET_CAMEL
|
||||
# endif
|
||||
@@ -602,7 +602,7 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if (SIZEOF_CURL_OFF_T < 8)
|
||||
#if SIZEOF_CURL_OFF_T < 8
|
||||
#error "too small curl_off_t"
|
||||
#else
|
||||
/* assume SIZEOF_CURL_OFF_T == 8 */
|
||||
@@ -613,7 +613,7 @@
|
||||
#define FMT_OFF_T CURL_FORMAT_CURL_OFF_T
|
||||
#define FMT_OFF_TU CURL_FORMAT_CURL_OFF_TU
|
||||
|
||||
#if (SIZEOF_TIME_T == 4)
|
||||
#if SIZEOF_TIME_T == 4
|
||||
# ifdef HAVE_TIME_T_UNSIGNED
|
||||
# define TIME_T_MAX UINT_MAX
|
||||
# define TIME_T_MIN 0
|
||||
@@ -1152,6 +1152,15 @@ typedef unsigned int curl_bit;
|
||||
#define SOCKEWOULDBLOCK EWOULDBLOCK
|
||||
#endif
|
||||
|
||||
/* The socket error may be EWOULDBLOCK or on some systems EAGAIN when
|
||||
it returned due to its inability to send/read data without blocking.
|
||||
We treat both error codes the same here. */
|
||||
#if !defined(USE_WINSOCK) && EAGAIN != SOCKEWOULDBLOCK
|
||||
#define SOCK_EAGAIN(e) ((e) == SOCKEWOULDBLOCK || (e) == EAGAIN)
|
||||
#else
|
||||
#define SOCK_EAGAIN(e) ((e) == SOCKEWOULDBLOCK)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Macro argv_item_t hides platform details to code using it.
|
||||
*/
|
||||
@@ -1344,6 +1353,20 @@ extern curl_calloc_callback Curl_ccalloc;
|
||||
(ptr) = NULL; \
|
||||
} while(0)
|
||||
|
||||
/* Same as curlx_safefree() but zeroes memory before freeing */
|
||||
#define curlx_safefreezero(ptr, size) \
|
||||
do { \
|
||||
curlx_freezero(ptr, size); \
|
||||
(ptr) = NULL; \
|
||||
} while(0)
|
||||
|
||||
/* Same as curlx_safefreezero() but determines length with strlen() */
|
||||
#define curlx_safefreezeroz(ptr) \
|
||||
do { \
|
||||
curlx_freezeroz(ptr); \
|
||||
(ptr) = NULL; \
|
||||
} while(0)
|
||||
|
||||
#include <curl/curl.h> /* for CURL_EXTERN, curl_socket_t, mprintf.h */
|
||||
|
||||
#ifdef DEBUGBUILD
|
||||
@@ -1557,7 +1580,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
|
||||
#endif
|
||||
|
||||
#if defined(USE_UNIX_SOCKETS) && defined(_WIN32)
|
||||
/* Offered by mingw-w64 v10+. MS SDK 10.17763/~VS2017+. */
|
||||
/* Offered by mingw-w64 v10+, MS SDK 10.0.16299.0/VS2017 15.4+ */
|
||||
#if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR >= 10)
|
||||
# include <afunix.h>
|
||||
#elif !defined(UNIX_PATH_MAX) /* Replicate logic present in afunix.h */
|
||||
@@ -1593,7 +1616,7 @@ typedef struct sockaddr_un {
|
||||
/* The code is compiled with C++ compiler.
|
||||
C++ always supports 'inline'. */
|
||||
# define CURL_INLINE inline /* 'inline' keyword supported */
|
||||
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901
|
||||
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||
/* C99 (and later) supports 'inline' keyword */
|
||||
# define CURL_INLINE inline /* 'inline' keyword supported */
|
||||
#elif defined(__GNUC__) && __GNUC__ >= 3
|
||||
@@ -1623,4 +1646,43 @@ typedef struct sockaddr_un {
|
||||
#define NOVERBOSE(x) x
|
||||
#endif
|
||||
|
||||
/* For FreeBSD it is included from curl/curl.h */
|
||||
#if defined(__DragonFly__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
#include <sys/param.h> /* for __DragonFly_version, OpenBSD,
|
||||
__NetBSD_Version__ */
|
||||
#endif
|
||||
|
||||
#ifndef _CURL_LOCAL_MEMZERO /* to be removed after a couple of releases */
|
||||
#ifdef _WIN32
|
||||
#if defined(_MSC_VER) && defined(NTDDI_VERSION) && \
|
||||
(NTDDI_VERSION >= 0x0A000010) /* MS SDK 10.0.26100.0+ */
|
||||
#pragma comment(lib, "volatileaccessu.lib")
|
||||
#define curlx_memzero(buf, size) SecureZeroMemory2(buf, size)
|
||||
#else
|
||||
#define curlx_memzero(buf, size) SecureZeroMemory(buf, size)
|
||||
#endif
|
||||
#elif defined(HAVE_MEMSET_S)
|
||||
#define curlx_memzero(buf, size) (void)memset_s(buf, size, 0, size)
|
||||
#elif defined(HAVE_MEMSET_EXPLICIT)
|
||||
#define curlx_memzero(buf, size) (void)memset_explicit(buf, 0, size)
|
||||
#elif defined(__CYGWIN__) || \
|
||||
(defined(__NEWLIB__) && !defined(__CLIB2__)) || \
|
||||
(defined(__GLIBC__) && \
|
||||
(__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))) || \
|
||||
(defined(__DragonFly__) && __DragonFly_version >= 500600 /* v5.6+ */) || \
|
||||
(defined(__FreeBSD__) && __FreeBSD_version >= 1100037 /* v11.0+ */) || \
|
||||
(defined(__OpenBSD__) && OpenBSD >= 201405 /* v5.5+ */)
|
||||
#define curlx_memzero(buf, size) explicit_bzero(buf, size)
|
||||
#elif defined(__NetBSD__) && __NetBSD_Version__ >= 702000000 /* v7.2+ */
|
||||
#define curlx_memzero(buf, size) (void)explicit_memset(buf, 0, size)
|
||||
#endif
|
||||
#endif /* !_CURL_LOCAL_MEMZERO */
|
||||
|
||||
#ifndef curlx_memzero
|
||||
#define USE_CURLX_MEMZERO
|
||||
void curlx_memzero(void *buf, size_t size);
|
||||
#endif
|
||||
void curlx_freezero(void *buf, size_t size);
|
||||
void curlx_freezeroz(void *buf);
|
||||
|
||||
#endif /* HEADER_CURL_SETUP_H */
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
# define USE_OPENSSL_SHA512_256 1
|
||||
# define HAS_SHA512_256_IMPLEMENTATION 1
|
||||
# ifdef __NetBSD__
|
||||
/* Some NetBSD versions has a bug in SHA-512/256.
|
||||
/* Some NetBSD versions have a bug in SHA-512/256.
|
||||
* See https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039
|
||||
* The problematic versions:
|
||||
* - NetBSD before 9.4
|
||||
@@ -54,7 +54,6 @@
|
||||
* NetBSD 10.99.11 development.
|
||||
* It is safe to apply the workaround even if the bug is not present, as
|
||||
* the workaround reduces performance slightly. */
|
||||
# include <sys/param.h>
|
||||
# if __NetBSD_Version__ < 904000000 || \
|
||||
(__NetBSD_Version__ >= 999000000 && \
|
||||
__NetBSD_Version__ < 1000000000) || \
|
||||
@@ -75,7 +74,8 @@
|
||||
#endif
|
||||
|
||||
#if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_GNUTLS)
|
||||
# include <nettle/sha.h>
|
||||
# include <nettle/sha2.h>
|
||||
# include <nettle/version.h>
|
||||
# ifdef SHA512_256_DIGEST_SIZE
|
||||
# define USE_GNUTLS_SHA512_256 1
|
||||
# endif
|
||||
@@ -102,7 +102,7 @@
|
||||
typedef EVP_MD_CTX *Curl_sha512_256_ctx;
|
||||
|
||||
/**
|
||||
* Initialise structure for SHA-512/256 calculation.
|
||||
* Initialize structure for SHA-512/256 calculation.
|
||||
*
|
||||
* @param context the calculation context
|
||||
* @return CURLE_OK if succeed,
|
||||
@@ -145,7 +145,7 @@ static CURLcode Curl_sha512_256_update(void *context,
|
||||
Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context;
|
||||
|
||||
if(!EVP_DigestUpdate(*ctx, data, length))
|
||||
return CURLE_SSL_CIPHER;
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
@@ -168,14 +168,14 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context)
|
||||
/* Use a larger buffer to work around a bug in NetBSD:
|
||||
https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 */
|
||||
unsigned char tmp_digest[CURL_SHA512_256_DIGEST_SIZE * 2];
|
||||
result = EVP_DigestFinal_ex(*ctx,
|
||||
tmp_digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER;
|
||||
result = EVP_DigestFinal_ex(*ctx, tmp_digest, NULL) ?
|
||||
CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
if(result == CURLE_OK)
|
||||
memcpy(digest, tmp_digest, CURL_SHA512_256_DIGEST_SIZE);
|
||||
explicit_memset(tmp_digest, 0, sizeof(tmp_digest));
|
||||
curlx_memzero(tmp_digest, sizeof(tmp_digest));
|
||||
#else /* !NEED_NETBSD_SHA512_256_WORKAROUND */
|
||||
result = EVP_DigestFinal_ex(*ctx, digest, NULL) ?
|
||||
CURLE_OK : CURLE_SSL_CIPHER;
|
||||
CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
#endif /* NEED_NETBSD_SHA512_256_WORKAROUND */
|
||||
|
||||
EVP_MD_CTX_destroy(*ctx);
|
||||
@@ -206,7 +206,7 @@ static CURLcode Curl_sha512_256_update(void *ctx,
|
||||
do {
|
||||
word32 ilen = (word32)CURLMIN(length, UINT_MAX);
|
||||
if(wc_Sha512_256Update(ctx, data, ilen))
|
||||
return CURLE_SSL_CIPHER;
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
length -= ilen;
|
||||
data += ilen;
|
||||
} while(length);
|
||||
@@ -216,7 +216,7 @@ static CURLcode Curl_sha512_256_update(void *ctx,
|
||||
static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *ctx)
|
||||
{
|
||||
if(wc_Sha512_256Final(ctx, digest))
|
||||
return CURLE_SSL_CIPHER;
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *ctx)
|
||||
typedef struct sha512_256_ctx Curl_sha512_256_ctx;
|
||||
|
||||
/**
|
||||
* Initialise structure for SHA-512/256 calculation.
|
||||
* Initialize structure for SHA-512/256 calculation.
|
||||
*
|
||||
* @param context the calculation context
|
||||
* @return always CURLE_OK
|
||||
@@ -262,7 +262,7 @@ static CURLcode Curl_sha512_256_update(void *context,
|
||||
{
|
||||
Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context;
|
||||
|
||||
DEBUGASSERT((data != NULL) || (length == 0));
|
||||
DEBUGASSERT(data || (length == 0));
|
||||
|
||||
sha512_256_update(ctx, length, (const uint8_t *)data);
|
||||
|
||||
@@ -281,8 +281,12 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context)
|
||||
{
|
||||
Curl_sha512_256_ctx * const ctx = (Curl_sha512_256_ctx *)context;
|
||||
|
||||
#if NETTLE_VERSION_MAJOR >= 4
|
||||
sha512_256_digest(ctx, (uint8_t *)digest);
|
||||
#else
|
||||
sha512_256_digest(ctx,
|
||||
(size_t)CURL_SHA512_256_DIGEST_SIZE, (uint8_t *)digest);
|
||||
#endif
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
@@ -296,7 +300,7 @@ static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context)
|
||||
|
||||
#ifdef __GNUC__
|
||||
# if defined(__has_attribute) && defined(__STDC_VERSION__)
|
||||
# if __has_attribute(always_inline) && __STDC_VERSION__ >= 199901
|
||||
# if __has_attribute(always_inline) && __STDC_VERSION__ >= 199901L
|
||||
# define CURL_FORCEINLINE CURL_INLINE __attribute__((always_inline))
|
||||
# endif
|
||||
# endif
|
||||
@@ -430,7 +434,7 @@ struct Curl_sha512_256ctx {
|
||||
typedef struct Curl_sha512_256ctx Curl_sha512_256_ctx;
|
||||
|
||||
/**
|
||||
* Initialise structure for SHA-512/256 calculation.
|
||||
* Initialize structure for SHA-512/256 calculation.
|
||||
*
|
||||
* @param context the calculation context
|
||||
* @return always CURLE_OK
|
||||
@@ -456,7 +460,7 @@ static CURLcode Curl_sha512_256_init(void *context)
|
||||
ctx->H[6] = UINT64_C(0x2B0199FC2C85B8AA);
|
||||
ctx->H[7] = UINT64_C(0x0EB72DDC81C52CA2);
|
||||
|
||||
/* Initialise number of bytes and high part of number of bits. */
|
||||
/* Initialize number of bytes and high part of number of bits. */
|
||||
ctx->count = UINT64_C(0);
|
||||
ctx->count_bits_hi = UINT64_C(0);
|
||||
|
||||
@@ -494,14 +498,10 @@ static void Curl_sha512_256_transform(uint64_t H[SHA512_256_HASH_SIZE_WORDS],
|
||||
|
||||
/* Four 'Sigma' macro functions.
|
||||
See FIPS PUB 180-4 formulae 4.10, 4.11, 4.12, 4.13. */
|
||||
#define SIG0(x) \
|
||||
(Curl_rotr64(x, 28) ^ Curl_rotr64(x, 34) ^ Curl_rotr64(x, 39))
|
||||
#define SIG1(x) \
|
||||
(Curl_rotr64(x, 14) ^ Curl_rotr64(x, 18) ^ Curl_rotr64(x, 41))
|
||||
#define sig0(x) \
|
||||
(Curl_rotr64(x, 1) ^ Curl_rotr64(x, 8) ^ ((x) >> 7))
|
||||
#define sig1(x) \
|
||||
(Curl_rotr64(x, 19) ^ Curl_rotr64(x, 61) ^ ((x) >> 6))
|
||||
#define SIG0(x) (Curl_rotr64(x, 28) ^ Curl_rotr64(x, 34) ^ Curl_rotr64(x, 39))
|
||||
#define SIG1(x) (Curl_rotr64(x, 14) ^ Curl_rotr64(x, 18) ^ Curl_rotr64(x, 41))
|
||||
#define sig0(x) (Curl_rotr64(x, 1) ^ Curl_rotr64(x, 8) ^ ((x) >> 7))
|
||||
#define sig1(x) (Curl_rotr64(x, 19) ^ Curl_rotr64(x, 61) ^ ((x) >> 6))
|
||||
|
||||
if(1) {
|
||||
unsigned int t;
|
||||
@@ -645,7 +645,7 @@ static CURLcode Curl_sha512_256_update(void *context,
|
||||
/* the void pointer here is required to mute Intel compiler warning */
|
||||
void * const ctx_buf = ctx->buffer;
|
||||
|
||||
DEBUGASSERT((data != NULL) || (length == 0));
|
||||
DEBUGASSERT(data || (length == 0));
|
||||
|
||||
if(length == 0)
|
||||
return CURLE_OK; /* Shortcut, do nothing */
|
||||
@@ -711,7 +711,7 @@ static CURLcode Curl_sha512_256_update(void *context,
|
||||
static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context)
|
||||
{
|
||||
struct Curl_sha512_256ctx * const ctx = (struct Curl_sha512_256ctx *)context;
|
||||
uint64_t num_bits; /* Number of processed bits */
|
||||
uint64_t num_bits; /* Number of processed bits */
|
||||
unsigned int bytes_have; /* Number of bytes in the context buffer */
|
||||
/* the void pointer here is required to mute Intel compiler warning */
|
||||
void * const ctx_buf = ctx->buffer;
|
||||
|
||||
@@ -260,7 +260,7 @@ CURLSHcode curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
|
||||
|
||||
case CURL_LOCK_DATA_CONNECT:
|
||||
/* It is safe to set this option several times on a share. */
|
||||
if(!share->cpool.initialised) {
|
||||
if(!share->cpool.initialized) {
|
||||
Curl_cpool_init(&share->cpool, share->admin, share, 103);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -81,9 +81,9 @@ CURLSHcode Curl_share_lock(struct Curl_easy *data, curl_lock_data type,
|
||||
CURLSHcode Curl_share_unlock(struct Curl_easy *data, curl_lock_data type);
|
||||
|
||||
/* convenience macro to check if this handle is using a shared SSL spool */
|
||||
#define CURL_SHARE_ssl_scache(data) ((data)->share && \
|
||||
((data)->share->specifier & \
|
||||
(1 << CURL_LOCK_DATA_SSL_SESSION)))
|
||||
#define CURL_SHARE_ssl_scache(data) \
|
||||
((data)->share && \
|
||||
((data)->share->specifier & (1 << CURL_LOCK_DATA_SSL_SESSION)))
|
||||
|
||||
CURLcode Curl_share_easy_unlink(struct Curl_easy *data);
|
||||
CURLcode Curl_share_easy_link(struct Curl_easy *data,
|
||||
|
||||
@@ -52,25 +52,25 @@ extern PSecurityFunctionTable Curl_pSecFn;
|
||||
#define SP_NAME_NEGOTIATE "Negotiate"
|
||||
#define SP_NAME_KERBEROS "Kerberos"
|
||||
|
||||
/* Offered by mingw-w64 v9+. MS SDK 7.0A+. */
|
||||
/* Offered by mingw-w64 v9+, MS SDK 7.0A/VS2010+ */
|
||||
#ifndef ISC_REQ_USE_HTTP_STYLE
|
||||
#define ISC_REQ_USE_HTTP_STYLE 0x01000000
|
||||
#endif
|
||||
|
||||
/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */
|
||||
/* Offered by mingw-w64 v8+, MS SDK 6.0A/VS2008+ */
|
||||
#ifndef SEC_E_INVALID_PARAMETER
|
||||
#define SEC_E_INVALID_PARAMETER ((HRESULT)0x8009035DL)
|
||||
#endif
|
||||
/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */
|
||||
/* Offered by mingw-w64 v8+, MS SDK 6.0A/VS2008+ */
|
||||
#ifndef SEC_E_DELEGATION_POLICY
|
||||
#define SEC_E_DELEGATION_POLICY ((HRESULT)0x8009035EL)
|
||||
#endif
|
||||
/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */
|
||||
/* Offered by mingw-w64 v8+, MS SDK 6.0A/VS2008+ */
|
||||
#ifndef SEC_E_POLICY_NLTM_ONLY
|
||||
#define SEC_E_POLICY_NLTM_ONLY ((HRESULT)0x8009035FL)
|
||||
#endif
|
||||
|
||||
/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */
|
||||
/* Offered by mingw-w64 v8+, MS SDK 6.0A/VS2008+ */
|
||||
#ifndef SEC_I_SIGNATURE_NEEDED
|
||||
#define SEC_I_SIGNATURE_NEEDED ((HRESULT)0x0009035CL)
|
||||
#endif
|
||||
|
||||
@@ -29,8 +29,9 @@
|
||||
#include "multiif.h"
|
||||
|
||||
#include "cf-dns.h"
|
||||
#include "cf-recvbuf.h"
|
||||
#include "cf-socket.h"
|
||||
#include "connect.h"
|
||||
#include "cf-setup.h"
|
||||
#include "http2.h"
|
||||
#include "http_proxy.h"
|
||||
#include "cf-h1-proxy.h"
|
||||
@@ -563,6 +564,9 @@ static struct trc_cft_def trc_cfts[] = {
|
||||
{ &Curl_cft_unix, TRC_CT_NETWORK },
|
||||
{ &Curl_cft_tcp_accept, TRC_CT_NETWORK },
|
||||
{ &Curl_cft_ip_happy, TRC_CT_NETWORK },
|
||||
#ifndef CURL_DISABLE_WEBSOCKETS
|
||||
{ &Curl_cft_recvbuf, TRC_CT_PROTOCOL },
|
||||
#endif
|
||||
{ &Curl_cft_setup, TRC_CT_PROTOCOL },
|
||||
#if !defined(CURL_DISABLE_HTTP) && defined(USE_NGHTTP2)
|
||||
{ &Curl_cft_nghttp2, TRC_CT_PROTOCOL },
|
||||
@@ -578,6 +582,9 @@ static struct trc_cft_def trc_cfts[] = {
|
||||
{ &Curl_cft_h1_proxy, TRC_CT_PROXY },
|
||||
#ifdef USE_NGHTTP2
|
||||
{ &Curl_cft_h2_proxy, TRC_CT_PROXY },
|
||||
#endif
|
||||
#if defined(USE_PROXY_HTTP3) && defined(USE_NGHTTP3)
|
||||
{ &Curl_cft_h3_proxy, TRC_CT_PROXY },
|
||||
#endif
|
||||
{ &Curl_cft_http_proxy, TRC_CT_PROXY },
|
||||
#endif /* !CURL_DISABLE_HTTP */
|
||||
|
||||
@@ -80,7 +80,7 @@ static wchar_t *fn_convert_UTF8_to_wchar(const char *str_utf8)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* declare GetFullPathNameW for mingw-w64 UWP builds targeting old windows */
|
||||
/* declare GetFullPathNameW for mingw-w64 UWP builds targeting old Windows */
|
||||
#if defined(CURL_WINDOWS_UWP) && defined(__MINGW32__) && \
|
||||
(_WIN32_WINNT < _WIN32_WINNT_WIN10)
|
||||
WINBASEAPI DWORD WINAPI GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR *);
|
||||
@@ -247,7 +247,7 @@ cleanup:
|
||||
CURLX_FREE(ibuf);
|
||||
CURLX_FREE(obuf);
|
||||
#endif
|
||||
return *out ? true : false;
|
||||
return !!*out;
|
||||
}
|
||||
|
||||
#ifndef CURL_WINDOWS_UWP
|
||||
|
||||
@@ -30,10 +30,10 @@
|
||||
*
|
||||
* Provide the target buffer @dest and size of the target buffer @dsize, If
|
||||
* the source string @src with its *string length* @slen fits in the target
|
||||
* buffer it is copied there - including storing a null terminator.
|
||||
* buffer it is copied there - including storing a null-terminator.
|
||||
*
|
||||
* If the target buffer is too small, the copy is not performed but if the
|
||||
* target buffer has a non-zero size it gets a null terminator stored.
|
||||
* target buffer has a non-zero size it gets a null-terminator stored.
|
||||
*/
|
||||
void curlx_strcopy(char *dest, /* destination buffer */
|
||||
size_t dsize, /* size of target buffer */
|
||||
|
||||
@@ -94,3 +94,32 @@ void *curlx_memdup0(const char *src, size_t length)
|
||||
buf[length] = 0;
|
||||
return buf;
|
||||
}
|
||||
|
||||
#ifdef USE_CURLX_MEMZERO
|
||||
static void *(* const volatile p_curlx_memset)(void *buf, int val,
|
||||
size_t size) = memset;
|
||||
|
||||
/* Local fallback in case there is no system function to securely zero a memory
|
||||
buffer. */
|
||||
void curlx_memzero(void *buf, size_t size)
|
||||
{
|
||||
if(buf)
|
||||
p_curlx_memset(buf, 0, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Free 'buf' after zeroing its content. */
|
||||
void curlx_freezero(void *buf, size_t size)
|
||||
{
|
||||
if(buf)
|
||||
curlx_memzero(buf, size);
|
||||
curlx_free(buf);
|
||||
}
|
||||
|
||||
/* Free 'buf' after zeroing its content, where 'buf' is null-terminated. */
|
||||
void curlx_freezeroz(void *buf)
|
||||
{
|
||||
if(buf)
|
||||
curlx_memzero(buf, strlen(buf));
|
||||
curlx_free(buf);
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ static const char *get_winsock_error(int err, char *buf, size_t len)
|
||||
p = "Winsock library is not ready";
|
||||
break;
|
||||
case WSANOTINITIALISED:
|
||||
p = "Winsock library not initialised";
|
||||
p = "Winsock library not initialized";
|
||||
break;
|
||||
case WSAVERNOTSUPPORTED:
|
||||
p = "Winsock version not supported";
|
||||
@@ -268,7 +268,7 @@ const char *curlx_strerror(int err, char *buf, size_t buflen)
|
||||
!get_winsock_error(err, buf, buflen) &&
|
||||
#endif
|
||||
!curlx_get_winapi_error((DWORD)err, buf, buflen))
|
||||
SNPRINTF(buf, buflen, "Unknown error %d (%#x)", err, err);
|
||||
SNPRINTF(buf, buflen, "Unknown error %d (%#x)", err, (unsigned int)err);
|
||||
#else /* !_WIN32 */
|
||||
|
||||
#if defined(HAVE_STRERROR_R) && defined(HAVE_POSIX_STRERROR_R)
|
||||
|
||||
@@ -45,13 +45,17 @@ void curlx_str_trim(struct Curl_str *out, size_t len)
|
||||
}
|
||||
|
||||
/* Get a word until the first DELIM or end of string. At least one byte long.
|
||||
return non-zero on error */
|
||||
return non-zero on error. If 'max' is zero, it will always return error. */
|
||||
int curlx_str_until(const char **linep, struct Curl_str *out,
|
||||
const size_t max, char delim)
|
||||
{
|
||||
const char *s = *linep;
|
||||
const char *s;
|
||||
size_t len = 0;
|
||||
DEBUGASSERT(linep && *linep && out && max && delim);
|
||||
DEBUGASSERT(linep);
|
||||
DEBUGASSERT(*linep);
|
||||
DEBUGASSERT(out);
|
||||
DEBUGASSERT(delim);
|
||||
s = *linep;
|
||||
|
||||
curlx_str_init(out);
|
||||
while(*s && (*s != delim)) {
|
||||
|
||||
@@ -195,7 +195,7 @@ static CURLcode cw_out_cb_write(struct cw_out_ctx *ctx,
|
||||
if(nwritten == CURL_WRITEFUNC_PAUSE) {
|
||||
if(data->conn->scheme->flags & PROTOPT_NONETWORK) {
|
||||
/* Protocols that work without network cannot be paused. This is
|
||||
actually only FILE:// now, and it cannot pause since the transfer is
|
||||
actually only file:// now, and it cannot pause since the transfer is
|
||||
not done using the "normal" procedure. */
|
||||
failf(data, "Write callback asked for PAUSE when not supported");
|
||||
return CURLE_WRITE_ERROR;
|
||||
|
||||
@@ -117,7 +117,8 @@ static CURLcode cw_pause_flush(struct Curl_easy *data,
|
||||
result = Curl_cwriter_write(data, cw_pause->next, (*plast)->type,
|
||||
(const char *)buf, wlen);
|
||||
CURL_TRC_WRITE(data, "[PAUSE] flushed %zu/%zu bytes, type=%x -> %d",
|
||||
wlen, ctx->buf_total, (*plast)->type, result);
|
||||
wlen, ctx->buf_total, (unsigned int)(*plast)->type,
|
||||
(int)result);
|
||||
Curl_bufq_skip(&(*plast)->b, wlen);
|
||||
DEBUGASSERT(ctx->buf_total >= wlen);
|
||||
ctx->buf_total -= wlen;
|
||||
@@ -128,7 +129,8 @@ static CURLcode cw_pause_flush(struct Curl_easy *data,
|
||||
result = Curl_cwriter_write(data, cw_pause->next, (*plast)->type,
|
||||
(const char *)buf, 0);
|
||||
CURL_TRC_WRITE(data, "[PAUSE] flushed 0/%zu bytes, type=%x -> %d",
|
||||
ctx->buf_total, (*plast)->type, result);
|
||||
ctx->buf_total, (unsigned int)(*plast)->type,
|
||||
(int)result);
|
||||
}
|
||||
|
||||
if(Curl_bufq_is_empty(&(*plast)->b)) {
|
||||
@@ -165,7 +167,7 @@ static CURLcode cw_pause_write(struct Curl_easy *data,
|
||||
wtype &= ~CLIENTWRITE_EOS;
|
||||
result = Curl_cwriter_write(data, writer->next, wtype, buf, wlen);
|
||||
CURL_TRC_WRITE(data, "[PAUSE] writing %zu/%zu bytes of type %x -> %d",
|
||||
wlen, blen, wtype, result);
|
||||
wlen, blen, (unsigned int)wtype, (int)result);
|
||||
if(result)
|
||||
return result;
|
||||
buf += wlen;
|
||||
@@ -191,8 +193,8 @@ static CURLcode cw_pause_write(struct Curl_easy *data,
|
||||
result = Curl_bufq_cwrite(&ctx->buf->b, buf, blen, &nwritten);
|
||||
}
|
||||
CURL_TRC_WRITE(data, "[PAUSE] buffer %zu more bytes of type %x, "
|
||||
"total=%zu -> %d", nwritten, type, ctx->buf_total + wlen,
|
||||
result);
|
||||
"total=%zu -> %d", nwritten, (unsigned int)type,
|
||||
ctx->buf_total + wlen, (int)result);
|
||||
if(result)
|
||||
return result;
|
||||
buf += nwritten;
|
||||
|
||||
@@ -148,7 +148,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
|
||||
|
||||
*done = TRUE; /* unconditionally */
|
||||
|
||||
/* url-decode path before further evaluation */
|
||||
/* URL-decode path before further evaluation */
|
||||
result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
#if defined(_WIN32) && !defined(CURL_STATICLIB)
|
||||
|
||||
#if defined(USE_OPENSSL) && \
|
||||
!defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) && \
|
||||
!defined(OPENSSL_IS_AWSLC) && !defined(OPENSSL_IS_BORINGSSL) && \
|
||||
!defined(LIBRESSL_VERSION_NUMBER)
|
||||
#define PREVENT_OPENSSL_MEMLEAK
|
||||
#endif
|
||||
|
||||
@@ -351,7 +351,7 @@ UNITTEST CURLcode dns_shuffle_addr(struct Curl_easy *data,
|
||||
|
||||
if(num_addrs > 1) {
|
||||
struct Curl_addrinfo **nodes;
|
||||
CURL_TRC_DNS(data, "Shuffling %i addresses", num_addrs);
|
||||
CURL_TRC_DNS(data, "Shuffling %d addresses", num_addrs);
|
||||
|
||||
nodes = curlx_malloc(num_addrs * sizeof(*nodes));
|
||||
if(nodes) {
|
||||
@@ -616,20 +616,6 @@ CURLcode Curl_dnscache_add_negative(struct Curl_easy *data,
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
struct Curl_dns_entry *Curl_dns_entry_link(struct Curl_easy *data,
|
||||
struct Curl_dns_entry *dns)
|
||||
{
|
||||
if(!dns)
|
||||
return NULL;
|
||||
else {
|
||||
struct Curl_dnscache *dnscache = dnscache_get(data);
|
||||
dnscache_lock(data, dnscache);
|
||||
dns->refcount++;
|
||||
dnscache_unlock(data, dnscache);
|
||||
return dns;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Curl_dns_entry_unlink() releases a reference to the given cached DNS entry.
|
||||
* When the reference count reaches 0, the entry is destroyed. It is important
|
||||
@@ -789,7 +775,7 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
|
||||
|
||||
result = Curl_str2addr(address, port, &ai);
|
||||
if(result) {
|
||||
infof(data, "Resolve address '%s' found illegal", address);
|
||||
infof(data, "Resolve IP address '%s' found is illegal", address);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -842,7 +828,7 @@ err:
|
||||
Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
|
||||
}
|
||||
|
||||
/* put this new host in the cache, an overridy for ALL dns queries */
|
||||
/* put this new host in the cache, an override for ALL dns queries */
|
||||
dns = dnscache_add_addr(data, dnscache, CURL_DNSQ_ALL,
|
||||
&head, curlx_str(&source),
|
||||
curlx_strlen(&source), port, permanent);
|
||||
|
||||
@@ -80,11 +80,6 @@ void Curl_dns_entry_set_https_rr(struct Curl_dns_entry *dns,
|
||||
struct Curl_https_rrinfo *hinfo);
|
||||
#endif /* USE_HTTPSRR */
|
||||
|
||||
/* Increase the ref counter and return it for storing in another place.
|
||||
* May be called with NULL, in which case it returns NULL. */
|
||||
struct Curl_dns_entry *Curl_dns_entry_link(struct Curl_easy *data,
|
||||
struct Curl_dns_entry *dns);
|
||||
|
||||
/* unlink a dns entry, frees all resources if it was the last reference.
|
||||
* Always clears `*pdns`` */
|
||||
void Curl_dns_entry_unlink(struct Curl_easy *data,
|
||||
|
||||
@@ -259,7 +259,6 @@ static void doh_probe_done(struct Curl_easy *data,
|
||||
result = curlx_dyn_addn(&dohp->probe_resp[i].body,
|
||||
curlx_dyn_ptr(&doh_req->resp_body),
|
||||
curlx_dyn_len(&doh_req->resp_body));
|
||||
curlx_dyn_free(&doh_req->resp_body);
|
||||
}
|
||||
Curl_meta_remove(doh, CURL_EZM_DOH_PROBE);
|
||||
|
||||
@@ -319,7 +318,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data,
|
||||
sizeof(doh_req->req_body),
|
||||
&doh_req->req_body_len);
|
||||
if(d) {
|
||||
failf(data, "Failed to encode DoH packet [%d]", d);
|
||||
failf(data, "Failed to encode DoH packet [%d]", (int)d);
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto error;
|
||||
}
|
||||
@@ -418,7 +417,8 @@ static CURLcode doh_probe_run(struct Curl_easy *data,
|
||||
}
|
||||
|
||||
(void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS,
|
||||
(long)data->set.ssl.primary.ssl_options);
|
||||
((long)data->set.ssl.primary.ssl_options &
|
||||
~CURLSSLOPT_AUTO_CLIENT_CERT));
|
||||
|
||||
doh->state.internal = TRUE;
|
||||
doh->master_mid = data->mid; /* master transfer of this one */
|
||||
@@ -724,7 +724,9 @@ UNITTEST void de_init(struct dohentry *de)
|
||||
curlx_dyn_init(&de->cname[i], DYN_DOH_CNAME);
|
||||
}
|
||||
|
||||
/* @unittest 1655 */
|
||||
/* TTL value cap */
|
||||
#define MAX_DNS_TTL 86400U /* 24 hours */
|
||||
/* @unittest 1650 */
|
||||
UNITTEST DOHcode doh_resp_decode(const unsigned char *doh,
|
||||
size_t dohlen,
|
||||
DNStype dnstype,
|
||||
@@ -794,6 +796,8 @@ UNITTEST DOHcode doh_resp_decode(const unsigned char *doh,
|
||||
return DOH_DNS_OUT_OF_RANGE;
|
||||
|
||||
ttl = doh_get32bit(doh, index);
|
||||
if(ttl > MAX_DNS_TTL)
|
||||
ttl = MAX_DNS_TTL;
|
||||
if(ttl < d->ttl)
|
||||
d->ttl = ttl;
|
||||
index += 4;
|
||||
@@ -1087,10 +1091,14 @@ static CURLcode doh_decode_rdata_name(const unsigned char **buf,
|
||||
DEBUGASSERT(buf && remaining && dnsname);
|
||||
if(!buf || !remaining || !dnsname || !*remaining)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
curlx_dyn_init(&thename, CURL_MAXLEN_host_name);
|
||||
curlx_dyn_init(&thename, CURL_MAXLEN_HOST_NAME);
|
||||
rem = *remaining;
|
||||
cp = *buf;
|
||||
clen = *cp++;
|
||||
/* RFC 9460 says it must be uncompressed */
|
||||
if(clen > 63)
|
||||
return CURLE_WEIRD_SERVER_REPLY;
|
||||
|
||||
if(clen == 0) {
|
||||
/* special case - return "." as name */
|
||||
if(curlx_dyn_addn(&thename, ".", 1))
|
||||
@@ -1112,6 +1120,11 @@ static CURLcode doh_decode_rdata_name(const unsigned char **buf,
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
clen = *cp++;
|
||||
if(clen > 63) {
|
||||
/* invalid format */
|
||||
curlx_dyn_free(&thename);
|
||||
return CURLE_WEIRD_SERVER_REPLY;
|
||||
}
|
||||
}
|
||||
*buf = cp;
|
||||
*remaining = rem - 1;
|
||||
|
||||
@@ -167,7 +167,6 @@ void Curl_doh_cleanup(struct Curl_easy *data,
|
||||
struct Curl_resolv_async *async);
|
||||
#define Curl_doh_wanted(d) (!!(d)->set.doh)
|
||||
|
||||
|
||||
#else /* CURL_DISABLE_DOH */
|
||||
#define Curl_doh(a, b) NULL
|
||||
#define Curl_doh_take_result(x, y, z) CURLE_COULDNT_RESOLVE_HOST
|
||||
|
||||
@@ -78,8 +78,8 @@
|
||||
#include "easy_lock.h"
|
||||
|
||||
/* true globals -- for curl_global_init() and curl_global_cleanup() */
|
||||
static unsigned int initialized;
|
||||
static long easy_init_flags;
|
||||
static unsigned int initialized;
|
||||
static long easy_init_flags;
|
||||
|
||||
#ifdef GLOBAL_INIT_IS_THREADSAFE
|
||||
|
||||
@@ -942,7 +942,7 @@ static void dupeasy_meta_freeentry(void *p)
|
||||
/* Always FALSE. Cannot use a 0 assert here since compilers
|
||||
* are not in agreement if they then want a NORETURN attribute or
|
||||
* not. *sigh* */
|
||||
DEBUGASSERT(p == NULL);
|
||||
DEBUGASSERT(!p);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1052,6 +1052,11 @@ CURL *curl_easy_duphandle(CURL *curl)
|
||||
(void)Curl_hsts_loadfile(outcurl,
|
||||
outcurl->hsts, outcurl->set.str[STRING_HSTS]);
|
||||
(void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
|
||||
|
||||
/* Copy entries learned at runtime. (E.g. Strict-Transport-Security
|
||||
headers.) */
|
||||
if(Curl_hsts_copy(outcurl->hsts, data->hsts))
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ static struct addrinfo *mk_getaddrinfo(const struct ares_addrinfo *aihead)
|
||||
const char *name = aihead->name;
|
||||
|
||||
/* traverse the addrinfo list */
|
||||
for(ai = aihead->nodes; ai != NULL; ai = ai->ai_next) {
|
||||
for(ai = aihead->nodes; ai; ai = ai->ai_next) {
|
||||
size_t ss_size;
|
||||
size_t namelen = name ? strlen(name) + 1 : 0;
|
||||
/* ignore elements with unsupported address family,
|
||||
|
||||
@@ -154,7 +154,7 @@ static void AddFormInfo(struct FormInfo *form_info, struct FormInfo *parent)
|
||||
|
||||
static void free_formlist(struct FormInfo *ptr)
|
||||
{
|
||||
for(; ptr != NULL; ptr = ptr->more) {
|
||||
for(; ptr; ptr = ptr->more) {
|
||||
Curl_bufref_free(&ptr->name);
|
||||
Curl_bufref_free(&ptr->value);
|
||||
Curl_bufref_free(&ptr->contenttype);
|
||||
@@ -184,7 +184,7 @@ static void free_formlist(struct FormInfo *ptr)
|
||||
*
|
||||
* name/value pair where only the content pointer is remembered:
|
||||
* curl_formadd(&post, &last, CURLFORM_COPYNAME, "name",
|
||||
* CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10,
|
||||
* CURLFORM_PTRCONTENTS, ptr, CURLFORM_CONTENTSLENGTH, 10L,
|
||||
* CURLFORM_END);
|
||||
* (if CURLFORM_CONTENTSLENGTH is missing strlen () is used)
|
||||
*
|
||||
@@ -223,7 +223,7 @@ static CURLFORMcode FormAddCheck(struct FormInfo *first_form,
|
||||
/* go through the list, check for completeness and if everything is
|
||||
* alright add the HttpPost item otherwise set retval accordingly */
|
||||
|
||||
for(form = first_form; form != NULL; form = form->more) {
|
||||
for(form = first_form; form; form = form->more) {
|
||||
const char *name = Curl_bufref_ptr(&form->name);
|
||||
|
||||
if(((!name || !Curl_bufref_ptr(&form->value)) && !post) ||
|
||||
|
||||
@@ -113,10 +113,10 @@ struct ftp_conn {
|
||||
char *account;
|
||||
char *alternative_to_user;
|
||||
char *entrypath; /* the PWD reply when we logged on */
|
||||
const char *file; /* url-decoded filename (or path), points into rawpath */
|
||||
const char *file; /* URL-decoded filename (or path), points into rawpath */
|
||||
char *rawpath; /* URL decoded, allocated, version of the path */
|
||||
struct pathcomp *dirs; /* allocated array for path components */
|
||||
char *prevpath; /* url-decoded conn->path from the previous transfer */
|
||||
char *prevpath; /* URL-decoded conn->path from the previous transfer */
|
||||
char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a
|
||||
and others (A/I or zero) */
|
||||
char *server_os; /* The target server operating system. */
|
||||
|
||||
+152
-153
@@ -355,7 +355,6 @@ static void close_secondarysocket(struct Curl_easy *data,
|
||||
{
|
||||
(void)ftpc;
|
||||
CURL_TRC_FTP(data, "[%s] closing DATA connection", FTP_CSTATE(ftpc));
|
||||
Curl_conn_close(data, SECONDARYSOCKET);
|
||||
Curl_conn_cf_discard_all(data, data->conn, SECONDARYSOCKET);
|
||||
}
|
||||
|
||||
@@ -733,7 +732,7 @@ static CURLcode getftpresponse(struct Curl_easy *data,
|
||||
|
||||
pp->pending_resp = FALSE;
|
||||
CURL_TRC_FTP(data, "getftpresponse -> result=%d, nread=%zu, ftpcode=%d",
|
||||
result, *nreadp, *ftpcodep);
|
||||
(int)result, *nreadp, *ftpcodep);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -743,7 +742,7 @@ static CURLcode ftp_state_user(struct Curl_easy *data,
|
||||
struct connectdata *conn)
|
||||
{
|
||||
CURLcode result = Curl_pp_sendf(data, &ftpc->pp, "USER %s",
|
||||
conn->user ? conn->user : "");
|
||||
Curl_creds_user(conn->creds));
|
||||
if(!result) {
|
||||
ftpc->ftp_trying_alternative = FALSE;
|
||||
ftp_state(data, ftpc, FTP_USER);
|
||||
@@ -1084,7 +1083,7 @@ static CURLcode ftp_port_open_socket(struct Curl_easy *data,
|
||||
curl_socket_t *portsockp)
|
||||
{
|
||||
char buffer[STRERROR_LEN];
|
||||
int error = 0;
|
||||
int sockerr = 0;
|
||||
const struct Curl_addrinfo *ai;
|
||||
CURLcode result = CURLE_FTP_PORT_FAILED;
|
||||
|
||||
@@ -1096,14 +1095,14 @@ static CURLcode ftp_port_open_socket(struct Curl_easy *data,
|
||||
if(result == CURLE_OUT_OF_MEMORY)
|
||||
return result;
|
||||
result = CURLE_FTP_PORT_FAILED;
|
||||
error = SOCKERRNO;
|
||||
sockerr = SOCKERRNO;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if(!ai) {
|
||||
failf(data, "socket failure: %s",
|
||||
curlx_strerror(error, buffer, sizeof(buffer)));
|
||||
curlx_strerror(sockerr, buffer, sizeof(buffer)));
|
||||
return CURLE_FTP_PORT_FAILED;
|
||||
}
|
||||
*aip = ai;
|
||||
@@ -1132,7 +1131,7 @@ static CURLcode ftp_port_bind_socket(struct Curl_easy *data,
|
||||
#endif
|
||||
char buffer[STRERROR_LEN];
|
||||
unsigned short port;
|
||||
int error;
|
||||
int sockerr;
|
||||
|
||||
memcpy(sa, ai->ai_addr, ai->ai_addrlen);
|
||||
*sslen_io = ai->ai_addrlen;
|
||||
@@ -1145,13 +1144,13 @@ static CURLcode ftp_port_bind_socket(struct Curl_easy *data,
|
||||
sa6->sin6_port = htons(port);
|
||||
#endif
|
||||
if(bind(portsock, sa, *sslen_io)) {
|
||||
error = SOCKERRNO;
|
||||
if(non_local && (error == SOCKEADDRNOTAVAIL)) {
|
||||
sockerr = SOCKERRNO;
|
||||
if(non_local && (sockerr == SOCKEADDRNOTAVAIL)) {
|
||||
/* The requested bind address is not local. Use the address used for
|
||||
* the control connection instead and restart the port loop.
|
||||
*/
|
||||
infof(data, "bind(port=%hu) on non-local address failed: %s", port,
|
||||
curlx_strerror(error, buffer, sizeof(buffer)));
|
||||
curlx_strerror(sockerr, buffer, sizeof(buffer)));
|
||||
|
||||
*sslen_io = sizeof(*ss);
|
||||
if(getsockname(conn->sock[FIRSTSOCKET], sa, sslen_io)) {
|
||||
@@ -1163,9 +1162,9 @@ static CURLcode ftp_port_bind_socket(struct Curl_easy *data,
|
||||
non_local = FALSE; /* do not try this again */
|
||||
continue;
|
||||
}
|
||||
if(error != SOCKEADDRINUSE && error != SOCKEACCES) {
|
||||
if(sockerr != SOCKEADDRINUSE && sockerr != SOCKEACCES) {
|
||||
failf(data, "bind(port=%hu) failed: %s", port,
|
||||
curlx_strerror(error, buffer, sizeof(buffer)));
|
||||
curlx_strerror(sockerr, buffer, sizeof(buffer)));
|
||||
return CURLE_FTP_PORT_FAILED;
|
||||
}
|
||||
}
|
||||
@@ -1188,8 +1187,7 @@ static CURLcode ftp_port_bind_socket(struct Curl_easy *data,
|
||||
curlx_strerror(SOCKERRNO, buffer, sizeof(buffer)));
|
||||
return CURLE_FTP_PORT_FAILED;
|
||||
}
|
||||
CURL_TRC_FTP(data, "ftp_port_bind_socket(), socket bound to port %d",
|
||||
port);
|
||||
CURL_TRC_FTP(data, "ftp_port_bind_socket(), socket bound to port %d", port);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
@@ -1276,8 +1274,7 @@ static CURLcode ftp_port_send_command(struct Curl_easy *data,
|
||||
* EPRT |2|1080::8:800:200C:417A|5282|
|
||||
*/
|
||||
result = Curl_pp_sendf(data, &ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
|
||||
sa->sa_family == AF_INET ? 1 : 2,
|
||||
myhost, port);
|
||||
sa->sa_family == AF_INET ? 1 : 2, myhost, port);
|
||||
if(result) {
|
||||
failf(data, "Failure sending EPRT command: %s",
|
||||
curl_easy_strerror(result));
|
||||
@@ -1393,10 +1390,13 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
|
||||
ftp_state(data, ftpc, FTP_STOP);
|
||||
}
|
||||
else {
|
||||
/* successfully set up the listen socket filter. SSL needed? */
|
||||
/* successfully set up the listen socket filter. SSL needed?
|
||||
* Use the control connections origin for cert verification. */
|
||||
if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port &&
|
||||
!Curl_conn_is_ssl(conn, SECONDARYSOCKET)) {
|
||||
result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET);
|
||||
result = Curl_ssl_cfilter_add(
|
||||
data, Curl_conn_get_origin(conn, FIRSTSOCKET),
|
||||
conn, SECONDARYSOCKET);
|
||||
}
|
||||
conn->bits.do_more = FALSE;
|
||||
Curl_pgrsTime(data, TIMER_STARTACCEPT);
|
||||
@@ -1983,11 +1983,7 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data,
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(conn->bits.ipv6
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
&& !(conn->bits.tunnel_proxy || conn->bits.socksproxy)
|
||||
#endif
|
||||
) {
|
||||
if(conn->bits.ipv6 && !Curl_conn_is_tunneling(conn, FIRSTSOCKET)) {
|
||||
/* We cannot disable EPSV when doing IPv6, so this is instead a fail */
|
||||
failf(data, "Failed EPSV attempt, exiting");
|
||||
return CURLE_WEIRD_SERVER_REPLY;
|
||||
@@ -2019,8 +2015,8 @@ static CURLcode ftp_control_addr_dup(struct Curl_easy *data, char **newhostp)
|
||||
the effective control connection address is the proxy address,
|
||||
not the ftp host. */
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(conn->bits.tunnel_proxy || conn->bits.socksproxy)
|
||||
*newhostp = curlx_strdup(conn->host.name);
|
||||
if(Curl_conn_is_tunneling(conn, FIRSTSOCKET))
|
||||
*newhostp = curlx_strdup(conn->origin->hostname);
|
||||
else
|
||||
#endif
|
||||
if(!Curl_conn_get_ip_info(data, conn, FIRSTSOCKET, &is_ipv6, &ipquad) &&
|
||||
@@ -2059,8 +2055,6 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
CURLcode result;
|
||||
struct Curl_dns_entry *dns = NULL;
|
||||
unsigned short connectport; /* the local port connect() should use! */
|
||||
const struct pingpong *pp = &ftpc->pp;
|
||||
char *newhost = NULL;
|
||||
unsigned short newport = 0;
|
||||
@@ -2075,7 +2069,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
|
||||
ptr++;
|
||||
/* |||12345| */
|
||||
sep = ptr[0];
|
||||
if((ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) {
|
||||
if(sep && (ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) {
|
||||
const char *p = &ptr[3];
|
||||
curl_off_t num;
|
||||
if(curlx_str_number(&p, &num, 0xffff) || (*p != sep)) {
|
||||
@@ -2125,7 +2119,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
|
||||
/* told to ignore the remotely given IP but instead use the host we used
|
||||
for the control connection */
|
||||
infof(data, "Skip %u.%u.%u.%u for data connection, reuse %s instead",
|
||||
ip[0], ip[1], ip[2], ip[3], conn->host.name);
|
||||
ip[0], ip[1], ip[2], ip[3], conn->origin->hostname);
|
||||
result = ftp_control_addr_dup(data, &newhost);
|
||||
if(result)
|
||||
return result;
|
||||
@@ -2147,67 +2141,25 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
|
||||
return CURLE_FTP_WEIRD_PASV_REPLY;
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(conn->bits.proxy) {
|
||||
/* This connection uses a proxy and we need to connect to the proxy again
|
||||
* here. We do not want to rely on a former host lookup that might have
|
||||
* expired now, instead we remake the lookup here and now! */
|
||||
struct ip_quadruple ipquad;
|
||||
bool is_ipv6;
|
||||
const char * const host_name = conn->bits.socksproxy ?
|
||||
conn->socks_proxy.host.name : conn->http_proxy.host.name;
|
||||
DEBUGASSERT(newhost);
|
||||
Curl_peer_unlink(&conn->origin2);
|
||||
result = Curl_peer_create(data, conn->scheme, newhost, newport,
|
||||
&conn->origin2);
|
||||
if(result)
|
||||
goto error;
|
||||
|
||||
result = Curl_conn_get_ip_info(data, data->conn, FIRSTSOCKET,
|
||||
&is_ipv6, &ipquad);
|
||||
/* If FIRSTSOCKET goes via another peer, SECONDARY needs as well,
|
||||
* but with its new port. */
|
||||
if(conn->via_peer) {
|
||||
Curl_peer_unlink(&conn->via_peer2);
|
||||
result = Curl_peer_create(data, conn->via_peer->scheme,
|
||||
conn->via_peer->hostname, newport,
|
||||
&conn->via_peer2);
|
||||
if(result)
|
||||
goto error;
|
||||
|
||||
(void)Curl_resolv_blocking(
|
||||
data, is_ipv6 ? CURL_DNSQ_AAAA : CURL_DNSQ_A,
|
||||
host_name, ipquad.remote_port, Curl_conn_get_transport(data, conn),
|
||||
&dns);
|
||||
/* we connect to the proxy's port */
|
||||
connectport = (unsigned short)ipquad.remote_port;
|
||||
|
||||
if(!dns) {
|
||||
failf(data, "cannot resolve proxy host %s:%hu", host_name, connectport);
|
||||
result = CURLE_COULDNT_RESOLVE_PROXY;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
/* normal, direct, ftp connection */
|
||||
DEBUGASSERT(newhost);
|
||||
|
||||
/* postponed address resolution in case of tcp fastopen */
|
||||
if(conn->bits.tcp_fastopen && !conn->bits.reuse && !newhost[0]) {
|
||||
curlx_free(newhost);
|
||||
result = ftp_control_addr_dup(data, &newhost);
|
||||
if(result)
|
||||
goto error;
|
||||
}
|
||||
|
||||
(void)Curl_resolv_blocking(
|
||||
data, Curl_resolv_dns_queries(data, conn->ip_version),
|
||||
newhost, newport, Curl_conn_get_transport(data, conn), &dns);
|
||||
connectport = newport; /* we connect to the remote port */
|
||||
|
||||
if(!dns) {
|
||||
failf(data, "cannot resolve new host %s:%hu", newhost, connectport);
|
||||
result = CURLE_FTP_CANT_GET_HOST;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUGASSERT(newhost);
|
||||
curlx_free(conn->secondaryhostname);
|
||||
conn->secondary_port = newport;
|
||||
conn->secondaryhostname = newhost;
|
||||
newhost = NULL;
|
||||
|
||||
result = Curl_conn_setup(data, conn, SECONDARYSOCKET, dns,
|
||||
result = Curl_conn_setup(data, conn, SECONDARYSOCKET,
|
||||
conn->bits.ftp_use_data_ssl ?
|
||||
CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE);
|
||||
|
||||
@@ -2227,13 +2179,10 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
|
||||
|
||||
#ifdef CURLVERBOSE
|
||||
if(data->set.verbose) {
|
||||
/* Dump information about this second connection when we have issued a PASV
|
||||
* command before and thus we have connected to a possibly new IP address.
|
||||
*/
|
||||
char buf[256];
|
||||
Curl_printable_address(dns->addr, buf, sizeof(buf));
|
||||
infof(data, "Connecting to %s (%s) port %d",
|
||||
conn->secondaryhostname, buf, connectport);
|
||||
/* Dump information about this second connection when we have issued
|
||||
* a PASV command. */
|
||||
infof(data, "Connecting to %s port %d",
|
||||
conn->origin2->hostname, conn->origin2->port);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2241,7 +2190,6 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
|
||||
ftp_state(data, ftpc, FTP_STOP); /* this phase is completed */
|
||||
|
||||
error:
|
||||
Curl_dns_entry_unlink(data, &dns);
|
||||
curlx_free(newhost);
|
||||
return result;
|
||||
}
|
||||
@@ -2267,10 +2215,10 @@ static CURLcode ftp_statemach(struct Curl_easy *data,
|
||||
* This function shall be called when the second FTP (data) connection is
|
||||
* connected.
|
||||
*
|
||||
* 'complete' can return 0 for incomplete, 1 for done and -1 for go back
|
||||
* 'more' can return DOMORE_INCOMPLETE, DOMORE_DONE or DOMORE_GOBACK
|
||||
* (which is for when PASV is being sent to retry a failed EPSV).
|
||||
*/
|
||||
static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
|
||||
static CURLcode ftp_do_more(struct Curl_easy *data, domore *more)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
|
||||
@@ -2285,7 +2233,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
|
||||
if(!ftpc || !ftp)
|
||||
return CURLE_FAILED_INIT;
|
||||
|
||||
*completep = 0; /* default to stay in the state */
|
||||
*more = DOMORE_INCOMPLETE; /* default to stay in the state */
|
||||
|
||||
/* if the second connection has been set up, try to connect it fully
|
||||
* to the remote host. This may not complete at this time, for several
|
||||
@@ -2303,7 +2251,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
|
||||
if(result || (!connected && !is_eptr &&
|
||||
!Curl_conn_is_ip_connected(data, SECONDARYSOCKET))) {
|
||||
if(result && !is_eptr && (ftpc->count1 == 0)) {
|
||||
*completep = -1; /* go back to DOING please */
|
||||
*more = DOMORE_GOBACK; /* go back to DOING please */
|
||||
/* this is a EPSV connect failing, try PASV instead */
|
||||
return ftp_epsv_disable(data, ftpc, conn);
|
||||
}
|
||||
@@ -2316,7 +2264,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
|
||||
They are only done to kickstart the do_more state */
|
||||
result = ftp_statemach(data, ftpc, &complete);
|
||||
|
||||
*completep = (int)complete;
|
||||
if(complete)
|
||||
*more = DOMORE_DONE;
|
||||
|
||||
/* if we got an error or if we do not wait for a data connection return
|
||||
immediately */
|
||||
@@ -2326,7 +2275,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
|
||||
/* if we reach the end of the FTP state machine here, *complete will be
|
||||
TRUE but so is ftpc->wait_data_conn, which says we need to wait for the
|
||||
data connection and therefore we are not actually complete */
|
||||
*completep = 0;
|
||||
*more = DOMORE_INCOMPLETE;
|
||||
}
|
||||
|
||||
if(ftp->transfer <= PPTRANSFER_INFO) {
|
||||
@@ -2348,8 +2297,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
*completep = 1; /* this state is now complete when the server has
|
||||
connected back to us */
|
||||
*more = DOMORE_DONE; /* this state is now complete when the server has
|
||||
connected back to us */
|
||||
}
|
||||
else {
|
||||
result = ftp_check_ctrl_on_data_wait(data, ftpc);
|
||||
@@ -2368,7 +2317,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
|
||||
* deemed necessary and directly sent `STORE name`. If this was
|
||||
* then complete, but we are still waiting on the data connection,
|
||||
* the transfer has not been initiated yet. */
|
||||
*completep = (int)(ftpc->wait_data_conn ? 0 : complete);
|
||||
*more = (!ftpc->wait_data_conn && complete) ?
|
||||
DOMORE_DONE : DOMORE_INCOMPLETE;
|
||||
}
|
||||
else {
|
||||
/* download */
|
||||
@@ -2411,7 +2361,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
|
||||
}
|
||||
|
||||
result = ftp_statemach(data, ftpc, &complete);
|
||||
*completep = (int)complete;
|
||||
if(complete)
|
||||
*more = DOMORE_DONE;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -2421,7 +2372,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
|
||||
|
||||
if(!ftpc->wait_data_conn) {
|
||||
/* no waiting for the data connection so this is now complete */
|
||||
*completep = 1;
|
||||
*more = DOMORE_DONE;
|
||||
CURL_TRC_FTP(data, "[%s] DO-MORE phase ends with %d", FTP_CSTATE(ftpc),
|
||||
(int)result);
|
||||
}
|
||||
@@ -2436,7 +2387,7 @@ static CURLcode ftp_dophase_done(struct Curl_easy *data,
|
||||
bool connected)
|
||||
{
|
||||
if(connected) {
|
||||
int completed;
|
||||
domore completed;
|
||||
CURLcode result = ftp_do_more(data, &completed);
|
||||
|
||||
if(result) {
|
||||
@@ -2950,7 +2901,7 @@ static CURLcode ftp_state_loggedin(struct Curl_easy *data,
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(data->conn->bits.ftp_use_control_ssl) {
|
||||
if(Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) {
|
||||
/* PBSZ = PROTECTION BUFFER SIZE.
|
||||
|
||||
The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
|
||||
@@ -2986,7 +2937,8 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data,
|
||||
if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
|
||||
/* 331 Password required for ...
|
||||
(the server requires to send the user's password too) */
|
||||
result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s", data->conn->passwd);
|
||||
result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s",
|
||||
Curl_creds_passwd(data->conn->creds));
|
||||
if(!result)
|
||||
ftp_state(data, ftpc, FTP_PASS);
|
||||
}
|
||||
@@ -3161,8 +3113,8 @@ static CURLcode ftp_wait_resp(struct Curl_easy *data,
|
||||
CURLcode result = CURLE_OK;
|
||||
if(ftpcode == 230) {
|
||||
/* 230 User logged in - already! Take as 220 if TLS required. */
|
||||
if(data->set.use_ssl <= CURLUSESSL_TRY ||
|
||||
conn->bits.ftp_use_control_ssl)
|
||||
if(ftpc->use_ssl <= CURLUSESSL_TRY ||
|
||||
Curl_conn_is_ssl(conn, FIRSTSOCKET))
|
||||
return ftp_state_user_resp(data, ftpc, ftpcode);
|
||||
}
|
||||
else if(ftpcode != 220) {
|
||||
@@ -3171,7 +3123,7 @@ static CURLcode ftp_wait_resp(struct Curl_easy *data,
|
||||
return CURLE_WEIRD_SERVER_REPLY;
|
||||
}
|
||||
|
||||
if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) {
|
||||
if(ftpc->use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
|
||||
/* We do not have an SSL/TLS control connection yet, but FTPS is
|
||||
requested. Try an FTPS connection now */
|
||||
|
||||
@@ -3243,16 +3195,17 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
|
||||
/* this was BLOCKING, keep it so for now */
|
||||
bool done;
|
||||
if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
|
||||
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
|
||||
result = Curl_ssl_cfilter_add(
|
||||
data, Curl_conn_get_origin(conn, FIRSTSOCKET), conn, FIRSTSOCKET);
|
||||
if(result) {
|
||||
/* we failed and bail out */
|
||||
return CURLE_USE_SSL_FAILED;
|
||||
}
|
||||
}
|
||||
/* BLOCKING */
|
||||
result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, &done);
|
||||
if(!result) {
|
||||
conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */
|
||||
conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */
|
||||
result = ftp_state_user(data, ftpc, conn);
|
||||
}
|
||||
}
|
||||
@@ -3264,7 +3217,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
|
||||
/* remain in this same state */
|
||||
}
|
||||
else {
|
||||
if(data->set.use_ssl > CURLUSESSL_TRY)
|
||||
if(ftpc->use_ssl > CURLUSESSL_TRY)
|
||||
/* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
|
||||
result = CURLE_USE_SSL_FAILED;
|
||||
else
|
||||
@@ -3285,7 +3238,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
|
||||
case FTP_PBSZ:
|
||||
result =
|
||||
Curl_pp_sendf(data, &ftpc->pp, "PROT %c",
|
||||
data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
|
||||
ftpc->use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
|
||||
if(!result)
|
||||
ftp_state(data, ftpc, FTP_PROT);
|
||||
break;
|
||||
@@ -3293,10 +3246,10 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
|
||||
case FTP_PROT:
|
||||
if(ftpcode / 100 == 2)
|
||||
/* We have enabled SSL for the data connection! */
|
||||
conn->bits.ftp_use_data_ssl = (data->set.use_ssl != CURLUSESSL_CONTROL);
|
||||
conn->bits.ftp_use_data_ssl = (ftpc->use_ssl != CURLUSESSL_CONTROL);
|
||||
/* FTP servers typically responds with 500 if they decide to reject
|
||||
our 'P' request */
|
||||
else if(data->set.use_ssl > CURLUSESSL_CONTROL)
|
||||
else if(ftpc->use_ssl > CURLUSESSL_CONTROL)
|
||||
/* we failed and bails out */
|
||||
return CURLE_USE_SSL_FAILED;
|
||||
|
||||
@@ -3572,7 +3525,6 @@ static CURLcode ftp_connect(struct Curl_easy *data,
|
||||
result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done);
|
||||
if(result)
|
||||
return result;
|
||||
conn->bits.ftp_use_control_ssl = TRUE;
|
||||
}
|
||||
|
||||
Curl_pp_init(pp, Curl_pgrs_now(data)); /* once per transfer */
|
||||
@@ -3641,30 +3593,10 @@ static CURLcode ftp_sendquote(struct Curl_easy *data,
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* ftp_done()
|
||||
*
|
||||
* The DONE function. This does what needs to be done after a single DO has
|
||||
* performed.
|
||||
*
|
||||
* Input argument is already checked for validity.
|
||||
*/
|
||||
static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
|
||||
bool premature)
|
||||
static CURLcode ftp_done_status(struct connectdata *conn,
|
||||
struct ftp_conn *ftpc, CURLcode status,
|
||||
bool premature)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
|
||||
struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
|
||||
struct pingpong *pp;
|
||||
size_t nread;
|
||||
int ftpcode;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
if(!ftp || !ftpc)
|
||||
return CURLE_OK;
|
||||
|
||||
pp = &ftpc->pp;
|
||||
switch(status) {
|
||||
case CURLE_BAD_DOWNLOAD_RESUME:
|
||||
case CURLE_FTP_WEIRD_PASV_REPLY:
|
||||
@@ -3693,10 +3625,13 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
|
||||
ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
|
||||
current path, as this connection is going */
|
||||
connclose(conn, "FTP ended with bad error code");
|
||||
result = status; /* use the already set error code */
|
||||
break;
|
||||
return status; /* use the already set error code */
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static void ftp_done_wildcard(struct Curl_easy *data, struct ftp_conn *ftpc)
|
||||
{
|
||||
if(data->state.wildcardmatch) {
|
||||
if(data->set.chunk_end && ftpc->file) {
|
||||
Curl_set_in_callback(data, TRUE);
|
||||
@@ -3706,14 +3641,18 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
|
||||
}
|
||||
ftpc->known_filesize = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void ftp_done_path(struct Curl_easy *data, struct ftp_conn *ftpc,
|
||||
CURLcode result)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
if(result) {
|
||||
/* We can limp along anyway (and should try to since we may already be in
|
||||
* the error path) */
|
||||
ftpc->ctl_valid = FALSE; /* mark control connection as bad */
|
||||
connclose(conn, "FTP: out of memory!"); /* mark for connection closure */
|
||||
curlx_free(ftpc->prevpath);
|
||||
ftpc->prevpath = NULL; /* no path remembering */
|
||||
curlx_safefree(ftpc->prevpath); /* no path remembering */
|
||||
}
|
||||
else { /* remember working directory for connection reuse */
|
||||
const char *rawPath = ftpc->rawpath;
|
||||
@@ -3740,13 +3679,17 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
|
||||
if(ftpc->prevpath)
|
||||
infof(data, "Remembering we are in directory \"%s\"", ftpc->prevpath);
|
||||
}
|
||||
}
|
||||
|
||||
/* shut down the socket to inform the server we are done */
|
||||
|
||||
static CURLcode ftp_done_secondary_socket(struct Curl_easy *data,
|
||||
struct ftp_conn *ftpc,
|
||||
CURLcode result)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
if(Curl_conn_is_setup(conn, SECONDARYSOCKET)) {
|
||||
if(!result && ftpc->dont_check && data->req.maxdownload > 0) {
|
||||
/* partial download completed */
|
||||
result = Curl_pp_sendf(data, pp, "%s", "ABOR");
|
||||
result = Curl_pp_sendf(data, &ftpc->pp, "%s", "ABOR");
|
||||
if(result) {
|
||||
failf(data, "Failure sending ABOR command: %s",
|
||||
curl_easy_strerror(result));
|
||||
@@ -3757,16 +3700,27 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
|
||||
|
||||
close_secondarysocket(data, ftpc);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode ftp_done_control_reply(struct Curl_easy *data,
|
||||
struct ftp_conn *ftpc,
|
||||
struct FTP *ftp, CURLcode result,
|
||||
bool premature)
|
||||
{
|
||||
struct connectdata *conn = data->conn;
|
||||
size_t nread;
|
||||
int ftpcode;
|
||||
|
||||
if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid &&
|
||||
pp->pending_resp && !premature) {
|
||||
ftpc->pp.pending_resp && !premature) {
|
||||
/*
|
||||
* Let's see what the server says about the transfer we performed, but
|
||||
* lower the timeout as sometimes this connection has died while the data
|
||||
* has been transferred. This happens when doing through NATs etc that
|
||||
* abandon old silent connections.
|
||||
*/
|
||||
pp->response = *Curl_pgrs_now(data); /* timeout relative now */
|
||||
ftpc->pp.response = *Curl_pgrs_now(data); /* timeout relative now */
|
||||
result = getftpresponse(data, &nread, &ftpcode);
|
||||
|
||||
if(!nread && (result == CURLE_OPERATION_TIMEDOUT)) {
|
||||
@@ -3803,7 +3757,14 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode ftp_done_check_partial(struct Curl_easy *data,
|
||||
struct ftp_conn *ftpc,
|
||||
struct FTP *ftp, CURLcode result,
|
||||
bool premature)
|
||||
{
|
||||
if(result || premature)
|
||||
/* the response code from the transfer showed an error already so no
|
||||
use checking further */
|
||||
@@ -3837,6 +3798,35 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
|
||||
result = CURLE_FTP_COULDNT_RETR_FILE;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
*
|
||||
* ftp_done()
|
||||
*
|
||||
* The DONE function. This does what needs to be done after a single DO has
|
||||
* performed.
|
||||
*
|
||||
* Input argument is already checked for validity.
|
||||
*/
|
||||
static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
|
||||
bool premature)
|
||||
{
|
||||
struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
|
||||
struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
|
||||
CURLcode result;
|
||||
|
||||
if(!ftp || !ftpc)
|
||||
return CURLE_OK;
|
||||
|
||||
result = ftp_done_status(data->conn, ftpc, status, premature);
|
||||
|
||||
ftp_done_wildcard(data, ftpc);
|
||||
ftp_done_path(data, ftpc, result);
|
||||
result = ftp_done_secondary_socket(data, ftpc, result);
|
||||
result = ftp_done_control_reply(data, ftpc, ftp, result, premature);
|
||||
result = ftp_done_check_partial(data, ftpc, ftp, result, premature);
|
||||
|
||||
/* clear these for next connection */
|
||||
ftp->transfer = PPTRANSFER_BODY;
|
||||
@@ -3845,7 +3835,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
|
||||
/* Send any post-transfer QUOTE strings? */
|
||||
if(!status && !result && !premature && data->set.postquote)
|
||||
result = ftp_sendquote(data, ftpc, data->set.postquote);
|
||||
CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_CSTATE(ftpc), result);
|
||||
CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_CSTATE(ftpc), (int)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -4455,7 +4445,8 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data,
|
||||
ftpc->use_ssl = data->set.use_ssl;
|
||||
ftpc->ccc = data->set.ftp_ccc;
|
||||
|
||||
CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(ftpc), result);
|
||||
CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(ftpc),
|
||||
(int)result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -4463,14 +4454,22 @@ bool ftp_conns_match(struct connectdata *needle, struct connectdata *conn)
|
||||
{
|
||||
struct ftp_conn *nftpc = Curl_conn_meta_get(needle, CURL_META_FTP_CONN);
|
||||
struct ftp_conn *cftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
|
||||
/* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */
|
||||
/* Also match ACCOUNT, ALTERNATIVE-TO-USER and CCC options */
|
||||
if(!nftpc || !cftpc ||
|
||||
Curl_timestrcmp(nftpc->account, cftpc->account) ||
|
||||
Curl_timestrcmp(nftpc->alternative_to_user,
|
||||
cftpc->alternative_to_user) ||
|
||||
(nftpc->use_ssl != cftpc->use_ssl) ||
|
||||
(nftpc->ccc != cftpc->ccc))
|
||||
return FALSE;
|
||||
/* A mismatch on `use_ssl` MUST have been found in connection matching
|
||||
* before we come here. This is a check on MAYBE/MUST use of STARTTLS and
|
||||
* it only works on FTP. But IMAP/SMTP etc have the same `use_ssl` and
|
||||
* no extra match like FTP. We lack tests in this area, so let FTP fail
|
||||
* loudly here to help other cases. */
|
||||
if(nftpc->use_ssl > cftpc->use_ssl) {
|
||||
DEBUGASSERT(0);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
@@ -196,13 +196,11 @@ void Curl_wildcard_dtor(struct WildcardData **wcp)
|
||||
wc->dtor = ZERO_NULL;
|
||||
wc->ftpwc = NULL;
|
||||
}
|
||||
DEBUGASSERT(wc->ftpwc == NULL);
|
||||
DEBUGASSERT(!wc->ftpwc);
|
||||
|
||||
Curl_llist_destroy(&wc->filelist, NULL);
|
||||
curlx_free(wc->path);
|
||||
wc->path = NULL;
|
||||
curlx_free(wc->pattern);
|
||||
wc->pattern = NULL;
|
||||
curlx_safefree(wc->path);
|
||||
curlx_safefree(wc->pattern);
|
||||
wc->state = CURLWC_INIT;
|
||||
curlx_free(wc);
|
||||
*wcp = NULL;
|
||||
@@ -312,8 +310,9 @@ static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data,
|
||||
str + parser->offsets.group : NULL;
|
||||
finfo->strings.perm = parser->offsets.perm ?
|
||||
str + parser->offsets.perm : NULL;
|
||||
finfo->strings.target = parser->offsets.symlink_target ?
|
||||
str + parser->offsets.symlink_target : NULL;
|
||||
finfo->strings.target = parser->offsets.symlink_target &&
|
||||
(finfo->filetype == CURLFILETYPE_SYMLINK) ?
|
||||
str + parser->offsets.symlink_target : NULL;
|
||||
finfo->strings.time = str + parser->offsets.time;
|
||||
finfo->strings.user = parser->offsets.user ?
|
||||
str + parser->offsets.user : NULL;
|
||||
@@ -940,7 +939,7 @@ static CURLcode parse_winnt(struct Curl_easy *data,
|
||||
parser->item_length++;
|
||||
if(c == ' ') {
|
||||
mem[parser->item_offset + parser->item_length - 1] = 0;
|
||||
if(strcmp("<DIR>", mem + parser->item_offset) == 0) {
|
||||
if(!strcmp("<DIR>", mem + parser->item_offset)) {
|
||||
finfo->filetype = CURLFILETYPE_DIRECTORY;
|
||||
finfo->size = 0;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
#define SEND_TYPE_ARG3 int
|
||||
#define SEND_TYPE_RETV int
|
||||
|
||||
#elif defined(__AMIGA__) /* Any AmigaOS flavour */
|
||||
#elif defined(__AMIGA__) /* Any AmigaOS flavor */
|
||||
|
||||
/* long recv(long, char *, long, long); */
|
||||
#define RECV_TYPE_ARG1 long
|
||||
|
||||
@@ -67,11 +67,8 @@ void Curl_initinfo(struct Curl_easy *data)
|
||||
info->httpauthpicked = 0;
|
||||
info->numconnects = 0;
|
||||
|
||||
curlx_free(info->contenttype);
|
||||
info->contenttype = NULL;
|
||||
|
||||
curlx_free(info->wouldredirect);
|
||||
info->wouldredirect = NULL;
|
||||
curlx_safefree(info->contenttype);
|
||||
curlx_safefree(info->wouldredirect);
|
||||
|
||||
memset(&info->primary, 0, sizeof(info->primary));
|
||||
info->retry_after = 0;
|
||||
|
||||
@@ -133,7 +133,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
|
||||
if(!timeout_ms)
|
||||
timeout_ms = TIMEDIFF_T_MAX;
|
||||
|
||||
/* Do not busyloop. The entire loop thing is a work-around as it causes a
|
||||
/* Do not busyloop. The entire loop thing is a workaround as it causes a
|
||||
BLOCKING behavior which is a NO-NO. This function should rather be
|
||||
split up in a do and a doing piece where the pieces that are not
|
||||
possible to send now will be sent in the doing function repeatedly
|
||||
|
||||
@@ -305,7 +305,7 @@ static CURLcode hds_cw_collect_write(struct Curl_easy *data,
|
||||
CURLH_HEADER)));
|
||||
CURLcode result = Curl_headers_push(data, buf, blen, htype);
|
||||
CURL_TRC_WRITE(data, "header_collect pushed(type=%x, len=%zu) -> %d",
|
||||
htype, blen, result);
|
||||
htype, blen, (int)result);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -70,10 +70,6 @@
|
||||
#define USE_ALARM_TIMEOUT
|
||||
#endif
|
||||
|
||||
#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */
|
||||
|
||||
#define MAX_DNS_CACHE_SIZE 29999
|
||||
|
||||
#define RESOLV_FAIL(for_proxy) \
|
||||
((for_proxy) ? CURLE_COULDNT_RESOLVE_PROXY : CURLE_COULDNT_RESOLVE_HOST)
|
||||
|
||||
@@ -511,7 +507,6 @@ const struct Curl_addrinfo *Curl_resolv_get_ai(struct Curl_easy *data,
|
||||
return Curl_async_get_ai(data, async, ai_family, index);
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_HTTPSRR
|
||||
const struct Curl_https_rrinfo *
|
||||
Curl_resolv_get_https(struct Curl_easy *data, uint32_t resolv_id)
|
||||
@@ -529,7 +524,6 @@ bool Curl_resolv_knows_https(struct Curl_easy *data, uint32_t resolv_id)
|
||||
return TRUE;
|
||||
return Curl_async_knows_https(data, async);
|
||||
}
|
||||
|
||||
#endif /* USE_HTTPSRR */
|
||||
|
||||
#endif /* USE_CURL_ASYNC */
|
||||
@@ -740,7 +734,7 @@ out:
|
||||
failf(data, "Could not resolve: %s:%u", hostname, port);
|
||||
}
|
||||
else {
|
||||
failf(data, "Error %d resolving %s:%u", result, hostname, port);
|
||||
failf(data, "Error %d resolving %s:%u", (int)result, hostname, port);
|
||||
}
|
||||
}
|
||||
else if(cache_dns && *pdns) {
|
||||
@@ -795,9 +789,6 @@ CURL_NORETURN static void alarmfunc(int sig)
|
||||
(void)sig;
|
||||
siglongjmp(curl_jmpenv, 1);
|
||||
}
|
||||
#endif /* USE_ALARM_TIMEOUT */
|
||||
|
||||
#ifdef USE_ALARM_TIMEOUT
|
||||
|
||||
static CURLcode resolv_alarm_timeout(struct Curl_easy *data,
|
||||
uint8_t dns_queries,
|
||||
@@ -935,6 +926,33 @@ clean_up:
|
||||
|
||||
#endif /* USE_ALARM_TIMEOUT */
|
||||
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
static CURLcode resolv_unix(struct Curl_easy *data,
|
||||
const char *unix_path,
|
||||
bool abstract_path,
|
||||
struct Curl_dns_entry **pdns)
|
||||
{
|
||||
struct Curl_addrinfo *addr;
|
||||
CURLcode result;
|
||||
|
||||
DEBUGASSERT(unix_path);
|
||||
*pdns = NULL;
|
||||
|
||||
result = Curl_unix2addr(unix_path, abstract_path, &addr);
|
||||
if(result) {
|
||||
if(result == CURLE_TOO_LARGE) {
|
||||
/* Long paths are not supported for now */
|
||||
failf(data, "Unix socket path too long: '%s'", unix_path);
|
||||
result = CURLE_COULDNT_RESOLVE_HOST;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
*pdns = Curl_dnscache_mk_entry(data, 0, &addr, NULL, 0);
|
||||
return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
#endif /* USE_UNIX_SOCKETS */
|
||||
|
||||
/*
|
||||
* Curl_resolv() is the main name resolve function within libcurl. It resolves
|
||||
* a name and returns a pointer to the entry in the 'entry' argument. This
|
||||
@@ -956,16 +974,14 @@ clean_up:
|
||||
* any other CURLcode error, *pdns == NULL
|
||||
*/
|
||||
CURLcode Curl_resolv(struct Curl_easy *data,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t dns_queries,
|
||||
const char *hostname,
|
||||
uint16_t port,
|
||||
uint8_t transport,
|
||||
bool for_proxy,
|
||||
timediff_t timeout_ms,
|
||||
uint32_t *presolv_id,
|
||||
struct Curl_dns_entry **pdns)
|
||||
{
|
||||
DEBUGASSERT(hostname && *hostname);
|
||||
*presolv_id = 0;
|
||||
*pdns = NULL;
|
||||
|
||||
@@ -975,14 +991,23 @@ CURLcode Curl_resolv(struct Curl_easy *data,
|
||||
else if(!timeout_ms)
|
||||
timeout_ms = CURL_TIMEOUT_RESOLVE_MS;
|
||||
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
if(peer->unix_socket)
|
||||
return resolv_unix(data, peer->hostname, (bool)peer->abstract_uds, pdns);
|
||||
#else
|
||||
if(peer->unix_socket)
|
||||
return hostip_resolv_failed(data, peer->hostname, for_proxy);
|
||||
#endif
|
||||
|
||||
#ifdef USE_ALARM_TIMEOUT
|
||||
if(timeout_ms && data->set.no_signal) {
|
||||
/* Cannot use ALARM when signals are disabled */
|
||||
timeout_ms = 0;
|
||||
}
|
||||
if(timeout_ms && !Curl_doh_wanted(data)) {
|
||||
return resolv_alarm_timeout(data, dns_queries, hostname, port, transport,
|
||||
for_proxy, timeout_ms, presolv_id, pdns);
|
||||
return resolv_alarm_timeout(data, dns_queries, peer->hostname, peer->port,
|
||||
transport, for_proxy, timeout_ms, presolv_id,
|
||||
pdns);
|
||||
}
|
||||
#endif /* !USE_ALARM_TIMEOUT */
|
||||
|
||||
@@ -991,8 +1016,9 @@ CURLcode Curl_resolv(struct Curl_easy *data,
|
||||
infof(data, "timeout on name lookup is not supported");
|
||||
#endif
|
||||
|
||||
return hostip_resolv(data, dns_queries, hostname, port, transport,
|
||||
for_proxy, timeout_ms, TRUE, presolv_id, pdns);
|
||||
return hostip_resolv(data, dns_queries, peer->hostname, peer->port,
|
||||
transport, for_proxy, timeout_ms, TRUE, presolv_id,
|
||||
pdns);
|
||||
}
|
||||
|
||||
#ifdef USE_CURL_ASYNC
|
||||
@@ -1047,7 +1073,7 @@ CURLcode Curl_resolv_take_result(struct Curl_easy *data, uint32_t resolv_id,
|
||||
}
|
||||
else if(result) {
|
||||
failf(data, "Error %d resolving %s:%u",
|
||||
result, async->hostname, async->port);
|
||||
(int)result, async->hostname, async->port);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1101,30 +1127,3 @@ void Curl_resolv_destroy_all(struct Curl_easy *data)
|
||||
}
|
||||
|
||||
#endif /* USE_CURL_ASYNC */
|
||||
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
CURLcode Curl_resolv_unix(struct Curl_easy *data,
|
||||
const char *unix_path,
|
||||
bool abstract_path,
|
||||
struct Curl_dns_entry **pdns)
|
||||
{
|
||||
struct Curl_addrinfo *addr;
|
||||
CURLcode result;
|
||||
|
||||
DEBUGASSERT(unix_path);
|
||||
*pdns = NULL;
|
||||
|
||||
result = Curl_unix2addr(unix_path, abstract_path, &addr);
|
||||
if(result) {
|
||||
if(result == CURLE_TOO_LARGE) {
|
||||
/* Long paths are not supported for now */
|
||||
failf(data, "Unix socket path too long: '%s'", unix_path);
|
||||
result = CURLE_COULDNT_RESOLVE_HOST;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
*pdns = Curl_dnscache_mk_entry(data, 0, &addr, NULL, 0);
|
||||
return *pdns ? CURLE_OK : CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
#endif /* USE_UNIX_SOCKETS */
|
||||
|
||||
@@ -45,6 +45,7 @@ struct easy_pollset;
|
||||
struct Curl_https_rrinfo;
|
||||
struct Curl_multi;
|
||||
struct Curl_dns_entry;
|
||||
struct Curl_peer;
|
||||
|
||||
/* DNS query types */
|
||||
#define CURL_DNSQ_A (1U << 0)
|
||||
@@ -96,9 +97,8 @@ void Curl_printable_address(const struct Curl_addrinfo *ai,
|
||||
* - other: the operation failed, `*pdns` is NULL, `*presolv_id` is 0.
|
||||
*/
|
||||
CURLcode Curl_resolv(struct Curl_easy *data,
|
||||
struct Curl_peer *peer,
|
||||
uint8_t dns_queries,
|
||||
const char *hostname,
|
||||
uint16_t port,
|
||||
uint8_t transport,
|
||||
bool for_proxy,
|
||||
timediff_t timeout_ms,
|
||||
@@ -184,11 +184,4 @@ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
|
||||
uint8_t transport);
|
||||
#endif
|
||||
|
||||
#ifdef USE_UNIX_SOCKETS
|
||||
CURLcode Curl_resolv_unix(struct Curl_easy *data,
|
||||
const char *unix_path,
|
||||
bool abstract_path,
|
||||
struct Curl_dns_entry **pdns);
|
||||
#endif
|
||||
|
||||
#endif /* HEADER_CURL_HOSTIP_H */
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
* the HAVE_GETHOSTBYNAME_R_3, HAVE_GETHOSTBYNAME_R_5 or
|
||||
* HAVE_GETHOSTBYNAME_R_6 defines accordingly. Note that HAVE_GETADDRBYNAME
|
||||
* has the corresponding rules. This is primarily on *nix. Note that some Unix
|
||||
* flavours have thread-safe versions of the plain gethostbyname() etc.
|
||||
* flavors have thread-safe versions of the plain gethostbyname() etc.
|
||||
*
|
||||
*/
|
||||
struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
|
||||
|
||||
+87
-58
@@ -96,7 +96,7 @@ void Curl_hsts_cleanup(struct hsts **hp)
|
||||
static void hsts_append(struct hsts *h, struct stsentry *sts)
|
||||
{
|
||||
if(Curl_llist_count(&h->list) == MAX_HSTS_ENTRIES) {
|
||||
/* It's full. Remove the first entry in the list */
|
||||
/* It is full. Remove the first entry in the list */
|
||||
struct Curl_llist_node *e = Curl_llist_head(&h->list);
|
||||
struct stsentry *oldsts = Curl_node_elem(e);
|
||||
Curl_node_remove(e);
|
||||
@@ -121,7 +121,7 @@ static CURLcode hsts_create(struct hsts *h,
|
||||
struct stsentry *sts = curlx_calloc(1, sizeof(struct stsentry) + hlen);
|
||||
if(!sts)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
/* the null terminator is already there */
|
||||
/* the null-terminator is already there */
|
||||
memcpy(sts->host, hostname, hlen);
|
||||
sts->expires = expires;
|
||||
sts->includeSubDomains = subdomains;
|
||||
@@ -130,6 +130,81 @@ static CURLcode hsts_create(struct hsts *h,
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* Copy all live entries from src into dst. Used by curl_easy_duphandle so the
|
||||
* clone inherits entries learned at runtime. E.g. Strict-Transport-Security.
|
||||
*/
|
||||
CURLcode Curl_hsts_copy(struct hsts *dst, struct hsts *src)
|
||||
{
|
||||
struct Curl_llist_node *e;
|
||||
time_t now = time(NULL);
|
||||
for(e = Curl_llist_head(&src->list); e; e = Curl_node_next(e)) {
|
||||
struct stsentry *sts = Curl_node_elem(e);
|
||||
if(sts->expires > now) {
|
||||
CURLcode result = hsts_create(dst, sts->host, strlen(sts->host),
|
||||
sts->includeSubDomains != 0, sts->expires);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the matching HSTS entry, or NULL if the given hostname is not
|
||||
* currently an HSTS one.
|
||||
*
|
||||
* The 'subdomain' argument tells the function if subdomain matching should be
|
||||
* attempted.
|
||||
*
|
||||
* @unittest 1660
|
||||
*/
|
||||
UNITTEST struct stsentry *hsts_check(struct hsts *h, const char *hostname,
|
||||
size_t hlen, bool subdomain);
|
||||
UNITTEST struct stsentry *hsts_check(struct hsts *h, const char *hostname,
|
||||
size_t hlen, bool subdomain)
|
||||
{
|
||||
struct stsentry *bestsub = NULL;
|
||||
if(h) {
|
||||
time_t now = time(NULL);
|
||||
struct Curl_llist_node *e;
|
||||
struct Curl_llist_node *n;
|
||||
size_t blen = 0;
|
||||
|
||||
if((hlen > MAX_HSTS_HOSTLEN) || !hlen)
|
||||
return NULL;
|
||||
if(hostname[hlen - 1] == '.')
|
||||
/* remove the trailing dot */
|
||||
--hlen;
|
||||
|
||||
for(e = Curl_llist_head(&h->list); e; e = n) {
|
||||
struct stsentry *sts = Curl_node_elem(e);
|
||||
size_t ntail;
|
||||
n = Curl_node_next(e);
|
||||
if(sts->expires <= now) {
|
||||
/* remove expired entries */
|
||||
Curl_node_remove(&sts->node);
|
||||
hsts_free(sts);
|
||||
continue;
|
||||
}
|
||||
ntail = strlen(sts->host);
|
||||
if((subdomain && sts->includeSubDomains) && (ntail < hlen)) {
|
||||
size_t offs = hlen - ntail;
|
||||
if((hostname[offs - 1] == '.') &&
|
||||
curl_strnequal(&hostname[offs], sts->host, ntail) &&
|
||||
(ntail > blen)) {
|
||||
/* save the tail match with the longest tail */
|
||||
bestsub = sts;
|
||||
blen = ntail;
|
||||
}
|
||||
}
|
||||
/* avoid curl_strequal because the hostname is not null-terminated */
|
||||
if((hlen == ntail) && curl_strnequal(hostname, sts->host, hlen))
|
||||
return sts;
|
||||
}
|
||||
}
|
||||
return bestsub;
|
||||
}
|
||||
|
||||
CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
|
||||
const char *header)
|
||||
{
|
||||
@@ -203,7 +278,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
|
||||
|
||||
if(!expires) {
|
||||
/* remove the entry if present verbatim (without subdomain match) */
|
||||
sts = Curl_hsts(h, hostname, hlen, FALSE);
|
||||
sts = hsts_check(h, hostname, hlen, FALSE);
|
||||
if(sts) {
|
||||
Curl_node_remove(&sts->node);
|
||||
hsts_free(sts);
|
||||
@@ -218,7 +293,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
|
||||
expires += now;
|
||||
|
||||
/* check if it already exists */
|
||||
sts = Curl_hsts(h, hostname, hlen, FALSE);
|
||||
sts = hsts_check(h, hostname, hlen, FALSE);
|
||||
if(sts) {
|
||||
/* update these fields */
|
||||
sts->expires = expires;
|
||||
@@ -230,57 +305,6 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return TRUE if the given hostname is currently an HSTS one.
|
||||
*
|
||||
* The 'subdomain' argument tells the function if subdomain matching should be
|
||||
* attempted.
|
||||
*/
|
||||
struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
|
||||
size_t hlen, bool subdomain)
|
||||
{
|
||||
struct stsentry *bestsub = NULL;
|
||||
if(h) {
|
||||
time_t now = time(NULL);
|
||||
struct Curl_llist_node *e;
|
||||
struct Curl_llist_node *n;
|
||||
size_t blen = 0;
|
||||
|
||||
if((hlen > MAX_HSTS_HOSTLEN) || !hlen)
|
||||
return NULL;
|
||||
if(hostname[hlen - 1] == '.')
|
||||
/* remove the trailing dot */
|
||||
--hlen;
|
||||
|
||||
for(e = Curl_llist_head(&h->list); e; e = n) {
|
||||
struct stsentry *sts = Curl_node_elem(e);
|
||||
size_t ntail;
|
||||
n = Curl_node_next(e);
|
||||
if(sts->expires <= now) {
|
||||
/* remove expired entries */
|
||||
Curl_node_remove(&sts->node);
|
||||
hsts_free(sts);
|
||||
continue;
|
||||
}
|
||||
ntail = strlen(sts->host);
|
||||
if((subdomain && sts->includeSubDomains) && (ntail < hlen)) {
|
||||
size_t offs = hlen - ntail;
|
||||
if((hostname[offs - 1] == '.') &&
|
||||
curl_strnequal(&hostname[offs], sts->host, ntail) &&
|
||||
(ntail > blen)) {
|
||||
/* save the tail match with the longest tail */
|
||||
bestsub = sts;
|
||||
blen = ntail;
|
||||
}
|
||||
}
|
||||
/* avoid curl_strequal because the hostname is not null-terminated */
|
||||
if((hlen == ntail) && curl_strnequal(hostname, sts->host, hlen))
|
||||
return sts;
|
||||
}
|
||||
}
|
||||
return bestsub;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send this HSTS entry to the write callback.
|
||||
*/
|
||||
@@ -437,10 +461,10 @@ static CURLcode hsts_add_host_expire(struct hsts *h,
|
||||
|
||||
if(hostlen) {
|
||||
/* only add it if not already present */
|
||||
e = Curl_hsts(h, host, hostlen, subdomain);
|
||||
e = hsts_check(h, host, hostlen, subdomain);
|
||||
if(!e)
|
||||
result = hsts_create(h, host, hostlen, subdomain, expires);
|
||||
/* 'host' is not necessarily null terminated */
|
||||
/* 'host' is not necessarily null-terminated */
|
||||
else if((hostlen == strlen(e->host) &&
|
||||
curl_strnequal(host, e->host, hostlen))) {
|
||||
/* the same hostname, use the largest expire time and keep the strictest
|
||||
@@ -504,7 +528,7 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h)
|
||||
const char *date = e.expire;
|
||||
if(!e.name[0] || e.expire[MAX_HSTS_DATELEN] ||
|
||||
e.name[MAX_HSTS_HOSTLEN])
|
||||
/* bail out if no name was stored or if a null terminator is gone */
|
||||
/* bail out if no name was stored or if a null-terminator is gone */
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
if(!date[0])
|
||||
date = UNLIMITED;
|
||||
@@ -610,6 +634,11 @@ CURLcode Curl_hsts_loadfiles(struct Curl_easy *data)
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Curl_hsts_applies(struct hsts *h, const struct Curl_peer *dest)
|
||||
{
|
||||
return !!hsts_check(h, dest->hostname, strlen(dest->hostname), TRUE);
|
||||
}
|
||||
|
||||
#if defined(DEBUGBUILD) || defined(UNITTESTS)
|
||||
#undef time
|
||||
#endif
|
||||
|
||||
@@ -25,9 +25,13 @@
|
||||
***************************************************************************/
|
||||
#include "curl_setup.h"
|
||||
|
||||
struct hsts;
|
||||
|
||||
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_HSTS)
|
||||
#include "llist.h"
|
||||
|
||||
struct Curl_peer;
|
||||
|
||||
#define MAX_HSTS_ENTRIES 10000
|
||||
|
||||
#if defined(DEBUGBUILD) || defined(UNITTESTS)
|
||||
@@ -50,10 +54,9 @@ struct hsts {
|
||||
|
||||
struct hsts *Curl_hsts_init(void);
|
||||
void Curl_hsts_cleanup(struct hsts **hp);
|
||||
CURLcode Curl_hsts_copy(struct hsts *dst, struct hsts *src);
|
||||
CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
|
||||
const char *header);
|
||||
struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
|
||||
size_t hlen, bool subdomain);
|
||||
CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
|
||||
const char *file);
|
||||
CURLcode Curl_hsts_loadfile(struct Curl_easy *data,
|
||||
@@ -61,6 +64,9 @@ CURLcode Curl_hsts_loadfile(struct Curl_easy *data,
|
||||
CURLcode Curl_hsts_loadcb(struct Curl_easy *data,
|
||||
struct hsts *h);
|
||||
CURLcode Curl_hsts_loadfiles(struct Curl_easy *data);
|
||||
|
||||
bool Curl_hsts_applies(struct hsts *h, const struct Curl_peer *dest);
|
||||
|
||||
#else
|
||||
#define Curl_hsts_cleanup(x)
|
||||
#define Curl_hsts_loadcb(x, y) CURLE_OK
|
||||
|
||||
+194
-202
@@ -154,7 +154,7 @@ char *Curl_checkProxyheaders(struct Curl_easy *data,
|
||||
{
|
||||
struct curl_slist *head;
|
||||
|
||||
for(head = (conn->bits.proxy && data->set.sep_headers) ?
|
||||
for(head = (conn->http_proxy.peer && data->set.sep_headers) ?
|
||||
data->set.proxyheaders : data->set.headers;
|
||||
head; head = head->next) {
|
||||
if(curl_strnequal(head->data, thisheader, thislen) &&
|
||||
@@ -250,34 +250,38 @@ char *Curl_copy_header_value(const char *header)
|
||||
*
|
||||
* Returns CURLcode.
|
||||
*/
|
||||
static CURLcode http_output_basic(struct Curl_easy *data, bool proxy)
|
||||
static CURLcode http_output_basic(struct Curl_easy *data,
|
||||
struct connectdata *conn, bool proxy)
|
||||
{
|
||||
size_t size = 0;
|
||||
char *authorization = NULL;
|
||||
char **userp;
|
||||
const char *user;
|
||||
const char *pwd;
|
||||
char **p_hd;
|
||||
CURLcode result;
|
||||
struct Curl_creds *creds = NULL;
|
||||
char *out;
|
||||
|
||||
/* credentials are unique per transfer for HTTP, do not use the ones for the
|
||||
connection */
|
||||
if(proxy) {
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
userp = &data->req.proxyuserpwd;
|
||||
user = data->state.aptr.proxyuser;
|
||||
pwd = data->state.aptr.proxypasswd;
|
||||
p_hd = &data->req.hd_proxy_auth;
|
||||
creds = conn->http_proxy.creds;
|
||||
#else
|
||||
(void)conn;
|
||||
return CURLE_NOT_BUILT_IN;
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
userp = &data->req.userpwd;
|
||||
user = data->state.aptr.user;
|
||||
pwd = data->state.aptr.passwd;
|
||||
p_hd = &data->req.hd_auth;
|
||||
creds = data->state.creds;
|
||||
}
|
||||
|
||||
out = curl_maprintf("%s:%s", user ? user : "", pwd ? pwd : "");
|
||||
if(!creds) {
|
||||
DEBUGASSERT(0);
|
||||
return CURLE_FAILED_INIT;
|
||||
}
|
||||
|
||||
out = curl_maprintf("%s:%s", creds->user, creds->passwd);
|
||||
if(!out)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
@@ -291,12 +295,12 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
curlx_free(*userp);
|
||||
*userp = curl_maprintf("%sAuthorization: Basic %s\r\n",
|
||||
proxy ? "Proxy-" : "",
|
||||
authorization);
|
||||
curlx_free(*p_hd);
|
||||
*p_hd = curl_maprintf("%sAuthorization: Basic %s\r\n",
|
||||
proxy ? "Proxy-" : "",
|
||||
authorization);
|
||||
curlx_free(authorization);
|
||||
if(!*userp) {
|
||||
if(!*p_hd) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto fail;
|
||||
}
|
||||
@@ -320,10 +324,11 @@ static CURLcode http_output_bearer(struct Curl_easy *data)
|
||||
char **userp;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
userp = &data->req.userpwd;
|
||||
DEBUGASSERT(Curl_creds_has_oauth_bearer(data->state.creds));
|
||||
userp = &data->req.hd_auth;
|
||||
curlx_free(*userp);
|
||||
*userp = curl_maprintf("Authorization: Bearer %s\r\n",
|
||||
data->set.str[STRING_BEARER]);
|
||||
Curl_creds_oauth_bearer(data->state.creds));
|
||||
|
||||
if(!*userp) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
@@ -333,7 +338,6 @@ static CURLcode http_output_bearer(struct Curl_easy *data)
|
||||
fail:
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -343,8 +347,10 @@ fail:
|
||||
*
|
||||
* return TRUE if one was picked
|
||||
*/
|
||||
static bool pickoneauth(struct auth *pick, unsigned long mask)
|
||||
static bool pickoneauth(struct auth *pick, unsigned long mask,
|
||||
struct Curl_creds *creds)
|
||||
{
|
||||
bool have_user_pass = Curl_creds_has_user_or_pass(creds);
|
||||
bool picked;
|
||||
/* only deal with authentication we want */
|
||||
unsigned long avail = pick->avail & pick->want & mask;
|
||||
@@ -352,20 +358,20 @@ static bool pickoneauth(struct auth *pick, unsigned long mask)
|
||||
|
||||
/* The order of these checks is highly relevant, as this will be the order
|
||||
of preference in case of the existence of multiple accepted types. */
|
||||
if(avail & CURLAUTH_NEGOTIATE)
|
||||
if(avail & CURLAUTH_NEGOTIATE) /* available on empty creds */
|
||||
pick->picked = CURLAUTH_NEGOTIATE;
|
||||
#ifndef CURL_DISABLE_BEARER_AUTH
|
||||
else if(avail & CURLAUTH_BEARER)
|
||||
else if((avail & CURLAUTH_BEARER) && Curl_creds_has_oauth_bearer(creds))
|
||||
pick->picked = CURLAUTH_BEARER;
|
||||
#endif
|
||||
#ifndef CURL_DISABLE_DIGEST_AUTH
|
||||
else if(avail & CURLAUTH_DIGEST)
|
||||
else if((avail & CURLAUTH_DIGEST) && have_user_pass)
|
||||
pick->picked = CURLAUTH_DIGEST;
|
||||
#endif
|
||||
else if(avail & CURLAUTH_NTLM)
|
||||
pick->picked = CURLAUTH_NTLM;
|
||||
#ifndef CURL_DISABLE_BASIC_AUTH
|
||||
else if(avail & CURLAUTH_BASIC)
|
||||
else if((avail & CURLAUTH_BASIC) && have_user_pass)
|
||||
pick->picked = CURLAUTH_BASIC;
|
||||
#endif
|
||||
#ifndef CURL_DISABLE_AWS
|
||||
@@ -527,10 +533,10 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode)
|
||||
* Either we are not authenticating, or we are supposed to be authenticating
|
||||
* something else. This is an error.
|
||||
*/
|
||||
if((httpcode == 401) && !data->state.aptr.user)
|
||||
if((httpcode == 401) && !data->state.creds)
|
||||
return TRUE;
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if((httpcode == 407) && !data->conn->bits.proxy_user_passwd)
|
||||
if((httpcode == 407) && !data->conn->http_proxy.creds)
|
||||
return TRUE;
|
||||
#endif
|
||||
|
||||
@@ -551,7 +557,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data)
|
||||
CURLcode result = CURLE_OK;
|
||||
unsigned long authmask = ~0UL;
|
||||
|
||||
if(!data->set.str[STRING_BEARER])
|
||||
if(!Curl_creds_has_oauth_bearer(data->state.creds))
|
||||
authmask &= (unsigned long)~CURLAUTH_BEARER;
|
||||
|
||||
if(100 <= data->req.httpcode && data->req.httpcode <= 199)
|
||||
@@ -561,10 +567,10 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data)
|
||||
if(data->state.authproblem)
|
||||
return data->set.http_fail_on_error ? CURLE_HTTP_RETURNED_ERROR : CURLE_OK;
|
||||
|
||||
if((data->state.aptr.user || data->set.str[STRING_BEARER]) &&
|
||||
if(data->state.creds &&
|
||||
((data->req.httpcode == 401) ||
|
||||
(data->req.authneg && data->req.httpcode < 300))) {
|
||||
pickhost = pickoneauth(&data->state.authhost, authmask);
|
||||
pickhost = pickoneauth(&data->state.authhost, authmask, data->state.creds);
|
||||
if(!pickhost)
|
||||
data->state.authproblem = TRUE;
|
||||
else
|
||||
@@ -578,11 +584,12 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data)
|
||||
}
|
||||
}
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(conn->bits.proxy_user_passwd &&
|
||||
if(conn->http_proxy.creds &&
|
||||
((data->req.httpcode == 407) ||
|
||||
(data->req.authneg && data->req.httpcode < 300))) {
|
||||
pickproxy = pickoneauth(&data->state.authproxy,
|
||||
authmask & ~CURLAUTH_BEARER);
|
||||
authmask & ~CURLAUTH_BEARER,
|
||||
conn->http_proxy.creds);
|
||||
if(!pickproxy)
|
||||
data->state.authproblem = TRUE;
|
||||
else
|
||||
@@ -694,14 +701,16 @@ static CURLcode output_auth_headers(struct Curl_easy *data,
|
||||
/* Basic */
|
||||
if(
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
(proxy && conn->bits.proxy_user_passwd &&
|
||||
(proxy && conn->http_proxy.creds &&
|
||||
Curl_creds_has_user_or_pass(conn->http_proxy.creds) &&
|
||||
!Curl_checkProxyheaders(data, conn,
|
||||
STRCONST("Proxy-authorization"))) ||
|
||||
#endif
|
||||
(!proxy && data->state.aptr.user &&
|
||||
(!proxy && data->state.creds &&
|
||||
Curl_creds_has_user_or_pass(data->state.creds) &&
|
||||
!Curl_checkheaders(data, STRCONST("Authorization")))) {
|
||||
auth = "Basic";
|
||||
result = http_output_basic(data, proxy);
|
||||
result = http_output_basic(data, conn, proxy);
|
||||
if(result)
|
||||
return result;
|
||||
}
|
||||
@@ -714,8 +723,7 @@ static CURLcode output_auth_headers(struct Curl_easy *data,
|
||||
#ifndef CURL_DISABLE_BEARER_AUTH
|
||||
if(authstatus->picked == CURLAUTH_BEARER) {
|
||||
/* Bearer */
|
||||
if(!proxy && data->set.str[STRING_BEARER] &&
|
||||
Curl_auth_allowed_to_host(data) &&
|
||||
if(!proxy && Curl_creds_has_oauth_bearer(data->state.creds) &&
|
||||
!Curl_checkheaders(data, STRCONST("Authorization"))) {
|
||||
auth = "Bearer";
|
||||
result = http_output_bearer(data);
|
||||
@@ -737,15 +745,15 @@ static CURLcode output_auth_headers(struct Curl_easy *data,
|
||||
data->info.httpauthpicked = authstatus->picked;
|
||||
infof(data, "%s auth using %s with user '%s'",
|
||||
proxy ? "Proxy" : "Server", auth,
|
||||
proxy ? (data->state.aptr.proxyuser ?
|
||||
data->state.aptr.proxyuser : "") :
|
||||
(data->state.aptr.user ?
|
||||
data->state.aptr.user : ""));
|
||||
proxy ? (conn->http_proxy.creds ?
|
||||
conn->http_proxy.creds->user : "") :
|
||||
(data->state.creds ?
|
||||
data->state.creds->user : ""));
|
||||
#else
|
||||
(void)proxy;
|
||||
infof(data, "Server auth using %s with user '%s'",
|
||||
auth, data->state.aptr.user ?
|
||||
data->state.aptr.user : "");
|
||||
auth, data->state.creds ?
|
||||
data->state.creds->user : "");
|
||||
#endif
|
||||
authstatus->multipass = !authstatus->done;
|
||||
}
|
||||
@@ -760,53 +768,47 @@ static CURLcode output_auth_headers(struct Curl_easy *data,
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Curl_http_output_auth() setups the authentication headers for the
|
||||
* host/proxy and the correct authentication
|
||||
* method. data->state.authdone is set to TRUE when authentication is
|
||||
* done.
|
||||
*
|
||||
* @param conn all information about the current connection
|
||||
* @param request pointer to the request keyword
|
||||
* @param path pointer to the requested path; should include query part
|
||||
* @param proxytunnel boolean if this is the request setting up a "proxy
|
||||
* tunnel"
|
||||
*
|
||||
* @returns CURLcode
|
||||
*/
|
||||
CURLcode Curl_http_output_auth(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
const char *request,
|
||||
Curl_HttpReq httpreq,
|
||||
const char *path,
|
||||
bool proxytunnel) /* TRUE if this is
|
||||
the request setting up
|
||||
the proxy tunnel */
|
||||
const char *query,
|
||||
bool is_connect)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
struct auth *authhost;
|
||||
struct auth *authproxy;
|
||||
const char *path_and_query = path;
|
||||
char *tmp_str = NULL;
|
||||
|
||||
DEBUGASSERT(data);
|
||||
|
||||
authhost = &data->state.authhost;
|
||||
authproxy = &data->state.authproxy;
|
||||
|
||||
if(
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
(conn->bits.httpproxy && conn->bits.proxy_user_passwd) ||
|
||||
(!conn->http_proxy.peer || !conn->http_proxy.creds) &&
|
||||
#endif
|
||||
data->state.aptr.user ||
|
||||
#ifdef USE_SPNEGO
|
||||
authhost->want & CURLAUTH_NEGOTIATE ||
|
||||
authproxy->want & CURLAUTH_NEGOTIATE ||
|
||||
!(authhost->want & CURLAUTH_NEGOTIATE) &&
|
||||
!(authproxy->want & CURLAUTH_NEGOTIATE) &&
|
||||
#endif
|
||||
data->set.str[STRING_BEARER])
|
||||
/* continue please */;
|
||||
else {
|
||||
!data->state.creds) {
|
||||
/* no authentication with no user or password */
|
||||
authhost->done = TRUE;
|
||||
authproxy->done = TRUE;
|
||||
return CURLE_OK; /* no authentication with no user or password */
|
||||
result = CURLE_OK;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(query) {
|
||||
tmp_str = curl_maprintf("%s?%s", path, query);
|
||||
if(!tmp_str) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
path_and_query = tmp_str;
|
||||
}
|
||||
|
||||
if(authhost->want && !authhost->picked)
|
||||
@@ -823,28 +825,25 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data,
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
/* Send proxy authentication header if needed */
|
||||
if(conn->bits.httpproxy &&
|
||||
(conn->bits.tunnel_proxy == (curl_bit)proxytunnel)) {
|
||||
result = output_auth_headers(data, conn, authproxy, request, path, TRUE);
|
||||
if(conn->bits.origin_is_proxy || is_connect) {
|
||||
result = output_auth_headers(data, conn, authproxy, request,
|
||||
path_and_query, TRUE);
|
||||
if(result)
|
||||
return result;
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
#else
|
||||
(void)proxytunnel;
|
||||
(void)is_connect;
|
||||
#endif /* CURL_DISABLE_PROXY */
|
||||
/* we have no proxy so let's pretend we are done authenticating
|
||||
with it */
|
||||
authproxy->done = TRUE;
|
||||
|
||||
/* To prevent the user+password to get sent to other than the original host
|
||||
due to a location-follow */
|
||||
if(Curl_auth_allowed_to_host(data)
|
||||
#ifndef CURL_DISABLE_NETRC
|
||||
|| conn->bits.netrc
|
||||
#endif
|
||||
)
|
||||
result = output_auth_headers(data, conn, authhost, request, path, FALSE);
|
||||
/* Either we have credentials for the origin we talk to or
|
||||
performing authentication is allowed here */
|
||||
if(data->state.creds || Curl_auth_allowed_to_host(data))
|
||||
result = output_auth_headers(data, conn, authhost, request,
|
||||
path_and_query, FALSE);
|
||||
else
|
||||
authhost->done = TRUE;
|
||||
|
||||
@@ -859,27 +858,31 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data,
|
||||
else
|
||||
data->req.authneg = FALSE;
|
||||
|
||||
out:
|
||||
curlx_free(tmp_str);
|
||||
return result;
|
||||
}
|
||||
|
||||
#else
|
||||
#else /* !CURL_DISABLE_HTTP_AUTH */
|
||||
/* when disabled */
|
||||
CURLcode Curl_http_output_auth(struct Curl_easy *data,
|
||||
struct connectdata *conn,
|
||||
const char *request,
|
||||
Curl_HttpReq httpreq,
|
||||
const char *path,
|
||||
bool proxytunnel)
|
||||
const char *query,
|
||||
bool is_connect)
|
||||
{
|
||||
(void)data;
|
||||
(void)conn;
|
||||
(void)request;
|
||||
(void)httpreq;
|
||||
(void)path;
|
||||
(void)proxytunnel;
|
||||
(void)query;
|
||||
(void)is_connect;
|
||||
return CURLE_OK;
|
||||
}
|
||||
#endif
|
||||
#endif /* !CURL_DISABLE_HTTP_AUTH, else */
|
||||
|
||||
#if defined(USE_SPNEGO) || defined(USE_NTLM) || \
|
||||
!defined(CURL_DISABLE_DIGEST_AUTH) || \
|
||||
@@ -1134,9 +1137,9 @@ static void http_switch_to_get(struct Curl_easy *data, int code)
|
||||
Curl_creader_set_rewind(data, FALSE);
|
||||
}
|
||||
|
||||
#define HTTPREQ_IS_POST(data) \
|
||||
((data)->state.httpreq == HTTPREQ_POST || \
|
||||
(data)->state.httpreq == HTTPREQ_POST_FORM || \
|
||||
#define HTTPREQ_IS_POST(data) \
|
||||
((data)->state.httpreq == HTTPREQ_POST || \
|
||||
(data)->state.httpreq == HTTPREQ_POST_FORM || \
|
||||
(data)->state.httpreq == HTTPREQ_POST_MIME)
|
||||
|
||||
CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
|
||||
@@ -1227,8 +1230,6 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
else {
|
||||
bool same_origin;
|
||||
CURLcode result;
|
||||
CURLU *u = curl_url();
|
||||
if(!u)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
@@ -1242,29 +1243,16 @@ CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
|
||||
return Curl_uc_to_curlcode(uc);
|
||||
}
|
||||
|
||||
same_origin = Curl_url_same_origin(u, data->state.uh);
|
||||
curl_url_cleanup(u);
|
||||
|
||||
#ifndef CURL_DISABLE_DIGEST_AUTH
|
||||
if(!same_origin)
|
||||
Curl_auth_digest_cleanup(&data->state.digest);
|
||||
{
|
||||
bool same_origin = Curl_url_same_origin(u, data->state.uh);
|
||||
curl_url_cleanup(u);
|
||||
if(!same_origin)
|
||||
Curl_auth_digest_cleanup(&data->state.digest);
|
||||
}
|
||||
#else
|
||||
curl_url_cleanup(u);
|
||||
#endif
|
||||
|
||||
if((!same_origin && !data->set.allow_auth_to_other_hosts) ||
|
||||
!data->set.str[STRING_USERNAME]) {
|
||||
result = Curl_reset_userpwd(data);
|
||||
if(result) {
|
||||
curlx_free(follow_url);
|
||||
return result;
|
||||
}
|
||||
curlx_safefree(data->state.aptr.user);
|
||||
curlx_safefree(data->state.aptr.passwd);
|
||||
}
|
||||
result = Curl_reset_proxypwd(data);
|
||||
if(result) {
|
||||
curlx_free(follow_url);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
DEBUGASSERT(follow_url);
|
||||
|
||||
@@ -1753,8 +1741,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data,
|
||||
if(is_connect)
|
||||
proxy = HEADER_CONNECT;
|
||||
else
|
||||
proxy = data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy ?
|
||||
HEADER_PROXY : HEADER_SERVER;
|
||||
proxy = data->conn->bits.origin_is_proxy ? HEADER_PROXY : HEADER_SERVER;
|
||||
|
||||
switch(proxy) {
|
||||
case HEADER_SERVER:
|
||||
@@ -1773,6 +1760,12 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data,
|
||||
else
|
||||
h[0] = data->set.headers;
|
||||
break;
|
||||
case HEADER_CONNECT_UDP:
|
||||
if(data->set.sep_headers)
|
||||
h[0] = data->set.proxyheaders;
|
||||
else
|
||||
h[0] = data->set.headers;
|
||||
break;
|
||||
}
|
||||
#else
|
||||
(void)is_connect;
|
||||
@@ -1992,10 +1985,8 @@ static CURLcode http_useragent(struct Curl_easy *data)
|
||||
it might have been used in the proxy connect, but if we have got a header
|
||||
with the user-agent string specified, we erase the previously made string
|
||||
here. */
|
||||
if(Curl_checkheaders(data, STRCONST("User-Agent"))) {
|
||||
curlx_free(data->state.aptr.uagent);
|
||||
data->state.aptr.uagent = NULL;
|
||||
}
|
||||
if(Curl_checkheaders(data, STRCONST("User-Agent")))
|
||||
curlx_safefree(data->state.aptr.uagent);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
@@ -2005,25 +1996,15 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data)
|
||||
struct dynamically_allocated_data *aptr = &data->state.aptr;
|
||||
const char *ptr;
|
||||
|
||||
if(!data->state.this_is_a_follow) {
|
||||
/* Free to avoid leaking memory on multiple requests */
|
||||
curlx_free(data->state.first_host);
|
||||
|
||||
data->state.first_host = curlx_strdup(conn->host.name);
|
||||
if(!data->state.first_host)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
data->state.first_remote_port = conn->remote_port;
|
||||
data->state.first_remote_protocol = conn->scheme->protocol;
|
||||
}
|
||||
curlx_safefree(aptr->host);
|
||||
#ifndef CURL_DISABLE_COOKIES
|
||||
curlx_safefree(data->req.cookiehost);
|
||||
#endif
|
||||
|
||||
ptr = Curl_checkheaders(data, STRCONST("Host"));
|
||||
if(ptr && (!data->state.this_is_a_follow ||
|
||||
curl_strequal(data->state.first_host, conn->host.name))) {
|
||||
if(ptr &&
|
||||
(!data->state.this_is_a_follow ||
|
||||
Curl_peer_equal(data->state.initial_origin, data->state.origin))) {
|
||||
#ifndef CURL_DISABLE_COOKIES
|
||||
/* If we have a given custom Host: header, we extract the hostname in
|
||||
order to possibly use it for cookie reasons later on. We only allow the
|
||||
@@ -2067,18 +2048,19 @@ static CURLcode http_set_aptr_host(struct Curl_easy *data)
|
||||
}
|
||||
else {
|
||||
/* Use the hostname as present in the URL if it was IPv6. */
|
||||
char *host = (data->state.up.hostname[0] == '[') ?
|
||||
data->state.up.hostname : conn->host.name;
|
||||
char *host = (data->state.origin->user_hostname[0] == '[') ?
|
||||
data->state.origin->user_hostname : data->state.origin->hostname;
|
||||
|
||||
if(((conn->given->protocol & (CURLPROTO_HTTPS | CURLPROTO_WSS)) &&
|
||||
(conn->remote_port == PORT_HTTPS)) ||
|
||||
(data->state.origin->port == PORT_HTTPS)) ||
|
||||
((conn->given->protocol & (CURLPROTO_HTTP | CURLPROTO_WS)) &&
|
||||
(conn->remote_port == PORT_HTTP)))
|
||||
(data->state.origin->port == PORT_HTTP)))
|
||||
/* if(HTTPS on port 443) OR (HTTP on port 80) then do not include
|
||||
the port number in the host string */
|
||||
aptr->host = curl_maprintf("Host: %s\r\n", host);
|
||||
else
|
||||
aptr->host = curl_maprintf("Host: %s:%d\r\n", host, conn->remote_port);
|
||||
aptr->host = curl_maprintf("Host: %s:%d\r\n",
|
||||
host, data->state.origin->port);
|
||||
|
||||
if(!aptr->host)
|
||||
/* without Host: we cannot make a nice request */
|
||||
@@ -2106,7 +2088,7 @@ static CURLcode http_target(struct Curl_easy *data,
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
|
||||
if(conn->bits.origin_is_proxy) {
|
||||
/* Using a proxy but does not tunnel through it */
|
||||
|
||||
/* The path sent to the proxy is in fact the entire URL, but if the remote
|
||||
@@ -2120,8 +2102,8 @@ static CURLcode http_target(struct Curl_easy *data,
|
||||
if(!h)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
if(conn->host.dispname != conn->host.name) {
|
||||
uc = curl_url_set(h, CURLUPART_HOST, conn->host.name, 0);
|
||||
if(data->state.origin->user_hostname != data->state.origin->hostname) {
|
||||
uc = curl_url_set(h, CURLUPART_HOST, data->state.origin->hostname, 0);
|
||||
if(uc) {
|
||||
curl_url_cleanup(h);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
@@ -2146,6 +2128,19 @@ static CURLcode http_target(struct Curl_easy *data,
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
else if(data->state.creds && (data->state.creds->source != CREDS_URL)) {
|
||||
/* credentials not from the URL need to be set */
|
||||
uc = curl_url_set(h, CURLUPART_USER,
|
||||
data->state.creds->user, CURLU_URLENCODE);
|
||||
if(!uc)
|
||||
uc = curl_url_set(h, CURLUPART_PASSWORD,
|
||||
data->state.creds->passwd, CURLU_URLENCODE);
|
||||
if(uc) {
|
||||
curl_url_cleanup(h);
|
||||
return Curl_uc_to_curlcode(uc);
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract the URL to use in the request. */
|
||||
uc = curl_url_get(h, CURLUPART_URL, &url, CURLU_NO_DEFAULT_PORT);
|
||||
if(uc) {
|
||||
@@ -2541,7 +2536,8 @@ static CURLcode http_cookies(struct Curl_easy *data,
|
||||
char *addcookies = NULL;
|
||||
bool linecap = FALSE;
|
||||
if(data->set.str[STRING_COOKIE] &&
|
||||
!Curl_checkheaders(data, STRCONST("Cookie")))
|
||||
!Curl_checkheaders(data, STRCONST("Cookie")) &&
|
||||
Curl_auth_allowed_to_host(data))
|
||||
addcookies = data->set.str[STRING_COOKIE];
|
||||
|
||||
if(data->cookies || addcookies) {
|
||||
@@ -2551,9 +2547,9 @@ static CURLcode http_cookies(struct Curl_easy *data,
|
||||
if(data->cookies && data->state.cookie_engine) {
|
||||
bool okay;
|
||||
const char *host = data->req.cookiehost ?
|
||||
data->req.cookiehost : data->conn->host.name;
|
||||
data->req.cookiehost : data->state.origin->hostname;
|
||||
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
|
||||
result = Curl_cookie_getlist(data, data->conn, &okay, host, &list);
|
||||
result = Curl_cookie_getlist(data, &okay, host, &list);
|
||||
if(!result && okay) {
|
||||
struct Curl_llist_node *n;
|
||||
size_t clen = 8; /* hold the size of the generated Cookie: header */
|
||||
@@ -2737,13 +2733,16 @@ static CURLcode http_check_new_conn(struct Curl_easy *data)
|
||||
|
||||
alpn = Curl_conn_get_alpn_negotiated(data, conn);
|
||||
if(alpn && !strcmp("h3", alpn)) {
|
||||
DEBUGASSERT(Curl_conn_http_version(data, conn) == 30);
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(!conn->bits.origin_is_proxy)
|
||||
#endif
|
||||
DEBUGASSERT(Curl_conn_http_version(data, conn) == 30);
|
||||
info_version = "HTTP/3";
|
||||
}
|
||||
else if(alpn && !strcmp("h2", alpn)) {
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if((Curl_conn_http_version(data, conn) != 20) &&
|
||||
conn->bits.proxy && !conn->bits.tunnel_proxy) {
|
||||
conn->bits.origin_is_proxy) {
|
||||
result = Curl_http2_switch(data);
|
||||
if(result)
|
||||
return result;
|
||||
@@ -2842,7 +2841,7 @@ typedef enum {
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
H1_HD_PROXY_AUTH,
|
||||
#endif
|
||||
H1_HD_USER_AUTH,
|
||||
H1_HD_AUTH,
|
||||
H1_HD_RANGE,
|
||||
H1_HD_USER_AGENT,
|
||||
H1_HD_ACCEPT,
|
||||
@@ -2897,14 +2896,14 @@ static CURLcode http_add_hd(struct Curl_easy *data,
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
case H1_HD_PROXY_AUTH:
|
||||
if(data->req.proxyuserpwd)
|
||||
result = curlx_dyn_add(req, data->req.proxyuserpwd);
|
||||
if(data->req.hd_proxy_auth)
|
||||
result = curlx_dyn_add(req, data->req.hd_proxy_auth);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case H1_HD_USER_AUTH:
|
||||
if(data->req.userpwd)
|
||||
result = curlx_dyn_add(req, data->req.userpwd);
|
||||
case H1_HD_AUTH:
|
||||
if(data->req.hd_auth)
|
||||
result = curlx_dyn_add(req, data->req.hd_auth);
|
||||
break;
|
||||
|
||||
case H1_HD_RANGE:
|
||||
@@ -2952,8 +2951,7 @@ static CURLcode http_add_hd(struct Curl_easy *data,
|
||||
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
case H1_HD_PROXY_CONNECTION:
|
||||
if(conn->bits.httpproxy &&
|
||||
!conn->bits.tunnel_proxy &&
|
||||
if(conn->bits.origin_is_proxy &&
|
||||
!Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
|
||||
!Curl_checkProxyheaders(data, data->conn, STRCONST("Proxy-Connection")))
|
||||
result = curlx_dyn_add(req, "Proxy-Connection: Keep-Alive\r\n");
|
||||
@@ -2966,10 +2964,10 @@ static CURLcode http_add_hd(struct Curl_easy *data,
|
||||
|
||||
#ifndef CURL_DISABLE_ALTSVC
|
||||
case H1_HD_ALT_USED:
|
||||
if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used")))
|
||||
if(conn->bits.altused && conn->via_peer &&
|
||||
!Curl_checkheaders(data, STRCONST("Alt-Used")))
|
||||
result = curlx_dyn_addf(req, "Alt-Used: %s:%u\r\n",
|
||||
conn->conn_to_host.name,
|
||||
conn->conn_to_port);
|
||||
conn->via_peer->hostname, conn->via_peer->port);
|
||||
break;
|
||||
#endif
|
||||
|
||||
@@ -3062,29 +3060,16 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
|
||||
|
||||
/* select host to send */
|
||||
result = http_set_aptr_host(data);
|
||||
if(!result) {
|
||||
/* setup the authentication headers, how that method and host are known */
|
||||
char *pq = NULL;
|
||||
if(data->state.up.query) {
|
||||
pq = curl_maprintf("%s?%s", data->state.up.path, data->state.up.query);
|
||||
if(!pq) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
/* setup the authentication headers, how that method and host are known */
|
||||
if(!result)
|
||||
result = Curl_http_output_auth(data, data->conn, method, httpreq,
|
||||
(pq ? pq : data->state.up.path), FALSE);
|
||||
curlx_free(pq);
|
||||
}
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
result = http_useragent(data);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
data->state.up.path,
|
||||
data->state.up.query, FALSE);
|
||||
if(!result)
|
||||
result = http_useragent(data);
|
||||
/* Setup input reader, resume information and ranges */
|
||||
result = set_reader(data, httpreq);
|
||||
if(!result)
|
||||
result = set_reader(data, httpreq);
|
||||
if(!result)
|
||||
result = http_resume(data, httpreq);
|
||||
if(!result)
|
||||
@@ -3209,7 +3194,6 @@ static CURLcode http_header_a(struct Curl_easy *data,
|
||||
{
|
||||
#ifndef CURL_DISABLE_ALTSVC
|
||||
const char *v;
|
||||
struct connectdata *conn = data->conn;
|
||||
v = (data->asi &&
|
||||
(Curl_xfer_is_secure(data) ||
|
||||
#ifdef DEBUGBUILD
|
||||
@@ -3224,8 +3208,9 @@ static CURLcode http_header_a(struct Curl_easy *data,
|
||||
struct SingleRequest *k = &data->req;
|
||||
enum alpnid id = (k->httpversion == 30) ? ALPN_h3 :
|
||||
(k->httpversion == 20) ? ALPN_h2 : ALPN_h1;
|
||||
return Curl_altsvc_parse(data, data->asi, v, id, conn->host.name,
|
||||
curlx_uitous((unsigned int)conn->remote_port));
|
||||
return Curl_altsvc_parse(
|
||||
data, data->asi, v, id, data->state.origin->hostname,
|
||||
curlx_uitous((unsigned int)data->state.origin->port));
|
||||
}
|
||||
#else
|
||||
(void)data;
|
||||
@@ -3443,7 +3428,7 @@ static CURLcode http_header_p(struct Curl_easy *data,
|
||||
const char *v = HD_VAL(hd, hdlen, "Proxy-Connection:");
|
||||
if(v) {
|
||||
struct connectdata *conn = data->conn;
|
||||
if((k->httpversion == 10) && conn->bits.httpproxy &&
|
||||
if((k->httpversion == 10) && conn->http_proxy.peer &&
|
||||
HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) {
|
||||
/*
|
||||
* When an HTTP/1.0 reply comes when using a proxy, the
|
||||
@@ -3454,7 +3439,7 @@ static CURLcode http_header_p(struct Curl_easy *data,
|
||||
connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */
|
||||
infof(data, "HTTP/1.0 proxy connection set to keep alive");
|
||||
}
|
||||
else if((k->httpversion == 11) && conn->bits.httpproxy &&
|
||||
else if((k->httpversion == 11) && conn->http_proxy.peer &&
|
||||
HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) {
|
||||
/*
|
||||
* We get an HTTP/1.1 response from a proxy and it says it will
|
||||
@@ -3537,7 +3522,6 @@ static CURLcode http_header_s(struct Curl_easy *data,
|
||||
const char *hd, size_t hdlen)
|
||||
{
|
||||
#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_HSTS)
|
||||
struct connectdata *conn = data->conn;
|
||||
const char *v;
|
||||
#else
|
||||
(void)data;
|
||||
@@ -3552,8 +3536,8 @@ static CURLcode http_header_s(struct Curl_easy *data,
|
||||
/* If there is a custom-set Host: name, use it here, or else use
|
||||
* real peer hostname. */
|
||||
const char *host = data->req.cookiehost ?
|
||||
data->req.cookiehost : conn->host.name;
|
||||
const bool secure_context = Curl_secure_context(conn, host);
|
||||
data->req.cookiehost : data->state.origin->hostname;
|
||||
const bool secure_context = Curl_secure_context(data, host);
|
||||
CURLcode result;
|
||||
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
|
||||
result = Curl_cookie_add(data, data->cookies, TRUE, FALSE, v, host,
|
||||
@@ -3575,8 +3559,8 @@ static CURLcode http_header_s(struct Curl_easy *data,
|
||||
)
|
||||
) ? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL;
|
||||
if(v) {
|
||||
CURLcode result =
|
||||
Curl_hsts_parse(data->hsts, conn->host.name, v);
|
||||
CURLcode result = Curl_hsts_parse(
|
||||
data->hsts, data->state.origin->hostname, v);
|
||||
if(result) {
|
||||
if(result == CURLE_OUT_OF_MEMORY)
|
||||
return result;
|
||||
@@ -3827,8 +3811,8 @@ static CURLcode http_size(struct Curl_easy *data)
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode verify_header(struct Curl_easy *data,
|
||||
const char *hd, size_t hdlen)
|
||||
CURLcode Curl_verify_header(struct Curl_easy *data,
|
||||
const char *hd, size_t hdlen)
|
||||
{
|
||||
struct SingleRequest *k = &data->req;
|
||||
const char *ptr = memchr(hd, 0x00, hdlen);
|
||||
@@ -3837,6 +3821,14 @@ static CURLcode verify_header(struct Curl_easy *data,
|
||||
failf(data, "Nul byte in header");
|
||||
return CURLE_WEIRD_SERVER_REPLY;
|
||||
}
|
||||
if(hdlen > 2) {
|
||||
ptr = memchr(hd, '\r', hdlen - 2);
|
||||
if(ptr) {
|
||||
/* CR may only precede the LF, nothing else */
|
||||
failf(data, "Carriage return found in header");
|
||||
return CURLE_WEIRD_SERVER_REPLY;
|
||||
}
|
||||
}
|
||||
if(k->headerline < 2)
|
||||
/* the first "header" is the status-line and it has no colon */
|
||||
return CURLE_OK;
|
||||
@@ -4191,6 +4183,17 @@ static CURLcode http_on_response(struct Curl_easy *data,
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* final response without error, prepare to receive the body */
|
||||
result = http_firstwrite(data);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
/* This is the last response that we get for the current request. Check on
|
||||
* the body size and determine if the response is complete. */
|
||||
result = http_size(data);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
/* If we requested a "no body", this is a good time to get
|
||||
* out and return home.
|
||||
*/
|
||||
@@ -4204,14 +4207,6 @@ static CURLcode http_on_response(struct Curl_easy *data,
|
||||
if((k->maxdownload == 0) && (k->httpversion_sent < 20))
|
||||
k->download_done = TRUE;
|
||||
|
||||
/* final response without error, prepare to receive the body */
|
||||
result = http_firstwrite(data);
|
||||
|
||||
if(!result)
|
||||
/* This is the last response that we get for the current request. Check on
|
||||
* the body size and determine if the response is complete. */
|
||||
result = http_size(data);
|
||||
|
||||
out:
|
||||
if(last_hd)
|
||||
/* if not written yet, write it now */
|
||||
@@ -4367,7 +4362,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
|
||||
}
|
||||
}
|
||||
|
||||
result = verify_header(data, hd, hdlen);
|
||||
result = Curl_verify_header(data, hd, hdlen);
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
@@ -4876,7 +4871,6 @@ struct name_const {
|
||||
size_t namelen;
|
||||
};
|
||||
|
||||
/* keep them sorted by length! */
|
||||
static const struct name_const H2_NON_FIELD[] = {
|
||||
{ STRCONST("Host") },
|
||||
{ STRCONST("Upgrade") },
|
||||
@@ -4890,10 +4884,8 @@ static bool h2_permissible_field(struct dynhds_entry *e)
|
||||
{
|
||||
size_t i;
|
||||
for(i = 0; i < CURL_ARRAYSIZE(H2_NON_FIELD); ++i) {
|
||||
if(e->namelen < H2_NON_FIELD[i].namelen)
|
||||
return TRUE;
|
||||
if(e->namelen == H2_NON_FIELD[i].namelen &&
|
||||
curl_strequal(H2_NON_FIELD[i].name, e->name))
|
||||
curl_strnequal(H2_NON_FIELD[i].name, e->name, e->namelen))
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
@@ -4950,7 +4942,7 @@ CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers,
|
||||
infof(data, "set pseudo header %s to %s", HTTP_PSEUDO_SCHEME, scheme);
|
||||
}
|
||||
else {
|
||||
scheme = Curl_xfer_is_secure(data) ? "https" : "http";
|
||||
scheme = data->state.origin->scheme->name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -83,8 +83,6 @@ char *Curl_checkProxyheaders(struct Curl_easy *data,
|
||||
CURLcode Curl_add_timecondition(struct Curl_easy *data, struct dynbuf *req);
|
||||
CURLcode Curl_add_custom_headers(struct Curl_easy *data, bool is_connect,
|
||||
int httpversion, struct dynbuf *req);
|
||||
CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, bool is_connect,
|
||||
struct dynhds *hds);
|
||||
|
||||
void Curl_http_to_fold(struct dynbuf *bf);
|
||||
|
||||
@@ -108,6 +106,11 @@ CURLcode Curl_http_write_resp_hd(struct Curl_easy *data,
|
||||
const char *hd, size_t hdlen,
|
||||
bool is_eos);
|
||||
|
||||
/* check a received header line for forbidden bytes/format, the same checks
|
||||
applied to regular response headers */
|
||||
CURLcode Curl_verify_header(struct Curl_easy *data,
|
||||
const char *hd, size_t hdlen);
|
||||
|
||||
/* These functions are in http.c */
|
||||
CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
|
||||
const char *auth);
|
||||
@@ -180,8 +183,9 @@ CURLcode Curl_http_write_resp_hds(struct Curl_easy *data,
|
||||
* @param request pointer to the request keyword
|
||||
* @param httpreq is the request type
|
||||
* @param path pointer to the requested path
|
||||
* @param proxytunnel boolean if this is the request setting up a "proxy
|
||||
* tunnel"
|
||||
* @param query pointer to the requested query or NULL
|
||||
* @param is_connect boolean if this is a CONNECT request
|
||||
* (where httpreq is HTTPREQ_GET since there is no HTTPREQ_CONNECT)
|
||||
*
|
||||
* @returns CURLcode
|
||||
*/
|
||||
@@ -190,9 +194,8 @@ CURLcode Curl_http_output_auth(struct Curl_easy *data,
|
||||
const char *request,
|
||||
Curl_HttpReq httpreq,
|
||||
const char *path,
|
||||
bool proxytunnel); /* TRUE if this is
|
||||
the request setting up
|
||||
the proxy tunnel */
|
||||
const char *query,
|
||||
bool is_connect);
|
||||
|
||||
/* Decode HTTP status code string. */
|
||||
CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len);
|
||||
|
||||
+65
-100
@@ -47,14 +47,11 @@
|
||||
#include "curlx/dynbuf.h"
|
||||
#include "headers.h"
|
||||
|
||||
#if (NGHTTP2_VERSION_NUM < 0x010c00)
|
||||
#error too old nghttp2 version, upgrade!
|
||||
#if NGHTTP2_VERSION_NUM < 0x010f00
|
||||
#error "nghttp2 1.15.0 or greater required"
|
||||
#endif
|
||||
|
||||
#if (NGHTTP2_VERSION_NUM >= 0x010c00)
|
||||
#define NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE 1
|
||||
#endif
|
||||
|
||||
|
||||
/* buffer dimensioning:
|
||||
* use 16K as chunk size, as that fits H2 DATA frames well */
|
||||
@@ -189,6 +186,8 @@ static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade)
|
||||
static void cf_h2_ctx_free(struct cf_h2_ctx *ctx)
|
||||
{
|
||||
if(ctx && ctx->initialized) {
|
||||
if(ctx->h2)
|
||||
nghttp2_session_del(ctx->h2);
|
||||
Curl_bufq_free(&ctx->inbufq);
|
||||
Curl_bufq_free(&ctx->outbufq);
|
||||
Curl_bufcp_free(&ctx->stream_bufcp);
|
||||
@@ -199,14 +198,6 @@ static void cf_h2_ctx_free(struct cf_h2_ctx *ctx)
|
||||
curlx_free(ctx);
|
||||
}
|
||||
|
||||
static void cf_h2_ctx_close(struct cf_h2_ctx *ctx)
|
||||
{
|
||||
if(ctx->h2) {
|
||||
nghttp2_session_del(ctx->h2);
|
||||
ctx->h2 = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t cf_h2_initial_win_size(struct Curl_easy *data)
|
||||
{
|
||||
#if NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE
|
||||
@@ -221,8 +212,8 @@ static uint32_t cf_h2_initial_win_size(struct Curl_easy *data)
|
||||
}
|
||||
|
||||
static size_t populate_settings(nghttp2_settings_entry *iv,
|
||||
struct Curl_easy *data,
|
||||
struct cf_h2_ctx *ctx)
|
||||
struct Curl_easy *data,
|
||||
struct cf_h2_ctx *ctx)
|
||||
{
|
||||
iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
|
||||
iv[0].value = Curl_multi_max_concurrent_streams(data->multi);
|
||||
@@ -232,7 +223,7 @@ static size_t populate_settings(nghttp2_settings_entry *iv,
|
||||
if(ctx)
|
||||
ctx->initial_win_size = iv[1].value;
|
||||
iv[2].settings_id = NGHTTP2_SETTINGS_ENABLE_PUSH;
|
||||
iv[2].value = data->multi->push_cb != NULL;
|
||||
iv[2].value = !!data->multi->push_cb;
|
||||
|
||||
return 3;
|
||||
}
|
||||
@@ -469,8 +460,7 @@ static int h2_client_new(struct Curl_cfilter *cf,
|
||||
return rc;
|
||||
/* We handle window updates ourself to enforce buffer limits */
|
||||
nghttp2_option_set_no_auto_window_update(o, 1);
|
||||
#if NGHTTP2_VERSION_NUM >= 0x013200
|
||||
/* with 1.50.0 */
|
||||
#if NGHTTP2_VERSION_NUM >= 0x013200 /* with 1.50.0 */
|
||||
/* turn off RFC 9113 leading and trailing white spaces validation against
|
||||
HTTP field value. */
|
||||
nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1);
|
||||
@@ -859,7 +849,7 @@ static int push_promise(struct Curl_cfilter *cf,
|
||||
|
||||
result = http2_data_setup(cf, newhandle, &newstream);
|
||||
if(result) {
|
||||
failf(data, "error setting up stream: %d", result);
|
||||
failf(data, "error setting up stream: %d", (int)result);
|
||||
discard_newhandle(cf, newhandle);
|
||||
rv = CURL_PUSH_DENY;
|
||||
goto fail;
|
||||
@@ -908,7 +898,7 @@ static void h2_xfer_write_resp_hd(struct Curl_cfilter *cf,
|
||||
stream->xfer_result = cf_h2_update_local_win(cf, data, stream);
|
||||
if(stream->xfer_result)
|
||||
CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of headers",
|
||||
stream->id, stream->xfer_result, blen);
|
||||
stream->id, (int)stream->xfer_result, blen);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -925,7 +915,7 @@ static void h2_xfer_write_resp(struct Curl_cfilter *cf,
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of data, "
|
||||
"RST-ing stream",
|
||||
stream->id, stream->xfer_result, blen);
|
||||
stream->id, (int)stream->xfer_result, blen);
|
||||
nghttp2_submit_rst_stream(ctx->h2, 0, stream->id,
|
||||
(uint32_t)NGHTTP2_ERR_CALLBACK_FAILURE);
|
||||
}
|
||||
@@ -1389,7 +1379,7 @@ static void cf_h2_header_error(struct Curl_cfilter *cf,
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
|
||||
failf(data, "Error receiving HTTP2 header: %d(%s)", result,
|
||||
failf(data, "Error receiving HTTP2 header: %d(%s)", (int)result,
|
||||
curl_easy_strerror(result));
|
||||
if(stream) {
|
||||
nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
|
||||
@@ -1409,7 +1399,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
struct Curl_cfilter *cf = userp;
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
struct h2_stream_ctx *stream;
|
||||
struct Curl_easy *data_s;
|
||||
struct Curl_easy *data;
|
||||
int32_t stream_id = frame->hd.stream_id;
|
||||
CURLcode result;
|
||||
(void)flags;
|
||||
@@ -1417,15 +1407,15 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
DEBUGASSERT(stream_id); /* should never be a zero stream ID here */
|
||||
|
||||
/* get the stream from the hash based on Stream ID */
|
||||
data_s = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if(!GOOD_EASY_HANDLE(data_s))
|
||||
data = nghttp2_session_get_stream_user_data(session, stream_id);
|
||||
if(!GOOD_EASY_HANDLE(data))
|
||||
/* Receiving a Stream ID not in the hash should not happen, this is an
|
||||
internal error more than anything else! */
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
|
||||
stream = H2_STREAM_CTX(ctx, data_s);
|
||||
stream = H2_STREAM_CTX(ctx, data);
|
||||
if(!stream) {
|
||||
failf(data_s, "Internal NULL stream");
|
||||
failf(data, "Internal NULL stream");
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
@@ -1438,14 +1428,14 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
!strncmp(HTTP_PSEUDO_AUTHORITY, (const char *)name, namelen)) {
|
||||
/* pseudo headers are lower case */
|
||||
int rc = 0;
|
||||
char *check = curl_maprintf("%s:%d", cf->conn->host.name,
|
||||
cf->conn->remote_port);
|
||||
char *check = curl_maprintf("%s:%d", data->state.origin->hostname,
|
||||
data->state.origin->port);
|
||||
if(!check)
|
||||
/* no memory */
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
if(!curl_strequal(check, (const char *)value) &&
|
||||
((cf->conn->remote_port != cf->conn->given->defport) ||
|
||||
!curl_strequal(cf->conn->host.name, (const char *)value))) {
|
||||
((data->state.origin->port != cf->conn->given->defport) ||
|
||||
!curl_strequal(data->state.origin->hostname, (const char *)value))) {
|
||||
/* This is push is not for the same authority that was asked for in
|
||||
* the URL. RFC 7540 section 8.2 says: "A client MUST treat a
|
||||
* PUSH_PROMISE for which the server is not authoritative as a stream
|
||||
@@ -1473,7 +1463,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
char **headp;
|
||||
if(stream->push_headers_alloc > 1000) {
|
||||
/* this is beyond crazy many headers, bail out */
|
||||
failf(data_s, "Too many PUSH_PROMISE headers");
|
||||
failf(data, "Too many PUSH_PROMISE headers");
|
||||
free_push_headers(stream);
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
@@ -1497,13 +1487,13 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
|
||||
if(stream->bodystarted) {
|
||||
/* This is a trailer */
|
||||
CURL_TRC_CF(data_s, cf, "[%d] trailer: %.*s: %.*s",
|
||||
CURL_TRC_CF(data, cf, "[%d] trailer: %.*s: %.*s",
|
||||
stream->id, (int)namelen, name, (int)valuelen, value);
|
||||
result = Curl_dynhds_add(&stream->resp_trailers,
|
||||
(const char *)name, namelen,
|
||||
(const char *)value, valuelen);
|
||||
if(result) {
|
||||
cf_h2_header_error(cf, data_s, stream, result);
|
||||
cf_h2_header_error(cf, data, stream, result);
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
@@ -1511,21 +1501,21 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
}
|
||||
|
||||
if(namelen == sizeof(HTTP_PSEUDO_STATUS) - 1 &&
|
||||
memcmp(HTTP_PSEUDO_STATUS, name, namelen) == 0) {
|
||||
!memcmp(HTTP_PSEUDO_STATUS, name, namelen)) {
|
||||
/* nghttp2 guarantees :status is received first and only once. */
|
||||
char buffer[32];
|
||||
size_t hlen;
|
||||
result = Curl_http_decode_status(&stream->status_code,
|
||||
(const char *)value, valuelen);
|
||||
if(result) {
|
||||
cf_h2_header_error(cf, data_s, stream, result);
|
||||
cf_h2_header_error(cf, data, stream, result);
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
hlen = curl_msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%d\r",
|
||||
stream->status_code);
|
||||
result = Curl_headers_push(data_s, buffer, hlen, CURLH_PSEUDO);
|
||||
result = Curl_headers_push(data, buffer, hlen, CURLH_PSEUDO);
|
||||
if(result) {
|
||||
cf_h2_header_error(cf, data_s, stream, result);
|
||||
cf_h2_header_error(cf, data, stream, result);
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
curlx_dyn_reset(&ctx->scratch);
|
||||
@@ -1535,17 +1525,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
if(!result)
|
||||
result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
|
||||
if(!result)
|
||||
h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch),
|
||||
h2_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch),
|
||||
curlx_dyn_len(&ctx->scratch), FALSE);
|
||||
if(result) {
|
||||
cf_h2_header_error(cf, data_s, stream, result);
|
||||
cf_h2_header_error(cf, data, stream, result);
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
/* if we receive data for another handle, wake that up */
|
||||
if(CF_DATA_CURRENT(cf) != data_s)
|
||||
Curl_multi_mark_dirty(data_s);
|
||||
if(CF_DATA_CURRENT(cf) != data)
|
||||
Curl_multi_mark_dirty(data);
|
||||
|
||||
CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d",
|
||||
CURL_TRC_CF(data, cf, "[%d] status: HTTP/2 %03d",
|
||||
stream->id, stream->status_code);
|
||||
return 0;
|
||||
}
|
||||
@@ -1562,17 +1552,17 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
|
||||
if(!result)
|
||||
result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
|
||||
if(!result)
|
||||
h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch),
|
||||
h2_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch),
|
||||
curlx_dyn_len(&ctx->scratch), FALSE);
|
||||
if(result) {
|
||||
cf_h2_header_error(cf, data_s, stream, result);
|
||||
cf_h2_header_error(cf, data, stream, result);
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
/* if we receive data for another handle, wake that up */
|
||||
if(CF_DATA_CURRENT(cf) != data_s)
|
||||
Curl_multi_mark_dirty(data_s);
|
||||
if(CF_DATA_CURRENT(cf) != data)
|
||||
Curl_multi_mark_dirty(data);
|
||||
|
||||
CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s",
|
||||
CURL_TRC_CF(data, cf, "[%d] header: %.*s: %.*s",
|
||||
stream->id, (int)namelen, name, (int)valuelen, value);
|
||||
|
||||
return 0; /* 0 is successful */
|
||||
@@ -1620,7 +1610,7 @@ static ssize_t req_body_read_callback(nghttp2_session *session,
|
||||
nread = (ssize_t)n;
|
||||
|
||||
CURL_TRC_CF(data_s, cf, "[%d] req_body_read(len=%zu) eos=%d -> %zd, %d",
|
||||
stream_id, length, stream->body_eos, nread, result);
|
||||
stream_id, length, stream->body_eos, nread, (int)result);
|
||||
|
||||
if(stream->body_eos && Curl_bufq_is_empty(&stream->sendbuf)) {
|
||||
*data_flags = NGHTTP2_DATA_FLAG_EOF;
|
||||
@@ -1701,12 +1691,12 @@ static CURLcode http2_handle_stream_close(struct Curl_cfilter *cf,
|
||||
return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */
|
||||
}
|
||||
else if(stream->resp_hds_complete && data->req.no_body) {
|
||||
CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did "
|
||||
"not want a body anyway, ignore: %s (err %u)",
|
||||
stream->id, nghttp2_http2_strerror(stream->error),
|
||||
stream->error);
|
||||
stream->close_handled = TRUE;
|
||||
return CURLE_OK;
|
||||
CURL_TRC_CF(data, cf, "[%d] error after response headers, but we did "
|
||||
"not want a body anyway, ignore: %s (err %u)",
|
||||
stream->id, nghttp2_http2_strerror(stream->error),
|
||||
stream->error);
|
||||
stream->close_handled = TRUE;
|
||||
return CURLE_OK;
|
||||
}
|
||||
failf(data, "HTTP/2 stream %d reset by %s (error 0x%x %s)",
|
||||
stream->id, stream->reset_by_server ? "server" : "curl",
|
||||
@@ -1754,7 +1744,7 @@ static CURLcode http2_handle_stream_close(struct Curl_cfilter *cf,
|
||||
result = CURLE_OK;
|
||||
|
||||
out:
|
||||
CURL_TRC_CF(data, cf, "handle_stream_close -> %d, %zu", result, *pnlen);
|
||||
CURL_TRC_CF(data, cf, "handle_stream_close -> %d, %zu", (int)result, *pnlen);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1778,16 +1768,12 @@ static int sweight_in_effect(const struct Curl_easy *data)
|
||||
* struct.
|
||||
*/
|
||||
|
||||
static void h2_pri_spec(struct cf_h2_ctx *ctx,
|
||||
struct Curl_easy *data,
|
||||
static void h2_pri_spec(struct Curl_easy *data,
|
||||
nghttp2_priority_spec *pri_spec)
|
||||
{
|
||||
struct Curl_data_priority *prio = &data->set.priority;
|
||||
struct h2_stream_ctx *depstream = H2_STREAM_CTX(ctx, prio->parent);
|
||||
int32_t depstream_id = depstream ? depstream->id : 0;
|
||||
nghttp2_priority_spec_init(pri_spec, depstream_id,
|
||||
sweight_wanted(data),
|
||||
data->set.priority.exclusive);
|
||||
nghttp2_priority_spec_init(pri_spec, 0,
|
||||
sweight_wanted(data), FALSE);
|
||||
data->state.priority = *prio;
|
||||
}
|
||||
|
||||
@@ -1805,13 +1791,11 @@ static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
|
||||
int rv = 0;
|
||||
|
||||
if(stream && stream->id > 0 &&
|
||||
((sweight_wanted(data) != sweight_in_effect(data)) ||
|
||||
(data->set.priority.exclusive != data->state.priority.exclusive) ||
|
||||
(data->set.priority.parent != data->state.priority.parent))) {
|
||||
(sweight_wanted(data) != sweight_in_effect(data))) {
|
||||
/* send new weight and/or dependency */
|
||||
nghttp2_priority_spec pri_spec;
|
||||
|
||||
h2_pri_spec(ctx, data, &pri_spec);
|
||||
h2_pri_spec(data, &pri_spec);
|
||||
CURL_TRC_CF(data, cf, "[%d] Queuing PRIORITY", stream->id);
|
||||
DEBUGASSERT(stream->id != -1);
|
||||
rv = nghttp2_submit_priority(ctx->h2, NGHTTP2_FLAG_NONE,
|
||||
@@ -1869,7 +1853,7 @@ static CURLcode stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
|
||||
if(result && (result != CURLE_AGAIN))
|
||||
CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %d, %zu",
|
||||
stream->id, len, result, *pnread);
|
||||
stream->id, len, (int)result, *pnread);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1920,7 +1904,7 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
|
||||
result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread);
|
||||
if(result) {
|
||||
if(result != CURLE_AGAIN) {
|
||||
failf(data, "Failed receiving HTTP2 data: %d(%s)", result,
|
||||
failf(data, "Failed receiving HTTP2 data: %d(%s)", (int)result,
|
||||
curl_easy_strerror(result));
|
||||
return result;
|
||||
}
|
||||
@@ -2015,7 +1999,7 @@ out:
|
||||
}
|
||||
CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %d, %zu, "
|
||||
"window=%d/%d, connection %d/%d",
|
||||
stream->id, len, result, *pnread,
|
||||
stream->id, len, (int)result, *pnread,
|
||||
nghttp2_session_get_stream_effective_recv_data_length(
|
||||
ctx->h2, stream->id),
|
||||
nghttp2_session_get_stream_effective_local_window_size(
|
||||
@@ -2123,7 +2107,7 @@ static CURLcode h2_submit(struct h2_stream_ctx **pstream,
|
||||
goto out;
|
||||
}
|
||||
|
||||
h2_pri_spec(ctx, data, &pri_spec);
|
||||
h2_pri_spec(data, &pri_spec);
|
||||
if(!nghttp2_session_check_request_allowed(ctx->h2))
|
||||
CURL_TRC_CF(data, cf, "send request NOT allowed (via nghttp2)");
|
||||
|
||||
@@ -2200,7 +2184,7 @@ static CURLcode h2_submit(struct h2_stream_ctx **pstream,
|
||||
|
||||
out:
|
||||
CURL_TRC_CF(data, cf, "[%d] submit -> %d, %zu",
|
||||
stream ? stream->id : -1, result, *pnwritten);
|
||||
stream ? stream->id : -1, (int)result, *pnwritten);
|
||||
curlx_safefree(nva);
|
||||
*pstream = stream;
|
||||
Curl_dynhds_free(&h2_headers);
|
||||
@@ -2234,7 +2218,7 @@ static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
DEBUGASSERT(eos);
|
||||
result = cf_h2_body_send(cf, data, stream, buf, 0, eos, &n);
|
||||
CURL_TRC_CF(data, cf, "[%d] cf_body_send last CHUNK -> %d, %zu, eos=%d",
|
||||
stream->id, result, n, eos);
|
||||
stream->id, (int)result, n, eos);
|
||||
if(result)
|
||||
goto out;
|
||||
*pnwritten = len;
|
||||
@@ -2242,7 +2226,7 @@ static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
|
||||
else {
|
||||
result = cf_h2_body_send(cf, data, stream, buf, len, eos, pnwritten);
|
||||
CURL_TRC_CF(data, cf, "[%d] cf_body_send(len=%zu) -> %d, %zu, eos=%d",
|
||||
stream->id, len, result, *pnwritten, eos);
|
||||
stream->id, len, (int)result, *pnwritten, eos);
|
||||
}
|
||||
|
||||
/* Call the nghttp2 send loop and flush to write ALL buffered data,
|
||||
@@ -2278,7 +2262,7 @@ out:
|
||||
CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %d, %zu, "
|
||||
"eos=%d, h2 windows %d-%d (stream-conn), "
|
||||
"buffers %zu-%zu (stream-conn)",
|
||||
stream->id, len, result, *pnwritten,
|
||||
stream->id, len, (int)result, *pnwritten,
|
||||
stream->body_eos,
|
||||
nghttp2_session_get_stream_remote_window_size(
|
||||
ctx->h2, stream->id),
|
||||
@@ -2289,7 +2273,7 @@ out:
|
||||
else {
|
||||
CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %d, %zu, "
|
||||
"connection-window=%d, nw_send_buffer(%zu)",
|
||||
len, result, *pnwritten,
|
||||
len, (int)result, *pnwritten,
|
||||
nghttp2_session_get_remote_window_size(ctx->h2),
|
||||
Curl_bufq_len(&ctx->outbufq));
|
||||
}
|
||||
@@ -2322,7 +2306,7 @@ out:
|
||||
CURL_TRC_CF(data, cf, "[%d] flush -> %d, "
|
||||
"h2 windows %d-%d (stream-conn), "
|
||||
"buffers %zu-%zu (stream-conn)",
|
||||
stream->id, result,
|
||||
stream->id, (int)result,
|
||||
nghttp2_session_get_stream_remote_window_size(
|
||||
ctx->h2, stream->id),
|
||||
nghttp2_session_get_remote_window_size(ctx->h2),
|
||||
@@ -2332,7 +2316,7 @@ out:
|
||||
else {
|
||||
CURL_TRC_CF(data, cf, "flush -> %d, "
|
||||
"connection-window=%d, nw_send_buffer(%zu)",
|
||||
result, nghttp2_session_get_remote_window_size(ctx->h2),
|
||||
(int)result, nghttp2_session_get_remote_window_size(ctx->h2),
|
||||
Curl_bufq_len(&ctx->outbufq));
|
||||
}
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
@@ -2551,27 +2535,11 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
|
||||
result = CURLE_OK;
|
||||
|
||||
out:
|
||||
CURL_TRC_CF(data, cf, "cf_connect() -> %d, %d, ", result, *done);
|
||||
CURL_TRC_CF(data, cf, "cf_connect() -> %d, %d, ", (int)result, *done);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void cf_h2_close(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
|
||||
if(ctx) {
|
||||
struct cf_call_data save;
|
||||
|
||||
CF_DATA_SAVE(save, cf, data);
|
||||
cf_h2_ctx_close(ctx);
|
||||
CF_DATA_RESTORE(cf, save);
|
||||
cf->connected = FALSE;
|
||||
}
|
||||
if(cf->next)
|
||||
cf->next->cft->do_close(cf->next, data);
|
||||
}
|
||||
|
||||
static void cf_h2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
|
||||
{
|
||||
struct cf_h2_ctx *ctx = cf->ctx;
|
||||
@@ -2794,7 +2762,6 @@ struct Curl_cftype Curl_cft_nghttp2 = {
|
||||
CURL_LOG_LVL_NONE,
|
||||
cf_h2_destroy,
|
||||
cf_h2_connect,
|
||||
cf_h2_close,
|
||||
cf_h2_shutdown,
|
||||
cf_h2_adjust_pollset,
|
||||
cf_h2_data_pending,
|
||||
@@ -2869,9 +2836,7 @@ bool Curl_http2_may_switch(struct Curl_easy *data)
|
||||
(data->state.http_neg.wanted & CURL_HTTP_V2x) &&
|
||||
data->state.http_neg.h2_prior_knowledge) {
|
||||
#ifndef CURL_DISABLE_PROXY
|
||||
if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) {
|
||||
/* We do not support HTTP/2 proxies yet. Also it is debatable
|
||||
whether or not this setting should apply to HTTP/2 proxies. */
|
||||
if(data->conn->bits.origin_is_proxy) {
|
||||
infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
|
||||
return FALSE;
|
||||
}
|
||||
@@ -2951,7 +2916,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
|
||||
result = Curl_bufq_write(&ctx->inbufq,
|
||||
(const unsigned char *)mem, nread, &copied);
|
||||
if(result) {
|
||||
failf(data, "error on copying HTTP Upgrade response: %d", result);
|
||||
failf(data, "error on copying HTTP Upgrade response: %d", (int)result);
|
||||
return CURLE_RECV_ERROR;
|
||||
}
|
||||
if(copied < nread) {
|
||||
|
||||
@@ -271,13 +271,12 @@ static bool should_urlencode(struct Curl_str *service_name)
|
||||
* should_urlencode == true is equivalent to should_urlencode_uri_path
|
||||
* from the AWS SDK. Urls are already normalized by the curl URL parser
|
||||
*/
|
||||
|
||||
if(curlx_str_cmp(service_name, "s3") ||
|
||||
curlx_str_cmp(service_name, "s3-express") ||
|
||||
curlx_str_cmp(service_name, "s3-outposts")) {
|
||||
return false;
|
||||
return FALSE;
|
||||
}
|
||||
return true;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* maximum length for the aws sivg4 parts */
|
||||
@@ -384,7 +383,7 @@ static CURLcode make_headers(struct Curl_easy *data,
|
||||
char date_full_hdr[DATE_FULL_HDR_LEN];
|
||||
struct curl_slist *head = NULL;
|
||||
struct curl_slist *tmp_head = NULL;
|
||||
CURLcode ret = CURLE_OUT_OF_MEMORY;
|
||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||
struct curl_slist *l;
|
||||
bool again = TRUE;
|
||||
|
||||
@@ -516,8 +515,8 @@ static CURLcode make_headers(struct Curl_easy *data,
|
||||
}
|
||||
} while(again);
|
||||
|
||||
ret = merge_duplicate_headers(head);
|
||||
if(ret)
|
||||
result = merge_duplicate_headers(head);
|
||||
if(result)
|
||||
goto fail;
|
||||
|
||||
for(l = head; l; l = l->next) {
|
||||
@@ -540,11 +539,11 @@ static CURLcode make_headers(struct Curl_easy *data,
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ret = CURLE_OK;
|
||||
result = CURLE_OK;
|
||||
fail:
|
||||
curl_slist_free_all(head);
|
||||
|
||||
return ret;
|
||||
return result;
|
||||
}
|
||||
|
||||
#define CONTENT_SHA256_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Content-Sha256"))
|
||||
@@ -618,12 +617,12 @@ static CURLcode calc_s3_payload_hash(struct Curl_easy *data,
|
||||
bool empty_payload = (empty_method || data->set.filesize == 0);
|
||||
/* The POST payload is in memory */
|
||||
bool post_payload = (httpreq == HTTPREQ_POST && data->set.postfields);
|
||||
CURLcode ret = CURLE_OUT_OF_MEMORY;
|
||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||
|
||||
if(empty_payload || post_payload) {
|
||||
/* Calculate a real hash when we know the request payload */
|
||||
ret = calc_payload_hash(data, sha_hash, sha_hex);
|
||||
if(ret)
|
||||
result = calc_payload_hash(data, sha_hash, sha_hex);
|
||||
if(result)
|
||||
goto fail;
|
||||
}
|
||||
else {
|
||||
@@ -638,9 +637,9 @@ static CURLcode calc_s3_payload_hash(struct Curl_easy *data,
|
||||
curl_msnprintf(header, CONTENT_SHA256_HDR_LEN,
|
||||
"x-%.*s-content-sha256: %s", (int)plen, provider1, sha_hex);
|
||||
|
||||
ret = CURLE_OK;
|
||||
result = CURLE_OK;
|
||||
fail:
|
||||
return ret;
|
||||
return result;
|
||||
}
|
||||
|
||||
static int compare_func(const void *a, const void *b)
|
||||
@@ -818,66 +817,14 @@ fail:
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
|
||||
static CURLcode parse_sigv4_params(struct Curl_easy *data,
|
||||
const char *hostname,
|
||||
struct Curl_str *provider0,
|
||||
struct Curl_str *provider1,
|
||||
struct Curl_str *region,
|
||||
struct Curl_str *service)
|
||||
{
|
||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||
struct connectdata *conn = data->conn;
|
||||
const char *line;
|
||||
struct Curl_str provider0;
|
||||
struct Curl_str provider1;
|
||||
struct Curl_str region = { NULL, 0 };
|
||||
struct Curl_str service = { NULL, 0 };
|
||||
const char *hostname = conn->host.name;
|
||||
time_t clock;
|
||||
struct tm tm;
|
||||
char timestamp[TIMESTAMP_SIZE];
|
||||
char date[9];
|
||||
struct dynbuf canonical_headers;
|
||||
struct dynbuf signed_headers;
|
||||
struct dynbuf canonical_query;
|
||||
struct dynbuf canonical_path;
|
||||
char *date_header = NULL;
|
||||
Curl_HttpReq httpreq;
|
||||
const char *method = NULL;
|
||||
const char *payload_hash = NULL;
|
||||
size_t payload_hash_len = 0;
|
||||
unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH];
|
||||
char sha_hex[SHA256_HEX_LENGTH];
|
||||
char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */
|
||||
char *canonical_request = NULL;
|
||||
char *request_type = NULL;
|
||||
char *credential_scope = NULL;
|
||||
char *str_to_sign = NULL;
|
||||
const char *user = data->state.aptr.user ? data->state.aptr.user : "";
|
||||
char *secret = NULL;
|
||||
unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = { 0 };
|
||||
unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = { 0 };
|
||||
char *auth_headers = NULL;
|
||||
|
||||
if(data->set.path_as_is) {
|
||||
failf(data, "Cannot use sigv4 authentication with path-as-is flag");
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
}
|
||||
|
||||
if(Curl_checkheaders(data, STRCONST("Authorization"))) {
|
||||
/* Authorization already present, Bailing out */
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
/* we init those buffers here, so goto fail will free initialized dynbuf */
|
||||
curlx_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
|
||||
curlx_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER);
|
||||
curlx_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
|
||||
curlx_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER);
|
||||
|
||||
/*
|
||||
* Parameters parsing
|
||||
* Google and Outscale use the same OSC or GOOG,
|
||||
* but Amazon uses AWS and AMZ for header arguments.
|
||||
* AWS is the default because most of non-amazon providers
|
||||
* are still using aws:amz as a prefix.
|
||||
*/
|
||||
line = data->set.str[STRING_AWS_SIGV4];
|
||||
const char *line = data->set.str[STRING_AWS_SIGV4];
|
||||
if(!line || !*line)
|
||||
line = "aws:amz";
|
||||
|
||||
@@ -885,71 +832,89 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
|
||||
|
||||
No string can be longer than N bytes of non-whitespace
|
||||
*/
|
||||
if(curlx_str_until(&line, &provider0, MAX_SIGV4_LEN, ':')) {
|
||||
if(curlx_str_until(&line, provider0, MAX_SIGV4_LEN, ':')) {
|
||||
failf(data, "first aws-sigv4 provider cannot be empty");
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto fail;
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
}
|
||||
if(curlx_str_single(&line, ':') ||
|
||||
curlx_str_until(&line, &provider1, MAX_SIGV4_LEN, ':')) {
|
||||
provider1 = provider0;
|
||||
curlx_str_until(&line, provider1, MAX_SIGV4_LEN, ':')) {
|
||||
*provider1 = *provider0;
|
||||
}
|
||||
else if(curlx_str_single(&line, ':') ||
|
||||
curlx_str_until(&line, ®ion, MAX_SIGV4_LEN, ':') ||
|
||||
curlx_str_until(&line, region, MAX_SIGV4_LEN, ':') ||
|
||||
curlx_str_single(&line, ':') ||
|
||||
curlx_str_until(&line, &service, MAX_SIGV4_LEN, ':')) {
|
||||
curlx_str_until(&line, service, MAX_SIGV4_LEN, ':')) {
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
if(!curlx_strlen(&service)) {
|
||||
if(!curlx_strlen(service)) {
|
||||
const char *p = hostname;
|
||||
if(curlx_str_until(&p, &service, MAX_SIGV4_LEN, '.') ||
|
||||
if(curlx_str_until(&p, service, MAX_SIGV4_LEN, '.') ||
|
||||
curlx_str_single(&p, '.')) {
|
||||
failf(data, "aws-sigv4: service missing in parameters and hostname");
|
||||
result = CURLE_URL_MALFORMAT;
|
||||
goto fail;
|
||||
return CURLE_URL_MALFORMAT;
|
||||
}
|
||||
|
||||
infof(data, "aws_sigv4: picked service %.*s from host",
|
||||
(int)curlx_strlen(&service), curlx_str(&service));
|
||||
(int)curlx_strlen(service), curlx_str(service));
|
||||
|
||||
if(!curlx_strlen(®ion)) {
|
||||
if(curlx_str_until(&p, ®ion, MAX_SIGV4_LEN, '.') ||
|
||||
if(!curlx_strlen(region)) {
|
||||
if(curlx_str_until(&p, region, MAX_SIGV4_LEN, '.') ||
|
||||
curlx_str_single(&p, '.')) {
|
||||
failf(data, "aws-sigv4: region missing in parameters and hostname");
|
||||
result = CURLE_URL_MALFORMAT;
|
||||
goto fail;
|
||||
return CURLE_URL_MALFORMAT;
|
||||
}
|
||||
infof(data, "aws_sigv4: picked region %.*s from host",
|
||||
(int)curlx_strlen(®ion), curlx_str(®ion));
|
||||
(int)curlx_strlen(region), curlx_str(region));
|
||||
}
|
||||
}
|
||||
|
||||
Curl_http_method(data, &method, &httpreq);
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
payload_hash =
|
||||
parse_content_sha_hdr(data, curlx_str(&provider1),
|
||||
curlx_strlen(&provider1), &payload_hash_len);
|
||||
static CURLcode get_payload_hash(struct Curl_easy *data,
|
||||
Curl_HttpReq httpreq,
|
||||
struct Curl_str *provider0,
|
||||
struct Curl_str *provider1,
|
||||
struct Curl_str *service,
|
||||
unsigned char *sha_hash,
|
||||
char *sha_hex,
|
||||
char *content_sha256_hdr,
|
||||
const char **payload_hash_out,
|
||||
size_t *payload_hash_len_out)
|
||||
{
|
||||
*payload_hash_out =
|
||||
parse_content_sha_hdr(data, curlx_str(provider1),
|
||||
curlx_strlen(provider1), payload_hash_len_out);
|
||||
|
||||
if(!payload_hash) {
|
||||
if(!*payload_hash_out) {
|
||||
CURLcode result;
|
||||
/* AWS S3 requires a x-amz-content-sha256 header, and supports special
|
||||
* values like UNSIGNED-PAYLOAD */
|
||||
bool sign_as_s3 = curlx_str_casecompare(&provider0, "aws") &&
|
||||
curlx_str_casecompare(&service, "s3");
|
||||
bool sign_as_s3 = curlx_str_casecompare(provider0, "aws") &&
|
||||
curlx_str_casecompare(service, "s3");
|
||||
|
||||
if(sign_as_s3)
|
||||
result = calc_s3_payload_hash(data, httpreq, curlx_str(&provider1),
|
||||
curlx_strlen(&provider1), sha_hash,
|
||||
result = calc_s3_payload_hash(data, httpreq, curlx_str(provider1),
|
||||
curlx_strlen(provider1), sha_hash,
|
||||
sha_hex, content_sha256_hdr);
|
||||
else
|
||||
result = calc_payload_hash(data, sha_hash, sha_hex);
|
||||
if(result)
|
||||
goto fail;
|
||||
return result;
|
||||
|
||||
payload_hash = sha_hex;
|
||||
*payload_hash_out = sha_hex;
|
||||
/* may be shorter than SHA256_HEX_LENGTH, like S3_UNSIGNED_PAYLOAD */
|
||||
payload_hash_len = strlen(sha_hex);
|
||||
*payload_hash_len_out = strlen(sha_hex);
|
||||
}
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode get_timestamp(char *timestamp, size_t stampsize)
|
||||
{
|
||||
time_t clock;
|
||||
struct tm tm;
|
||||
CURLcode result;
|
||||
|
||||
#ifdef DEBUGBUILD
|
||||
{
|
||||
@@ -963,43 +928,54 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
|
||||
clock = time(NULL);
|
||||
#endif
|
||||
result = curlx_gmtime(clock, &tm);
|
||||
if(result) {
|
||||
goto fail;
|
||||
}
|
||||
if(!strftime(timestamp, sizeof(timestamp), "%Y%m%dT%H%M%SZ", &tm)) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto fail;
|
||||
}
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
if(!strftime(timestamp, stampsize, "%Y%m%dT%H%M%SZ", &tm))
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode make_canonical_request(struct Curl_easy *data,
|
||||
const char *hostname,
|
||||
char *timestamp,
|
||||
struct Curl_str *provider1,
|
||||
struct Curl_str *service,
|
||||
const char *method,
|
||||
const char *payload_hash,
|
||||
size_t payload_hash_len,
|
||||
char **date_header_out,
|
||||
char *content_sha256_hdr,
|
||||
struct dynbuf *canonical_headers,
|
||||
struct dynbuf *signed_headers,
|
||||
char **canonical_request_out)
|
||||
{
|
||||
struct dynbuf canonical_query;
|
||||
struct dynbuf canonical_path;
|
||||
CURLcode result;
|
||||
|
||||
curlx_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER);
|
||||
curlx_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER);
|
||||
|
||||
result = make_headers(data, hostname, timestamp,
|
||||
curlx_str(&provider1), curlx_strlen(&provider1),
|
||||
&date_header, content_sha256_hdr,
|
||||
&canonical_headers, &signed_headers);
|
||||
curlx_str(provider1), curlx_strlen(provider1),
|
||||
date_header_out, content_sha256_hdr,
|
||||
canonical_headers, signed_headers);
|
||||
if(result)
|
||||
goto fail;
|
||||
|
||||
if(*content_sha256_hdr) {
|
||||
/* make_headers() needed this without the \r\n for canonicalization */
|
||||
size_t hdrlen = strlen(content_sha256_hdr);
|
||||
DEBUGASSERT(hdrlen + 3 < sizeof(content_sha256_hdr));
|
||||
memcpy(content_sha256_hdr + hdrlen, "\r\n", 3);
|
||||
}
|
||||
|
||||
memcpy(date, timestamp, sizeof(date));
|
||||
date[sizeof(date) - 1] = 0;
|
||||
|
||||
result = canon_query(data->state.up.query, &canonical_query);
|
||||
if(result)
|
||||
goto fail;
|
||||
|
||||
result = canon_path(data->state.up.path, strlen(data->state.up.path),
|
||||
&canonical_path,
|
||||
should_urlencode(&service));
|
||||
should_urlencode(service));
|
||||
if(result)
|
||||
goto fail;
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
|
||||
canonical_request =
|
||||
*canonical_request_out =
|
||||
curl_maprintf("%s\n" /* HTTPRequestMethod */
|
||||
"%s\n" /* CanonicalURI */
|
||||
"%s\n" /* CanonicalQueryString */
|
||||
@@ -1010,37 +986,65 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
|
||||
curlx_dyn_ptr(&canonical_path),
|
||||
curlx_dyn_ptr(&canonical_query) ?
|
||||
curlx_dyn_ptr(&canonical_query) : "",
|
||||
curlx_dyn_ptr(&canonical_headers),
|
||||
curlx_dyn_ptr(&signed_headers),
|
||||
curlx_dyn_ptr(canonical_headers),
|
||||
curlx_dyn_ptr(signed_headers),
|
||||
(int)payload_hash_len, payload_hash);
|
||||
if(!canonical_request)
|
||||
if(!*canonical_request_out) {
|
||||
result = CURLE_OUT_OF_MEMORY;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
infof(data, "aws_sigv4: Canonical request (enclosed in []) - [%s]",
|
||||
canonical_request);
|
||||
result = CURLE_OK;
|
||||
fail:
|
||||
curlx_dyn_free(&canonical_query);
|
||||
curlx_dyn_free(&canonical_path);
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode make_string_to_sign(struct Curl_easy *data,
|
||||
struct Curl_str *provider0,
|
||||
struct Curl_str *region,
|
||||
struct Curl_str *service,
|
||||
const char *date,
|
||||
const char *timestamp,
|
||||
const char *canonical_request,
|
||||
char **request_type_out,
|
||||
char **credential_scope_out,
|
||||
char **str_to_sign_out)
|
||||
{
|
||||
char *request_type;
|
||||
char *credential_scope;
|
||||
char *str_to_sign;
|
||||
unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH];
|
||||
char sha_hex[SHA256_HEX_LENGTH];
|
||||
|
||||
request_type = curl_maprintf("%.*s4_request",
|
||||
(int)curlx_strlen(&provider0),
|
||||
curlx_str(&provider0));
|
||||
(int)curlx_strlen(provider0),
|
||||
curlx_str(provider0));
|
||||
if(!request_type)
|
||||
goto fail;
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
/* provider0 is lowercased *after* curl_maprintf() so that the buffer
|
||||
can be written to */
|
||||
Curl_strntolower(request_type, request_type, curlx_strlen(&provider0));
|
||||
Curl_strntolower(request_type, request_type, curlx_strlen(provider0));
|
||||
|
||||
credential_scope = curl_maprintf("%s/%.*s/%.*s/%s", date,
|
||||
(int)curlx_strlen(®ion),
|
||||
curlx_str(®ion),
|
||||
(int)curlx_strlen(&service),
|
||||
curlx_str(&service),
|
||||
(int)curlx_strlen(region),
|
||||
curlx_str(region),
|
||||
(int)curlx_strlen(service),
|
||||
curlx_str(service),
|
||||
request_type);
|
||||
if(!credential_scope)
|
||||
goto fail;
|
||||
if(!credential_scope) {
|
||||
curlx_free(request_type);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if(Curl_sha256it(sha_hash, (unsigned char *)canonical_request,
|
||||
strlen(canonical_request)))
|
||||
goto fail;
|
||||
if(Curl_sha256it(sha_hash, (const unsigned char *)canonical_request,
|
||||
strlen(canonical_request))) {
|
||||
curlx_free(request_type);
|
||||
curlx_free(credential_scope);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
sha256_to_hex(sha_hex, sha_hash);
|
||||
|
||||
@@ -1052,36 +1056,69 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
|
||||
"%s\n" /* RequestDateTime */
|
||||
"%s\n" /* CredentialScope */
|
||||
"%s", /* HashedCanonicalRequest in hex */
|
||||
(int)curlx_strlen(&provider0),
|
||||
curlx_str(&provider0),
|
||||
(int)curlx_strlen(provider0),
|
||||
curlx_str(provider0),
|
||||
timestamp,
|
||||
credential_scope,
|
||||
sha_hex);
|
||||
if(!str_to_sign)
|
||||
goto fail;
|
||||
if(!str_to_sign) {
|
||||
curlx_free(request_type);
|
||||
curlx_free(credential_scope);
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
/* make provider0 part done uppercase */
|
||||
Curl_strntoupper(str_to_sign, curlx_str(&provider0),
|
||||
curlx_strlen(&provider0));
|
||||
Curl_strntoupper(str_to_sign, curlx_str(provider0),
|
||||
curlx_strlen(provider0));
|
||||
|
||||
infof(data, "aws_sigv4: String to sign (enclosed in []) - [%s]",
|
||||
str_to_sign);
|
||||
|
||||
secret = curl_maprintf("%.*s4%s", (int)curlx_strlen(&provider0),
|
||||
curlx_str(&provider0), data->state.aptr.passwd ?
|
||||
data->state.aptr.passwd : "");
|
||||
*request_type_out = request_type;
|
||||
*credential_scope_out = credential_scope;
|
||||
*str_to_sign_out = str_to_sign;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
static CURLcode sign_and_set_auth_headers(struct Curl_easy *data,
|
||||
struct Curl_str *provider0,
|
||||
struct Curl_str *region,
|
||||
struct Curl_str *service,
|
||||
const char *request_type,
|
||||
const char *credential_scope,
|
||||
const char *date,
|
||||
const char *str_to_sign,
|
||||
const char *date_header,
|
||||
const char *content_sha256_hdr,
|
||||
struct dynbuf *signed_headers)
|
||||
{
|
||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||
const char *passwd = Curl_creds_passwd(data->state.creds);
|
||||
char *secret = NULL;
|
||||
unsigned char sign0[CURL_SHA256_DIGEST_LENGTH] = { 0 };
|
||||
unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = { 0 };
|
||||
char sha_hex[SHA256_HEX_LENGTH];
|
||||
char *auth_headers = NULL;
|
||||
char *user = curl_escape(Curl_creds_user(data->state.creds), 0);
|
||||
if(!user)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
|
||||
secret = curl_maprintf("%.*s4%s", (int)curlx_strlen(provider0),
|
||||
curlx_str(provider0), passwd);
|
||||
if(!secret)
|
||||
goto fail;
|
||||
/* make provider0 part done uppercase */
|
||||
Curl_strntoupper(secret, curlx_str(&provider0), curlx_strlen(&provider0));
|
||||
Curl_strntoupper(secret, curlx_str(provider0), curlx_strlen(provider0));
|
||||
|
||||
HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0);
|
||||
HMAC_SHA256(sign0, sizeof(sign0),
|
||||
curlx_str(®ion), curlx_strlen(®ion), sign1);
|
||||
curlx_str(region), curlx_strlen(region), sign1);
|
||||
HMAC_SHA256(sign1, sizeof(sign1),
|
||||
curlx_str(&service), curlx_strlen(&service), sign0);
|
||||
HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1);
|
||||
HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0);
|
||||
curlx_str(service), curlx_strlen(service), sign0);
|
||||
HMAC_SHA256(sign0, sizeof(sign0),
|
||||
request_type, strlen(request_type), sign1);
|
||||
HMAC_SHA256(sign1, sizeof(sign1),
|
||||
str_to_sign, strlen(str_to_sign), sign0);
|
||||
|
||||
sha256_to_hex(sha_hex, sign0);
|
||||
|
||||
@@ -1091,43 +1128,119 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
|
||||
"Credential=%s/%s, "
|
||||
"SignedHeaders=%s, "
|
||||
"Signature=%s\r\n"
|
||||
"%s"
|
||||
"%s%s",
|
||||
(int)curlx_strlen(provider0),
|
||||
curlx_str(provider0),
|
||||
user,
|
||||
credential_scope,
|
||||
curlx_dyn_ptr(signed_headers),
|
||||
sha_hex,
|
||||
/*
|
||||
* date_header is added here, only if it was not
|
||||
* user-specified (using CURLOPT_HTTPHEADER).
|
||||
* date_header includes \r\n
|
||||
*/
|
||||
"%s"
|
||||
"%s", /* optional sha256 header includes \r\n */
|
||||
(int)curlx_strlen(&provider0),
|
||||
curlx_str(&provider0),
|
||||
user,
|
||||
credential_scope,
|
||||
curlx_dyn_ptr(&signed_headers),
|
||||
sha_hex,
|
||||
date_header ? date_header : "",
|
||||
content_sha256_hdr);
|
||||
if(!auth_headers) {
|
||||
content_sha256_hdr,
|
||||
content_sha256_hdr[0] ? "\r\n": "");
|
||||
if(!auth_headers)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* provider 0 uppercase */
|
||||
Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1],
|
||||
curlx_str(&provider0), curlx_strlen(&provider0));
|
||||
curlx_str(provider0), curlx_strlen(provider0));
|
||||
|
||||
curlx_free(data->req.userpwd);
|
||||
data->req.userpwd = auth_headers;
|
||||
curlx_free(data->req.hd_auth);
|
||||
data->req.hd_auth = auth_headers;
|
||||
data->state.authhost.done = TRUE;
|
||||
result = CURLE_OK;
|
||||
|
||||
fail:
|
||||
curlx_dyn_free(&canonical_query);
|
||||
curlx_dyn_free(&canonical_path);
|
||||
curlx_free(user);
|
||||
curlx_free(secret);
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
|
||||
{
|
||||
CURLcode result = CURLE_OUT_OF_MEMORY;
|
||||
struct Curl_str provider0 = { NULL, 0 };
|
||||
struct Curl_str provider1 = { NULL, 0 };
|
||||
struct Curl_str region = { NULL, 0 };
|
||||
struct Curl_str service = { NULL, 0 };
|
||||
const char *hostname = data->state.origin->hostname;
|
||||
char timestamp[TIMESTAMP_SIZE];
|
||||
char date[9];
|
||||
struct dynbuf canonical_headers;
|
||||
struct dynbuf signed_headers;
|
||||
char *date_header = NULL;
|
||||
Curl_HttpReq httpreq;
|
||||
const char *method = NULL;
|
||||
const char *payload_hash = NULL;
|
||||
size_t payload_hash_len = 0;
|
||||
unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH];
|
||||
char sha_hex[SHA256_HEX_LENGTH];
|
||||
char content_sha256_hdr[CONTENT_SHA256_HDR_LEN + 2] = ""; /* add \r\n */
|
||||
char *canonical_request = NULL;
|
||||
char *request_type = NULL;
|
||||
char *credential_scope = NULL;
|
||||
char *str_to_sign = NULL;
|
||||
|
||||
if(data->set.path_as_is) {
|
||||
failf(data, "Cannot use sigv4 authentication with path-as-is flag");
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
}
|
||||
|
||||
if(Curl_checkheaders(data, STRCONST("Authorization")))
|
||||
/* Authorization already present, Bailing out */
|
||||
return CURLE_OK;
|
||||
|
||||
/* we init those buffers here, so goto fail will free initialized dynbuf */
|
||||
curlx_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
|
||||
curlx_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
|
||||
|
||||
result = parse_sigv4_params(data, hostname, &provider0, &provider1,
|
||||
®ion, &service);
|
||||
if(!result) {
|
||||
Curl_http_method(data, &method, &httpreq);
|
||||
result = get_payload_hash(data, httpreq, &provider0, &provider1, &service,
|
||||
sha_hash, sha_hex, content_sha256_hdr,
|
||||
&payload_hash, &payload_hash_len);
|
||||
}
|
||||
|
||||
if(!result)
|
||||
result = get_timestamp(timestamp, sizeof(timestamp));
|
||||
|
||||
if(!result)
|
||||
result = make_canonical_request(data, hostname, timestamp,
|
||||
&provider1, &service,
|
||||
method, payload_hash, payload_hash_len,
|
||||
&date_header, content_sha256_hdr,
|
||||
&canonical_headers, &signed_headers,
|
||||
&canonical_request);
|
||||
if(!result) {
|
||||
/* the timestamp might have been updated in make_canonical_request */
|
||||
memcpy(date, timestamp, sizeof(date) - 1);
|
||||
date[sizeof(date) - 1] = 0;
|
||||
|
||||
result = make_string_to_sign(data, &provider0, ®ion, &service,
|
||||
date, timestamp, canonical_request,
|
||||
&request_type, &credential_scope,
|
||||
&str_to_sign);
|
||||
}
|
||||
if(!result)
|
||||
result = sign_and_set_auth_headers(data, &provider0, ®ion, &service,
|
||||
request_type, credential_scope,
|
||||
date, str_to_sign, date_header,
|
||||
content_sha256_hdr, &signed_headers);
|
||||
|
||||
curlx_dyn_free(&canonical_headers);
|
||||
curlx_dyn_free(&signed_headers);
|
||||
curlx_free(canonical_request);
|
||||
curlx_free(request_type);
|
||||
curlx_free(credential_scope);
|
||||
curlx_free(str_to_sign);
|
||||
curlx_free(secret);
|
||||
curlx_free(date_header);
|
||||
return result;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user