diff --git a/CMake/CurlTests.c b/CMake/CurlTests.c index de2313080d..410a0f70ce 100644 --- a/CMake/CurlTests.c +++ b/CMake/CurlTests.c @@ -203,7 +203,7 @@ int main(void) #ifdef HAVE_UNISTD_H # include #endif -#ifdef HAVE_SYS_SOCKET_H +#ifndef _WIN32 # include #endif #ifdef HAVE_SYS_IOCTL_H @@ -230,7 +230,7 @@ int main(void) #ifdef HAVE_UNISTD_H # include #endif -#ifdef HAVE_SYS_SOCKET_H +#ifndef _WIN32 # include #endif #ifdef HAVE_SYS_IOCTL_H @@ -257,7 +257,7 @@ int main(void) #ifdef HAVE_SYS_TYPES_H # include #endif -#ifdef HAVE_SYS_SOCKET_H +#ifndef _WIN32 # include #endif int main(void) diff --git a/CMake/FindBearSSL.cmake b/CMake/FindBearSSL.cmake deleted file mode 100644 index ff55be0f22..0000000000 --- a/CMake/FindBearSSL.cmake +++ /dev/null @@ -1,58 +0,0 @@ -#*************************************************************************** -# _ _ ____ _ -# Project ___| | | | _ \| | -# / __| | | | |_) | | -# | (__| |_| | _ <| |___ -# \___|\___/|_| \_\_____| -# -# Copyright (C) Daniel Stenberg, , 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 -# -########################################################################### -# Find the BearSSL library -# -# Input variables: -# -# - `BEARSSL_INCLUDE_DIR`: The BearSSL include directory. -# - `BEARSSL_LIBRARY`: Path to `bearssl` library. -# -# Result variables: -# -# - `BEARSSL_FOUND`: System has BearSSL. -# - `BEARSSL_INCLUDE_DIRS`: The BearSSL include directories. -# - `BEARSSL_LIBRARIES`: The BearSSL library names. - -if(DEFINED BEARSSL_INCLUDE_DIRS AND NOT DEFINED BEARSSL_INCLUDE_DIR) - message(WARNING "BEARSSL_INCLUDE_DIRS is deprecated, use BEARSSL_INCLUDE_DIR instead.") - set(BEARSSL_INCLUDE_DIR "${BEARSSL_INCLUDE_DIRS}") - unset(BEARSSL_INCLUDE_DIRS) -endif() - -find_path(BEARSSL_INCLUDE_DIR NAMES "bearssl.h") -find_library(BEARSSL_LIBRARY NAMES "bearssl") - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(BearSSL - REQUIRED_VARS - BEARSSL_INCLUDE_DIR - BEARSSL_LIBRARY -) - -if(BEARSSL_FOUND) - set(BEARSSL_INCLUDE_DIRS ${BEARSSL_INCLUDE_DIR}) - set(BEARSSL_LIBRARIES ${BEARSSL_LIBRARY}) -endif() - -mark_as_advanced(BEARSSL_INCLUDE_DIR BEARSSL_LIBRARY) diff --git a/CMake/FindGSS.cmake b/CMake/FindGSS.cmake index c1802ee740..9000445acb 100644 --- a/CMake/FindGSS.cmake +++ b/CMake/FindGSS.cmake @@ -52,19 +52,23 @@ set(_gss_root_hints "$ENV{GSS_ROOT_DIR}" ) +set(_gss_CFLAGS "") +set(_gss_LIBRARY_DIRS "") + # Try to find library using system pkg-config if user did not specify root dir 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} ${_heimdal_modname}) - list(APPEND _gss_root_hints "${_GSS_PREFIX}") + pkg_search_module(_gss ${_gnu_modname} ${_mit_modname} ${_heimdal_modname}) + list(APPEND _gss_root_hints "${_gss_PREFIX}") + set(_gss_version "${_gss_VERSION}") endif() if(WIN32) list(APPEND _gss_root_hints "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MIT\\Kerberos;InstallDir]") endif() endif() -if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional approach. +if(NOT _gss_FOUND) # Not found by pkg-config. Let us take more traditional approach. find_file(_gss_configure_script NAMES "krb5-config" @@ -85,25 +89,29 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr ) if(_gss_configure_script) + + set(_gss_INCLUDE_DIRS "") + set(_gss_LIBRARIES "") + execute_process( COMMAND ${_gss_configure_script} "--cflags" "gssapi" - OUTPUT_VARIABLE _GSS_CFLAGS + OUTPUT_VARIABLE _gss_cflags_raw RESULT_VARIABLE _gss_configure_failed OUTPUT_STRIP_TRAILING_WHITESPACE ) - message(STATUS "FindGSS krb5-config --cflags: ${_GSS_CFLAGS}") + message(STATUS "FindGSS krb5-config --cflags: ${_gss_cflags_raw}") if(NOT _gss_configure_failed) # 0 means success - # Should also work in an odd case when multiple directories are given - string(STRIP "${_GSS_CFLAGS}" _GSS_CFLAGS) - string(REGEX REPLACE " +-I" ";" _GSS_CFLAGS "${_GSS_CFLAGS}") - string(REGEX REPLACE " +-([^I][^ \\t;]*)" ";-\\1" _GSS_CFLAGS "${_GSS_CFLAGS}") + # Should also work in an odd case when multiple directories are given. + string(STRIP "${_gss_cflags_raw}" _gss_cflags_raw) + string(REGEX REPLACE " +-(I)" ";-\\1" _gss_cflags_raw "${_gss_cflags_raw}") + string(REGEX REPLACE " +-([^I][^ \\t;]*)" ";-\\1" _gss_cflags_raw "${_gss_cflags_raw}") - foreach(_flag IN LISTS _GSS_CFLAGS) + foreach(_flag IN LISTS _gss_cflags_raw) if(_flag MATCHES "^-I") - string(REGEX REPLACE "^-I" "" _val "${_flag}") - list(APPEND _GSS_INCLUDE_DIRS "${_val}") + string(REGEX REPLACE "^-I" "" _flag "${_flag}") + list(APPEND _gss_INCLUDE_DIRS "${_flag}") else() - list(APPEND _GSS_CFLAGS "${_flag}") + list(APPEND _gss_CFLAGS "${_flag}") endif() endforeach() endif() @@ -117,32 +125,32 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr message(STATUS "FindGSS krb5-config --libs: ${_gss_lib_flags}") if(NOT _gss_configure_failed) # 0 means success - # This script gives us libraries and link directories. Blah. We have to deal with it. + # This script gives us libraries and link directories. string(STRIP "${_gss_lib_flags}" _gss_lib_flags) string(REGEX REPLACE " +-(L|l)" ";-\\1" _gss_lib_flags "${_gss_lib_flags}") string(REGEX REPLACE " +-([^Ll][^ \\t;]*)" ";-\\1" _gss_lib_flags "${_gss_lib_flags}") foreach(_flag IN LISTS _gss_lib_flags) if(_flag MATCHES "^-l") - string(REGEX REPLACE "^-l" "" _val "${_flag}") - list(APPEND _GSS_LIBRARIES "${_val}") + string(REGEX REPLACE "^-l" "" _flag "${_flag}") + list(APPEND _gss_LIBRARIES "${_flag}") elseif(_flag MATCHES "^-L") - string(REGEX REPLACE "^-L" "" _val "${_flag}") - list(APPEND _GSS_LIBRARY_DIRS "${_val}") + string(REGEX REPLACE "^-L" "" _flag "${_flag}") + list(APPEND _gss_LIBRARY_DIRS "${_flag}") endif() endforeach() endif() execute_process( COMMAND ${_gss_configure_script} "--version" - OUTPUT_VARIABLE _GSS_VERSION + OUTPUT_VARIABLE _gss_version 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. if(_gss_configure_failed) - set(_GSS_VERSION 0) + set(_gss_version 0) endif() execute_process( @@ -165,7 +173,7 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr else() # Either there is no config script or we are on a platform that does not provide one (Windows?) - find_path(_GSS_INCLUDE_DIRS NAMES "gssapi/gssapi.h" + find_path(_gss_INCLUDE_DIRS NAMES "gssapi/gssapi.h" HINTS ${_gss_root_hints} PATH_SUFFIXES @@ -173,9 +181,9 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr "inc" ) - if(_GSS_INCLUDE_DIRS) # jay, we have found something + if(_gss_INCLUDE_DIRS) # jay, we have found something cmake_push_check_state() - list(APPEND CMAKE_REQUIRED_INCLUDES "${_GSS_INCLUDE_DIRS}") + list(APPEND CMAKE_REQUIRED_INCLUDES "${_gss_INCLUDE_DIRS}") check_include_files("gssapi/gssapi_generic.h;gssapi/gssapi_krb5.h" _gss_have_mit_headers) if(_gss_have_mit_headers) @@ -193,7 +201,7 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr cmake_pop_check_state() else() # I am not convinced if this is the right way but this is what autotools do at the moment - find_path(_GSS_INCLUDE_DIRS NAMES "gssapi.h" + find_path(_gss_INCLUDE_DIRS NAMES "gssapi.h" HINTS ${_gss_root_hints} PATH_SUFFIXES @@ -201,17 +209,17 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr "inc" ) - if(_GSS_INCLUDE_DIRS) + if(_gss_INCLUDE_DIRS) set(GSS_FLAVOUR "Heimdal") else() - find_path(_GSS_INCLUDE_DIRS NAMES "gss.h" + find_path(_gss_INCLUDE_DIRS NAMES "gss.h" HINTS ${_gss_root_hints} PATH_SUFFIXES "include" ) - if(_GSS_INCLUDE_DIRS) + if(_gss_INCLUDE_DIRS) set(GSS_FLAVOUR "GNU") set(GSS_PC_REQUIRES "gss") endif() @@ -222,7 +230,7 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr if(GSS_FLAVOUR) set(_gss_libdir_suffixes "") set(_gss_libdir_hints ${_gss_root_hints}) - get_filename_component(_gss_calculated_potential_root "${_GSS_INCLUDE_DIRS}" DIRECTORY) + get_filename_component(_gss_calculated_potential_root "${_gss_INCLUDE_DIRS}" DIRECTORY) list(APPEND _gss_libdir_hints ${_gss_calculated_potential_root}) if(WIN32) @@ -256,7 +264,7 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr endif() endif() - find_library(_GSS_LIBRARIES NAMES ${_gss_libname} + find_library(_gss_LIBRARIES NAMES ${_gss_libname} HINTS ${_gss_libdir_hints} PATH_SUFFIXES @@ -265,36 +273,36 @@ if(NOT _GSS_FOUND) # Not found by pkg-config. Let us take more traditional appr endif() endif() else() - # _GSS_MODULE_NAME set since CMake 3.16 - if(_GSS_MODULE_NAME STREQUAL _gnu_modname OR _GSS_${_gnu_modname}_VERSION) + # _gss_MODULE_NAME set since CMake 3.16 + if(_gss_MODULE_NAME STREQUAL _gnu_modname OR _gss_${_gnu_modname}_VERSION) set(GSS_FLAVOUR "GNU") set(GSS_PC_REQUIRES "gss") - if(NOT _GSS_VERSION) # for old CMake versions? - set(_GSS_VERSION ${_GSS_${_gnu_modname}_VERSION}) + if(NOT _gss_version) # for old CMake versions? + set(_gss_version ${_gss_${_gnu_modname}_VERSION}) endif() - elseif(_GSS_MODULE_NAME STREQUAL _mit_modname OR _GSS_${_mit_modname}_VERSION) + elseif(_gss_MODULE_NAME STREQUAL _mit_modname OR _gss_${_mit_modname}_VERSION) set(GSS_FLAVOUR "MIT") set(GSS_PC_REQUIRES "mit-krb5-gssapi") - if(NOT _GSS_VERSION) # for old CMake versions? - set(_GSS_VERSION ${_GSS_${_mit_modname}_VERSION}) + if(NOT _gss_version) # for old CMake versions? + set(_gss_version ${_gss_${_mit_modname}_VERSION}) endif() else() set(GSS_FLAVOUR "Heimdal") set(GSS_PC_REQUIRES "heimdal-gssapi") - if(NOT _GSS_VERSION) # for old CMake versions? - set(_GSS_VERSION ${_GSS_${_heimdal_modname}_VERSION}) + if(NOT _gss_version) # for old CMake versions? + set(_gss_version ${_gss_${_heimdal_modname}_VERSION}) endif() endif() - message(STATUS "Found GSS/${GSS_FLAVOUR} (via pkg-config): ${_GSS_INCLUDE_DIRS} (found version \"${_GSS_VERSION}\")") + message(STATUS "Found GSS/${GSS_FLAVOUR} (via pkg-config): ${_gss_INCLUDE_DIRS} (found version \"${_gss_version}\")") endif() -string(REPLACE ";" " " _GSS_CFLAGS "${_GSS_CFLAGS}") +string(REPLACE ";" " " _gss_CFLAGS "${_gss_CFLAGS}") -set(GSS_INCLUDE_DIRS ${_GSS_INCLUDE_DIRS}) -set(GSS_LIBRARIES ${_GSS_LIBRARIES}) -set(GSS_LIBRARY_DIRS ${_GSS_LIBRARY_DIRS}) -set(GSS_CFLAGS ${_GSS_CFLAGS}) -set(GSS_VERSION ${_GSS_VERSION}) +set(GSS_INCLUDE_DIRS ${_gss_INCLUDE_DIRS}) +set(GSS_LIBRARIES ${_gss_LIBRARIES}) +set(GSS_LIBRARY_DIRS ${_gss_LIBRARY_DIRS}) +set(GSS_CFLAGS ${_gss_CFLAGS}) +set(GSS_VERSION ${_gss_version}) if(GSS_FLAVOUR) if(NOT GSS_VERSION AND GSS_FLAVOUR STREQUAL "Heimdal") @@ -346,12 +354,12 @@ find_package_handle_standard_args(GSS ) mark_as_advanced( - _GSS_CFLAGS - _GSS_FOUND - _GSS_INCLUDE_DIRS - _GSS_LIBRARIES - _GSS_LIBRARY_DIRS - _GSS_MODULE_NAME - _GSS_PREFIX - _GSS_VERSION + _gss_CFLAGS + _gss_FOUND + _gss_INCLUDE_DIRS + _gss_LIBRARIES + _gss_LIBRARY_DIRS + _gss_MODULE_NAME + _gss_PREFIX + _gss_version ) diff --git a/CMake/FindLibgsasl.cmake b/CMake/FindLibgsasl.cmake index c726ce1a49..878d651883 100644 --- a/CMake/FindLibgsasl.cmake +++ b/CMake/FindLibgsasl.cmake @@ -66,12 +66,12 @@ else() endif() include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(Libgsasl - REQUIRED_VARS - LIBGSASL_INCLUDE_DIR - LIBGSASL_LIBRARY - VERSION_VAR - LIBGSASL_VERSION + find_package_handle_standard_args(Libgsasl + REQUIRED_VARS + LIBGSASL_INCLUDE_DIR + LIBGSASL_LIBRARY + VERSION_VAR + LIBGSASL_VERSION ) if(LIBGSASL_FOUND) diff --git a/CMake/FindNGTCP2.cmake b/CMake/FindNGTCP2.cmake index 3cbd408c22..02e7652ab3 100644 --- a/CMake/FindNGTCP2.cmake +++ b/CMake/FindNGTCP2.cmake @@ -34,23 +34,28 @@ # # Input variables: # -# - `NGTCP2_INCLUDE_DIR`: The ngtcp2 include directory. -# - `NGTCP2_LIBRARY`: Path to `ngtcp2` library. +# - `NGTCP2_INCLUDE_DIR`: The ngtcp2 include directory. +# - `NGTCP2_LIBRARY`: Path to `ngtcp2` library. +# - `NGTCP2_CRYPTO_BORINGSSL_LIBRARY`: Path to `ngtcp2_crypto_boringssl` library. +# - `NGTCP2_CRYPTO_GNUTLS_LIBRARY`: Path to `ngtcp2_crypto_gnutls` library. +# - `NGTCP2_CRYPTO_OSSL_LIBRARY`: Path to `ngtcp2_crypto_ossl` library. +# - `NGTCP2_CRYPTO_QUICTLS_LIBRARY`: Path to `ngtcp2_crypto_quictls` library. +# - `NGTCP2_CRYPTO_WOLFSSL_LIBRARY`: Path to `ngtcp2_crypto_wolfssl` library. # # Result variables: # -# - `NGTCP2_FOUND`: System has ngtcp2. -# - `NGTCP2_INCLUDE_DIRS`: The ngtcp2 include directories. -# - `NGTCP2_LIBRARIES`: The ngtcp2 library names. -# - `NGTCP2_LIBRARY_DIRS`: The ngtcp2 library directories. -# - `NGTCP2_PC_REQUIRES`: The ngtcp2 pkg-config packages. -# - `NGTCP2_CFLAGS`: Required compiler flags. -# - `NGTCP2_VERSION`: Version of ngtcp2. +# - `NGTCP2_FOUND`: System has ngtcp2. +# - `NGTCP2_INCLUDE_DIRS`: The ngtcp2 include directories. +# - `NGTCP2_LIBRARIES`: The ngtcp2 library names. +# - `NGTCP2_LIBRARY_DIRS`: The ngtcp2 library directories. +# - `NGTCP2_PC_REQUIRES`: The ngtcp2 pkg-config packages. +# - `NGTCP2_CFLAGS`: Required compiler flags. +# - `NGTCP2_VERSION`: Version of ngtcp2. if(NGTCP2_FIND_COMPONENTS) set(_ngtcp2_crypto_backend "") foreach(_component IN LISTS NGTCP2_FIND_COMPONENTS) - if(_component MATCHES "^(BoringSSL|quictls|wolfSSL|GnuTLS|ossl)") + if(_component MATCHES "^(BoringSSL|GnuTLS|ossl|quictls|wolfSSL)") if(_ngtcp2_crypto_backend) message(FATAL_ERROR "NGTCP2: Only one crypto library can be selected") endif() diff --git a/CMake/Macros.cmake b/CMake/Macros.cmake index e3a654b5f3..710df70116 100644 --- a/CMake/Macros.cmake +++ b/CMake/Macros.cmake @@ -65,6 +65,7 @@ macro(curl_internal_test _curl_test) endif() endmacro() +# Option for dependencies that accepts an 'AUTO' value, which enables the dependency if detected. macro(curl_dependency_option _option_name _find_name _desc_name) set(${_option_name} "AUTO" CACHE STRING "Build curl with ${_desc_name} support (AUTO, ON or OFF)") set_property(CACHE ${_option_name} PROPERTY STRINGS "AUTO" "ON" "OFF") @@ -94,3 +95,80 @@ macro(curl_prefill_type_size _type _size) set(SIZEOF_${_type} ${_size}) set(SIZEOF_${_type}_CODE "#define SIZEOF_${_type} ${_size}") endmacro() + +# Internal: Recurse into target libraries and collect their include directories +# and macro definitions. +macro(curl_collect_target_options _target) + get_target_property(_val ${_target} INTERFACE_INCLUDE_DIRECTORIES) + if(_val) + list(APPEND _includes ${_val}) + endif() + get_target_property(_val ${_target} INCLUDE_DIRECTORIES) + if(_val) + list(APPEND _includes ${_val}) + endif() + get_target_property(_val ${_target} COMPILE_DEFINITIONS) + if(_val) + list(APPEND _definitions ${_val}) + endif() + get_target_property(_val ${_target} LINK_LIBRARIES) + if(_val) + foreach(_lib IN LISTS _val) + if(TARGET "${_lib}") + curl_collect_target_options(${_lib}) + endif() + endforeach() + endif() + unset(_val) +endmacro() + +# Create a clang-tidy target for test targets +macro(curl_add_clang_tidy_test_target _target_clang_tidy _target) + if(CURL_CLANG_TIDY) + + set(_includes "") + set(_definitions "") + + # Collect header directories and macro definitions applying to the directory + get_directory_property(_val INCLUDE_DIRECTORIES) + if(_val) + list(APPEND _includes ${_val}) + endif() + get_directory_property(_val COMPILE_DEFINITIONS) + if(_val) + list(APPEND _definitions ${_val}) + endif() + unset(_val) + + # Collect header directories and macro definitions from lib dependencies + curl_collect_target_options(${_target}) + + list(REMOVE_ITEM _includes "") + string(REPLACE ";" ";-I" _includes ";${_includes}") + list(REMOVE_DUPLICATES _includes) + + list(REMOVE_ITEM _definitions "") + string(REPLACE ";" ";-D" _definitions ";${_definitions}") + list(REMOVE_DUPLICATES _definitions) + list(SORT _definitions) # Sort like CMake does + + # Assemble source list + set(_sources "") + foreach(_source IN ITEMS ${ARGN}) + if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${_source}") # if not in source tree + set(_source "${CMAKE_CURRENT_BINARY_DIR}/${_source}") # look in the build tree, for generated files, e.g. lib1521.c + endif() + list(APPEND _sources "${_source}") + endforeach() + + add_custom_target(${_target_clang_tidy} USES_TERMINAL + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMAND ${CMAKE_C_CLANG_TIDY} ${_sources} -- ${_includes} ${_definitions} + DEPENDS ${_sources}) + add_dependencies(tests-clang-tidy ${_target_clang_tidy}) + + unset(_includes) + unset(_definitions) + unset(_sources) + endif() +endmacro() diff --git a/CMake/OtherTests.cmake b/CMake/OtherTests.cmake index 26e9d821c2..9f59e0dc81 100644 --- a/CMake/OtherTests.cmake +++ b/CMake/OtherTests.cmake @@ -25,6 +25,7 @@ include(CheckCSourceCompiles) include(CheckCSourceRuns) include(CheckTypeSize) +# #include header if condition is true macro(curl_add_header_include _check _header) if(${_check}) set(_source_epilogue "${_source_epilogue} @@ -41,7 +42,7 @@ if(NOT DEFINED HAVE_STRUCT_SOCKADDR_STORAGE) if(WIN32) set(CMAKE_EXTRA_INCLUDE_FILES "winsock2.h") list(APPEND CMAKE_REQUIRED_LIBRARIES "ws2_32") - elseif(HAVE_SYS_SOCKET_H) + else() set(CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h") endif() check_type_size("struct sockaddr_storage" SIZEOF_STRUCT_SOCKADDR_STORAGE) @@ -52,8 +53,8 @@ endif() if(NOT WIN32) set(_source_epilogue "#undef inline") curl_add_header_include(HAVE_SYS_TYPES_H "sys/types.h") - curl_add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h") check_c_source_compiles("${_source_epilogue} + #include int main(void) { int flag = MSG_NOSIGNAL; @@ -63,11 +64,13 @@ if(NOT WIN32) endif() set(_source_epilogue "#undef inline") -curl_add_header_include(HAVE_SYS_TIME_H "sys/time.h") check_c_source_compiles("${_source_epilogue} #ifdef _MSC_VER #include #endif + #ifndef _WIN32 + #include + #endif #include int main(void) { @@ -100,9 +103,11 @@ elseif(BSD OR CMAKE_SYSTEM_NAME MATCHES "BSD") endif() if(NOT DEFINED HAVE_GETADDRINFO_THREADSAFE) - set(_source_epilogue "#undef inline") - curl_add_header_include(HAVE_SYS_SOCKET_H "sys/socket.h") - curl_add_header_include(HAVE_SYS_TIME_H "sys/time.h") + set(_source_epilogue "#undef inline + #ifndef _WIN32 + #include + #include + #endif") curl_add_header_include(HAVE_NETDB_H "netdb.h") check_c_source_compiles("${_source_epilogue} int main(void) @@ -143,8 +148,8 @@ endif() if(NOT WIN32 AND NOT DEFINED HAVE_CLOCK_GETTIME_MONOTONIC_RAW) set(_source_epilogue "#undef inline") curl_add_header_include(HAVE_SYS_TYPES_H "sys/types.h") - curl_add_header_include(HAVE_SYS_TIME_H "sys/time.h") check_c_source_compiles("${_source_epilogue} + #include #include int main(void) { diff --git a/CMake/PickyWarnings.cmake b/CMake/PickyWarnings.cmake index be26d05cb8..7838f6f8ba 100644 --- a/CMake/PickyWarnings.cmake +++ b/CMake/PickyWarnings.cmake @@ -316,8 +316,9 @@ if(PICKY_COMPILER) list(APPEND _picky "-wd4668") # 'M' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' (in winbase.h) list(APPEND _picky "-wd4710") # 'snprintf': function not inlined list(APPEND _picky "-wd4711") # function 'A' selected for automatic inline expansion - list(APPEND _picky "-wd4746") # volatile access of '' is subject to /volatile: setting; - # consider using __iso_volatile_load/store intrinsic functions (ARM64) + # volatile access of '' is subject to /volatile: setting; + # consider using __iso_volatile_load/store intrinsic functions (ARM64) + list(APPEND _picky "-wd4746") list(APPEND _picky "-wd4774") # 'snprintf': format string expected in argument 3 is not a string literal list(APPEND _picky "-wd4820") # 'A': 'N' bytes padding added after data member 'B' if(MSVC_VERSION GREATER_EQUAL 1900) @@ -340,7 +341,7 @@ if(CMAKE_C_COMPILER_ID STREQUAL "Clang" AND MSVC) list(APPEND _picky_tmp "-clang:${_ccopt}") endif() endforeach() - set("${_wlist}" ${_picky_tmp}) + set("${_wlist}" ${_picky_tmp}) # cmake-lint: disable=C0103 endforeach() endif() diff --git a/CMake/Utilities.cmake b/CMake/Utilities.cmake index ff2173fc08..335713c84c 100644 --- a/CMake/Utilities.cmake +++ b/CMake/Utilities.cmake @@ -51,3 +51,31 @@ function(curl_dumpvars) endforeach() message("::endgroup::") endfunction() + +# Dump all target properties +function(curl_dumptargetprops _target) + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19 AND TARGET "${_target}") + execute_process(COMMAND "${CMAKE_COMMAND}" "--help-property-list" OUTPUT_VARIABLE _cmake_property_list) + string(REPLACE "\n" ";" _cmake_property_list "${_cmake_property_list}") + list(REMOVE_DUPLICATES _cmake_property_list) + list(REMOVE_ITEM _cmake_property_list "") + foreach(_prop IN LISTS _cmake_property_list) + if(_prop MATCHES "") + foreach(_config IN ITEMS "DEBUG" "RELEASE" "MINSIZEREL" "RELWITHDEBINFO") + string(REPLACE "" "${_config}" _propconfig "${_prop}") + get_property(_is_set TARGET "${_target}" PROPERTY "${_propconfig}" SET) + if(_is_set) + get_target_property(_val "${_target}" "${_propconfig}") + message("${_target}.${_propconfig} = '${_val}'") + endif() + endforeach() + else() + get_property(_is_set TARGET "${_target}" PROPERTY "${_prop}" SET) + if(_is_set) + get_target_property(_val "${_target}" "${_prop}") + message("${_target}.${_prop} = '${_val}'") + endif() + endif() + endforeach() + endif() +endfunction() diff --git a/CMake/unix-cache.cmake b/CMake/unix-cache.cmake index 58da04e222..2c85626a49 100644 --- a/CMake/unix-cache.cmake +++ b/CMake/unix-cache.cmake @@ -280,15 +280,12 @@ set(HAVE_SYS_PARAM_H 1) set(HAVE_SYS_POLL_H 1) set(HAVE_SYS_RESOURCE_H 1) set(HAVE_SYS_SELECT_H 1) -set(HAVE_SYS_SOCKET_H 1) if(CYGWIN OR CMAKE_SYSTEM_NAME STREQUAL "Linux") set(HAVE_SYS_SOCKIO_H 0) else() set(HAVE_SYS_SOCKIO_H 1) endif() -set(HAVE_SYS_STAT_H 1) -set(HAVE_SYS_TIME_H 1) set(HAVE_SYS_TYPES_H 1) set(HAVE_SYS_UN_H 1) if(CYGWIN) diff --git a/CMake/win32-cache.cmake b/CMake/win32-cache.cmake index 163a310fda..4fe9eb1139 100644 --- a/CMake/win32-cache.cmake +++ b/CMake/win32-cache.cmake @@ -39,7 +39,6 @@ if(MINGW) set(HAVE_STDINT_H 1) # detected by CMake internally in check_type_size() set(HAVE_STRINGS_H 1) # wrapper to string.h set(HAVE_SYS_PARAM_H 1) - set(HAVE_SYS_TIME_H 1) set(HAVE_UNISTD_H 1) set(HAVE_UTIME_H 1) # wrapper to sys/utime.h else() @@ -50,7 +49,6 @@ else() set(HAVE_OPENDIR 0) set(HAVE_STRINGS_H 0) set(HAVE_SYS_PARAM_H 0) - set(HAVE_SYS_TIME_H 0) set(HAVE_UTIME_H 0) if(MSVC) set(HAVE_UNISTD_H 0) @@ -118,7 +116,6 @@ set(HAVE_GETSOCKNAME 1) set(HAVE_GLIBC_STRERROR_R 0) set(HAVE_GMTIME_R 0) set(HAVE_IFADDRS_H 0) -set(HAVE_IF_NAMETOINDEX 0) set(HAVE_INET_NTOP 0) set(HAVE_INET_PTON 0) set(HAVE_IOCTLSOCKET 1) @@ -171,9 +168,7 @@ set(HAVE_SYS_IOCTL_H 0) set(HAVE_SYS_POLL_H 0) set(HAVE_SYS_RESOURCE_H 0) set(HAVE_SYS_SELECT_H 0) -set(HAVE_SYS_SOCKET_H 0) set(HAVE_SYS_SOCKIO_H 0) -set(HAVE_SYS_STAT_H 1) set(HAVE_SYS_TYPES_H 1) set(HAVE_SYS_UN_H 0) set(HAVE_SYS_UTIME_H 1) diff --git a/CMakeLists.txt b/CMakeLists.txt index 825577e2e8..c0cc0c57b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -277,7 +277,6 @@ if(ENABLE_DEBUG) message(WARNING "This curl build is Debug-enabled and insecure, do not use in production.") endif() option(ENABLE_CURLDEBUG "Enable TrackMemory debug feature" ${ENABLE_DEBUG}) -option(ENABLE_SERVER_DEBUG "Apply curl debug options to test servers" OFF) set(CURL_DEBUG_MACROS "") if(ENABLE_DEBUG) @@ -287,15 +286,11 @@ if(ENABLE_CURLDEBUG) list(APPEND CURL_DEBUG_MACROS "CURLDEBUG") endif() -option(CURL_TEST_BUNDLES "Build tests into single-binary bundles" OFF) - option(CURL_CLANG_TIDY "Run the build through clang-tidy" OFF) if(CURL_CLANG_TIDY) - # clang-tidy is not looking into #included sources, thus not compatible with - # unity builds and test bundles. - set(CMAKE_UNITY_BUILD OFF) - set(CURL_TEST_BUNDLES OFF) + set(CMAKE_UNITY_BUILD OFF) # clang-tidy is not looking into #included sources, thus not compatible with unity builds. set(_tidy_checks "") + list(APPEND _tidy_checks "-clang-analyzer-security.insecureAPI.bzero") # for FD_ZERO() (seen on macOS) list(APPEND _tidy_checks "-clang-analyzer-security.insecureAPI.strcpy") list(APPEND _tidy_checks "-clang-analyzer-optin.performance.Padding") list(APPEND _tidy_checks "-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling") @@ -376,6 +371,7 @@ if(ENABLE_ARES) list(APPEND CURL_LIBS ${CARES_LIBRARIES}) list(APPEND CURL_LIBDIRS ${CARES_LIBRARY_DIRS}) list(APPEND LIBCURL_PC_REQUIRES_PRIVATE ${CARES_PC_REQUIRES}) + include_directories(SYSTEM ${CARES_INCLUDE_DIRS}) link_directories(${CARES_LIBRARY_DIRS}) if(CARES_CFLAGS) string(APPEND CMAKE_C_FLAGS " ${CARES_CFLAGS}") @@ -520,7 +516,7 @@ if(PERL_EXECUTABLE) DEPENDS "${PROJECT_SOURCE_DIR}/scripts/mk-ca-bundle.pl" ) add_custom_target(curl-ca-firefox - COMMENT "generating a fresh ca-bundle.crt" VERBATIM USES_TERMINAL + COMMENT "Generating a fresh ca-bundle.crt" VERBATIM USES_TERMINAL COMMAND "${PERL_EXECUTABLE}" "${PROJECT_SOURCE_DIR}/scripts/firefox-db2pem.sh" "lib/ca-bundle.crt" DEPENDS "${PROJECT_SOURCE_DIR}/scripts/firefox-db2pem.sh" ) @@ -605,10 +601,12 @@ if(WIN32) set(_win32_winsock "ws2_32") endif() set(_win32_crypt32 "crypt32") + set(_win32_secur32 "secur32") if(MINGW32CE) # FIXME upstream: must specify the full path to avoid CMake converting "ws2" to "ws2.lib" set(_win32_winsock "${MINGW32CE_LIBRARY_DIR}/lib${_win32_winsock}.a") set(_win32_crypt32 "${MINGW32CE_LIBRARY_DIR}/lib${_win32_crypt32}.a") + set(_win32_secur32 "${MINGW32CE_LIBRARY_DIR}/lib${_win32_secur32}.a") endif() elseif(DOS) if(WATT_ROOT) @@ -677,24 +675,18 @@ if(CURL_DEFAULT_SSL_BACKEND) set(_valid_default_ssl_backend FALSE) endif() -if(APPLE) - cmake_dependent_option(CURL_USE_SECTRANSP "Enable Apple OS native SSL/TLS (Secure Transport)" OFF CURL_ENABLE_SSL OFF) -endif() if(WIN32) cmake_dependent_option(CURL_USE_SCHANNEL "Enable Windows native SSL/TLS (Schannel)" OFF CURL_ENABLE_SSL OFF) option(CURL_WINDOWS_SSPI "Enable SSPI on Windows" ${CURL_USE_SCHANNEL}) endif() cmake_dependent_option(CURL_USE_MBEDTLS "Enable mbedTLS for SSL/TLS" OFF CURL_ENABLE_SSL OFF) -cmake_dependent_option(CURL_USE_BEARSSL "Enable BearSSL 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) if(WIN32 OR - CURL_USE_SECTRANSP OR CURL_USE_SCHANNEL OR CURL_USE_MBEDTLS OR - CURL_USE_BEARSSL OR CURL_USE_WOLFSSL OR CURL_USE_GNUTLS OR CURL_USE_RUSTLS) @@ -712,10 +704,8 @@ option(CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG "Disable automatic loading of OpenS curl_count_true(_enabled_ssl_options_count CURL_USE_SCHANNEL - CURL_USE_SECTRANSP CURL_USE_OPENSSL CURL_USE_MBEDTLS - CURL_USE_BEARSSL CURL_USE_WOLFSSL CURL_USE_GNUTLS CURL_USE_RUSTLS @@ -739,26 +729,6 @@ if(CURL_WINDOWS_SSPI) set(USE_WINDOWS_SSPI ON) endif() -if(CURL_USE_SECTRANSP) - set(_use_core_foundation_and_core_services ON) - - find_library(SECURITY_FRAMEWORK NAMES "Security") - mark_as_advanced(SECURITY_FRAMEWORK) - if(NOT SECURITY_FRAMEWORK) - message(FATAL_ERROR "Security framework not found") - endif() - list(APPEND CURL_LIBS "-framework Security") - - set(_ssl_enabled ON) - set(USE_SECTRANSP ON) - - if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "secure-transport") - set(_valid_default_ssl_backend TRUE) - endif() - - message(WARNING "Secure Transport does not support TLS 1.3.") -endif() - if(_use_core_foundation_and_core_services) find_library(COREFOUNDATION_FRAMEWORK NAMES "CoreFoundation") mark_as_advanced(COREFOUNDATION_FRAMEWORK) @@ -842,21 +812,6 @@ if(CURL_USE_MBEDTLS) set(_curl_ca_bundle_supported TRUE) endif() -if(CURL_USE_BEARSSL) - find_package(BearSSL REQUIRED) - set(_ssl_enabled ON) - set(USE_BEARSSL ON) - list(APPEND CURL_LIBS ${BEARSSL_LIBRARIES}) - include_directories(SYSTEM ${BEARSSL_INCLUDE_DIRS}) - - if(CURL_DEFAULT_SSL_BACKEND AND CURL_DEFAULT_SSL_BACKEND STREQUAL "bearssl") - set(_valid_default_ssl_backend TRUE) - endif() - set(_curl_ca_bundle_supported TRUE) - - message(WARNING "BearSSL does not support TLS 1.3.") -endif() - if(CURL_USE_WOLFSSL) find_package(WolfSSL REQUIRED) set(_ssl_enabled ON) @@ -1651,6 +1606,23 @@ if(WIN32) # Windows XP is required for freeaddrinfo, getaddrinfo message(FATAL_ERROR "Building for Windows XP or newer is required.") endif() + + # Pre-fill detection results based on target OS version + if(HAVE_WIN32_WINNT AND HAVE_WIN32_WINNT GREATER_EQUAL 0x0600 AND # Windows Vista or newer + (MINGW OR MSVC) AND + NOT WINCE AND NOT WINDOWS_STORE) + set(HAVE_IF_NAMETOINDEX 1) + else() + set(HAVE_IF_NAMETOINDEX 0) + endif() + unset(HAVE_IF_NAMETOINDEX CACHE) +endif() + +if(NOT WIN32) + list(APPEND CURL_INCLUDES "sys/socket.h") +endif() +if(NOT WIN32 OR MINGW) + list(APPEND CURL_INCLUDES "sys/time.h") endif() # Detect headers @@ -1665,10 +1637,7 @@ check_include_file("sys/param.h" HAVE_SYS_PARAM_H) check_include_file("sys/poll.h" HAVE_SYS_POLL_H) check_include_file("sys/resource.h" HAVE_SYS_RESOURCE_H) check_include_file_concat_curl("sys/select.h" HAVE_SYS_SELECT_H) -check_include_file_concat_curl("sys/socket.h" HAVE_SYS_SOCKET_H) check_include_file("sys/sockio.h" HAVE_SYS_SOCKIO_H) -check_include_file("sys/stat.h" HAVE_SYS_STAT_H) -check_include_file_concat_curl("sys/time.h" HAVE_SYS_TIME_H) check_include_file_concat_curl("sys/types.h" HAVE_SYS_TYPES_H) check_include_file("sys/un.h" HAVE_SYS_UN_H) check_include_file_concat_curl("sys/utime.h" HAVE_SYS_UTIME_H) # sys/types.h (AmigaOS) @@ -1710,10 +1679,9 @@ foreach(_variable IN ITEMS HAVE_STDBOOL_H HAVE_STROPTS_H HAVE_SYS_IOCTL_H - HAVE_SYS_SOCKET_H HAVE_SYS_TYPES_H HAVE_UNISTD_H - ) +) if(${_variable}) string(APPEND CURL_TEST_DEFINES " -D${_variable}") endif() @@ -1749,6 +1717,9 @@ endif() # Apply to all feature checks if(WIN32) list(APPEND CMAKE_REQUIRED_LIBRARIES "${_win32_winsock}") + if(NOT WINCE AND NOT WINDOWS_STORE) + list(APPEND CMAKE_REQUIRED_LIBRARIES "iphlpapi") + endif() elseif(HAVE_LIBSOCKET) list(APPEND CMAKE_REQUIRED_LIBRARIES "socket") elseif(DOS) @@ -1800,12 +1771,12 @@ check_function_exists("eventfd" HAVE_EVENTFD) check_symbol_exists("ftruncate" "unistd.h" HAVE_FTRUNCATE) check_symbol_exists("getpeername" "${CURL_INCLUDES}" HAVE_GETPEERNAME) # winsock2.h unistd.h proto/bsdsocket.h check_symbol_exists("getsockname" "${CURL_INCLUDES}" HAVE_GETSOCKNAME) # winsock2.h unistd.h proto/bsdsocket.h +check_function_exists("if_nametoindex" HAVE_IF_NAMETOINDEX) # iphlpapi.h (Windows Vista+ non-UWP), net/if.h check_function_exists("getrlimit" HAVE_GETRLIMIT) check_function_exists("setlocale" HAVE_SETLOCALE) check_function_exists("setrlimit" HAVE_SETRLIMIT) if(NOT WIN32) - check_function_exists("if_nametoindex" HAVE_IF_NAMETOINDEX) # iphlpapi.h (Windows non-UWP), 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) @@ -1852,7 +1823,7 @@ if(WIN32) list(APPEND CMAKE_EXTRA_INCLUDE_FILES "winsock2.h") check_type_size("ADDRESS_FAMILY" SIZEOF_ADDRESS_FAMILY) set(HAVE_ADDRESS_FAMILY ${HAVE_SIZEOF_ADDRESS_FAMILY}) -elseif(HAVE_SYS_SOCKET_H) +else() list(APPEND CMAKE_EXTRA_INCLUDE_FILES "sys/socket.h") check_type_size("sa_family_t" SIZEOF_SA_FAMILY_T) set(HAVE_SA_FAMILY_T ${HAVE_SIZEOF_SA_FAMILY_T}) @@ -1875,7 +1846,7 @@ foreach(_curl_test IN ITEMS HAVE_BOOL_T STDC_HEADERS HAVE_ATOMIC - ) +) curl_internal_test(${_curl_test}) endforeach() @@ -1984,6 +1955,9 @@ set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "HAVE_CONFIG_H") if(WIN32) list(APPEND CURL_LIBS "${_win32_winsock}") + if(NOT WINCE AND NOT WINDOWS_STORE) + list(APPEND CURL_LIBS "iphlpapi") + endif() if(NOT WINCE) list(APPEND CURL_LIBS "bcrypt") endif() @@ -2004,6 +1978,9 @@ if(WIN32) endif() list(APPEND CURL_LIBS "${_win32_crypt32}") endif() + if(USE_WINDOWS_SSPI) + list(APPEND CURL_LIBS "${_win32_secur32}") + endif() endif() if(CMAKE_C_COMPILER_ID STREQUAL "MSVC") # MSVC but exclude clang-cl @@ -2031,6 +2008,7 @@ endif() # (= regenerate it). function(curl_transform_makefile_inc _input_file _output_file) file(READ ${_input_file} _makefile_inc_text) + # cmake-lint: disable=W0106 string(REPLACE "$(top_srcdir)" "\${PROJECT_SOURCE_DIR}" _makefile_inc_text ${_makefile_inc_text}) string(REPLACE "$(top_builddir)" "\${PROJECT_BINARY_DIR}" _makefile_inc_text ${_makefile_inc_text}) @@ -2102,7 +2080,6 @@ if(NOT CURL_DISABLE_NTLM AND (USE_OPENSSL OR USE_MBEDTLS OR USE_GNUTLS OR - USE_SECTRANSP OR USE_WIN32_CRYPTO OR (USE_WOLFSSL AND HAVE_WOLFSSL_DES_ECB_ENCRYPT))) set(_use_curl_ntlm_core ON) @@ -2183,8 +2160,7 @@ curl_add_if("HTTP2" USE_NGHTTP2) curl_add_if("HTTP3" USE_NGTCP2 OR USE_QUICHE OR USE_MSH3 OR USE_OPENSSL_QUIC) 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_BEARSSL OR - USE_MBEDTLS OR USE_SECTRANSP OR + OR USE_SCHANNEL OR USE_RUSTLS OR USE_MBEDTLS OR (USE_WOLFSSL AND HAVE_WOLFSSL_BIO_NEW))) curl_add_if("Unicode" ENABLE_UNICODE) curl_add_if("threadsafe" HAVE_ATOMIC OR @@ -2213,9 +2189,7 @@ set(_items "") curl_add_if("Schannel" _ssl_enabled AND USE_SCHANNEL) curl_add_if("${_openssl}" _ssl_enabled AND USE_OPENSSL AND OPENSSL_VERSION VERSION_LESS 3.0.0) curl_add_if("${_openssl} v3+" _ssl_enabled AND USE_OPENSSL AND OPENSSL_VERSION VERSION_GREATER_EQUAL 3.0.0) -curl_add_if("Secure Transport" _ssl_enabled AND USE_SECTRANSP) curl_add_if("mbedTLS" _ssl_enabled AND USE_MBEDTLS) -curl_add_if("BearSSL" _ssl_enabled AND USE_BEARSSL) curl_add_if("wolfSSL" _ssl_enabled AND USE_WOLFSSL) curl_add_if("GnuTLS" _ssl_enabled AND USE_GNUTLS) curl_add_if("rustls" _ssl_enabled AND USE_RUSTLS) diff --git a/include/curl/curl.h b/include/curl/curl.h index fb225c7903..7ef5b99349 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -158,11 +158,11 @@ typedef enum { CURLSSLBACKEND_POLARSSL CURL_DEPRECATED(7.69.0, "") = 6, CURLSSLBACKEND_WOLFSSL = 7, CURLSSLBACKEND_SCHANNEL = 8, - CURLSSLBACKEND_SECURETRANSPORT = 9, + CURLSSLBACKEND_SECURETRANSPORT CURL_DEPRECATED(8.15.0, "") = 9, CURLSSLBACKEND_AXTLS CURL_DEPRECATED(7.61.0, "") = 10, CURLSSLBACKEND_MBEDTLS = 11, CURLSSLBACKEND_MESALINK CURL_DEPRECATED(7.82.0, "") = 12, - CURLSSLBACKEND_BEARSSL = 13, + CURLSSLBACKEND_BEARSSL CURL_DEPRECATED(8.15.0, "") = 13, CURLSSLBACKEND_RUSTLS = 14 } curl_sslbackend; @@ -645,20 +645,7 @@ typedef enum { CURLE_UNRECOVERABLE_POLL, /* 99 - poll/select returned fatal error */ CURLE_TOO_LARGE, /* 100 - a value/data met its maximum */ CURLE_ECH_REQUIRED, /* 101 - ECH tried but failed */ - CURL_LAST, /* never use! */ - - CURLE_RESERVED115 = 115, /* 115-126 - used in tests */ - CURLE_RESERVED116 = 116, - CURLE_RESERVED117 = 117, - CURLE_RESERVED118 = 118, - CURLE_RESERVED119 = 119, - CURLE_RESERVED120 = 120, - CURLE_RESERVED121 = 121, - CURLE_RESERVED122 = 122, - CURLE_RESERVED123 = 123, - CURLE_RESERVED124 = 124, - CURLE_RESERVED125 = 125, - CURLE_RESERVED126 = 126 + CURL_LAST /* never use! */ } CURLcode; #ifndef CURL_NO_OLDIES /* define this to test if your app builds with all @@ -943,31 +930,31 @@ typedef enum { have introduced work-arounds for this flaw but those work-arounds 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 (1<<0) +#define CURLSSLOPT_ALLOW_BEAST (1L<<0) /* - NO_REVOKE tells libcurl to disable certificate revocation checks for those SSL backends where such behavior is present. */ -#define CURLSSLOPT_NO_REVOKE (1<<1) +#define CURLSSLOPT_NO_REVOKE (1L<<1) /* - NO_PARTIALCHAIN tells libcurl to *NOT* accept a partial certificate chain if possible. The OpenSSL backend has this ability. */ -#define CURLSSLOPT_NO_PARTIALCHAIN (1<<2) +#define CURLSSLOPT_NO_PARTIALCHAIN (1L<<2) /* - REVOKE_BEST_EFFORT tells libcurl to ignore certificate revocation offline checks and ignore missing revocation list for those SSL backends where such behavior is present. */ -#define CURLSSLOPT_REVOKE_BEST_EFFORT (1<<3) +#define CURLSSLOPT_REVOKE_BEST_EFFORT (1L<<3) /* - CURLSSLOPT_NATIVE_CA tells libcurl to use standard certificate store of operating system. Currently implemented under MS-Windows. */ -#define CURLSSLOPT_NATIVE_CA (1<<4) +#define CURLSSLOPT_NATIVE_CA (1L<<4) /* - CURLSSLOPT_AUTO_CLIENT_CERT tells libcurl to automatically locate and use a client certificate for authentication. (Schannel) */ -#define CURLSSLOPT_AUTO_CLIENT_CERT (1<<5) +#define CURLSSLOPT_AUTO_CLIENT_CERT (1L<<5) /* If possible, send data using TLS 1.3 early data */ -#define CURLSSLOPT_EARLYDATA (1<<6) +#define CURLSSLOPT_EARLYDATA (1L<<6) /* The default connection attempt delay in milliseconds for happy eyeballs. CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS.3 and happy-eyeballs-timeout-ms.d document @@ -1967,7 +1954,8 @@ typedef enum { CURLOPT(CURLOPT_SSL_VERIFYSTATUS, CURLOPTTYPE_LONG, 232), /* Set if we should enable TLS false start. */ - CURLOPT(CURLOPT_SSL_FALSESTART, CURLOPTTYPE_LONG, 233), + CURLOPTDEPRECATED(CURLOPT_SSL_FALSESTART, CURLOPTTYPE_LONG, 233, + 8.15.0, "Has no function"), /* Do not squash dot-dot sequences */ CURLOPT(CURLOPT_PATH_AS_IS, CURLOPTTYPE_LONG, 234), @@ -2301,10 +2289,10 @@ typedef enum { /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host name resolves addresses using more than one IP protocol version, this option might be handy to force libcurl to use a specific IP version. */ -#define CURL_IPRESOLVE_WHATEVER 0 /* default, uses addresses to all IP +#define CURL_IPRESOLVE_WHATEVER 0L /* default, uses addresses to all IP versions that your system allows */ -#define CURL_IPRESOLVE_V4 1 /* uses only IPv4 addresses/connections */ -#define CURL_IPRESOLVE_V6 2 /* uses only IPv6 addresses/connections */ +#define CURL_IPRESOLVE_V4 1L /* uses only IPv4 addresses/connections */ +#define CURL_IPRESOLVE_V6 2L /* uses only IPv6 addresses/connections */ /* Convenient "aliases" */ #define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER diff --git a/include/curl/curlver.h b/include/curl/curlver.h index 38661a4440..b3fc29b91c 100644 --- a/include/curl/curlver.h +++ b/include/curl/curlver.h @@ -32,13 +32,13 @@ /* This is the version number of the libcurl package from which this header file origins: */ -#define LIBCURL_VERSION "8.14.1-DEV" +#define LIBCURL_VERSION "8.15.0-DEV" /* The numeric version number is also available "in parts" by using these defines: */ #define LIBCURL_VERSION_MAJOR 8 -#define LIBCURL_VERSION_MINOR 14 -#define LIBCURL_VERSION_PATCH 1 +#define LIBCURL_VERSION_MINOR 15 +#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 will @@ -59,7 +59,7 @@ CURL_VERSION_BITS() macro since curl's own configure script greps for it and needs it to contain the full number. */ -#define LIBCURL_VERSION_NUM 0x080e01 +#define LIBCURL_VERSION_NUM 0x080f00 /* * This is the date and time when the full source package was created. The diff --git a/include/curl/system.h b/include/curl/system.h index f1c2719cfe..ebcdc5e4fd 100644 --- a/include/curl/system.h +++ b/include/curl/system.h @@ -329,7 +329,7 @@ defined(__ppc__) || defined(__powerpc__) || defined(__arm__) || \ defined(__sparc__) || defined(__mips__) || defined(__sh__) || \ defined(__XTENSA__) || \ - (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4) || \ + (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4) || \ (defined(__LONG_MAX__) && __LONG_MAX__ == 2147483647L)) # define CURL_TYPEOF_CURL_OFF_T long long # define CURL_FORMAT_CURL_OFF_T "lld" @@ -357,11 +357,11 @@ #else /* generic "safe guess" on old 32-bit style */ -# define CURL_TYPEOF_CURL_OFF_T long -# define CURL_FORMAT_CURL_OFF_T "ld" -# define CURL_FORMAT_CURL_OFF_TU "lu" -# define CURL_SUFFIX_CURL_OFF_T L -# define CURL_SUFFIX_CURL_OFF_TU UL +# define CURL_TYPEOF_CURL_OFF_T long long +# define CURL_FORMAT_CURL_OFF_T "lld" +# define CURL_FORMAT_CURL_OFF_TU "llu" +# define CURL_SUFFIX_CURL_OFF_T LL +# define CURL_SUFFIX_CURL_OFF_TU ULL # define CURL_TYPEOF_CURL_SOCKLEN_T int #endif @@ -399,52 +399,4 @@ typedef CURL_TYPEOF_CURL_OFF_T curl_off_t; #endif -/* - * CURL_ISOCPP and CURL_OFF_T_C definitions are done here in order to allow - * these to be visible and exported by the external libcurl interface API, - * while also making them visible to the library internals, simply including - * curl_setup.h, without actually needing to include curl.h internally. - * If some day this section would grow big enough, all this should be moved - * to its own header file. - */ - -/* - * Figure out if we can use the ## preprocessor operator, which is supported - * by ISO/ANSI C and C++. Some compilers support it without setting __STDC__ - * or __cplusplus so we need to carefully check for them too. - */ - -#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \ - defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \ - defined(__POCC__) || defined(__HIGHC__) || \ - defined(__ILEC400__) - /* This compiler is believed to have an ISO compatible preprocessor */ -#define CURL_ISOCPP -#else - /* This compiler is believed NOT to have an ISO compatible preprocessor */ -#undef CURL_ISOCPP -#endif - -/* - * Macros for minimum-width signed and unsigned curl_off_t integer constants. - */ - -#if defined(__BORLANDC__) && (__BORLANDC__ == 0x0551) -# define CURLINC_OFF_T_C_HLPR2(x) x -# define CURLINC_OFF_T_C_HLPR1(x) CURLINC_OFF_T_C_HLPR2(x) -# define CURL_OFF_T_C(Val) CURLINC_OFF_T_C_HLPR1(Val) ## \ - CURLINC_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T) -# define CURL_OFF_TU_C(Val) CURLINC_OFF_T_C_HLPR1(Val) ## \ - CURLINC_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU) -#else -# ifdef CURL_ISOCPP -# define CURLINC_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix -# else -# define CURLINC_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix -# endif -# define CURLINC_OFF_T_C_HLPR1(Val,Suffix) CURLINC_OFF_T_C_HLPR2(Val,Suffix) -# define CURL_OFF_T_C(Val) CURLINC_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T) -# define CURL_OFF_TU_C(Val) CURLINC_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU) -#endif - #endif /* CURLINC_SYSTEM_H */ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 9ab9e01857..9b0fda5409 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -29,7 +29,7 @@ set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "${CURL_DEBUG_MACROS} configure_file("curl_config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h") -# Get 'CSOURCES', 'HHEADERS' variables +# Get CSOURCES, HHEADERS, LIB_RCFILES variables curl_transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake") @@ -41,28 +41,22 @@ set_property(DIRECTORY APPEND PROPERTY INCLUDE_DIRECTORIES "${PROJECT_BINARY_DIR}/lib" # for "curl_config.h" ) -if(USE_ARES) - include_directories(SYSTEM ${CARES_INCLUDE_DIRS}) -endif() - if(CURL_BUILD_TESTING) - add_library( - curlu # special libcurlu library just for unittests - STATIC - EXCLUDE_FROM_ALL - ${HHEADERS} ${CSOURCES} - ) + # special libcurlu library just for unittests + add_library(curlu STATIC EXCLUDE_FROM_ALL ${HHEADERS} ${CSOURCES}) target_compile_definitions(curlu PUBLIC "CURL_STATICLIB" "UNITTESTS") target_link_libraries(curlu PRIVATE ${CURL_LIBS}) # There is plenty of parallelism when building the testdeps target. # Override the curlu batch size with the maximum to optimize performance. - set_target_properties(curlu PROPERTIES UNITY_BUILD_BATCH_SIZE 0) -endif() + set_target_properties(curlu PROPERTIES UNITY_BUILD_BATCH_SIZE 0 C_CLANG_TIDY "") -if(ENABLE_CURLDEBUG) - # We must compile this source separately to avoid memdebug.h redefinitions - # applying to it. - set_source_files_properties("memdebug.c" PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) + add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/unitprotos.h" + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMAND ${PERL_EXECUTABLE} "${PROJECT_SOURCE_DIR}/scripts/extract-unit-protos" + ${CSOURCES} > "${CMAKE_CURRENT_BINARY_DIR}/unitprotos.h" + DEPENDS "${PROJECT_SOURCE_DIR}/scripts/extract-unit-protos" ${CSOURCES} + VERBATIM) + add_custom_target(curlu-unitprotos ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/unitprotos.h") endif() ## Library definition @@ -181,7 +175,7 @@ if(BUILD_SHARED_LIBS) add_library(${PROJECT_NAME}::${LIB_SHARED} ALIAS ${LIB_SHARED}) if(WIN32) set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "dllmain.c") - set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "libcurl.rc") + set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES ${LIB_RCFILES}) if(CURL_HIDES_PRIVATE_SYMBOLS) set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "${PROJECT_SOURCE_DIR}/lib/libcurl.def") endif() @@ -217,6 +211,7 @@ if(BUILD_SHARED_LIBS) CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "SunOS" OR CMAKE_SYSTEM_NAME STREQUAL "Haiku" OR + CMAKE_SYSTEM_NAME STREQUAL "OHOS" OR # OpenHarmony CMAKE_SYSTEM_NAME STREQUAL "GNU/kFreeBSD" OR # FreeBSD comes with the a.out and ELF flavours but a.out was supported # up to v3.x and ELF from v3.x. I cannot imagine someone running CMake @@ -231,7 +226,7 @@ if(BUILD_SHARED_LIBS) option(CURL_LIBCURL_VERSIONED_SYMBOLS "Enable libcurl versioned symbols" OFF) if(CURL_LIBCURL_SOVERSION OR CURL_LIBCURL_VERSIONED_SYMBOLS) - # Get 'VERSIONCHANGE', 'VERSIONADD', 'VERSIONDEL', 'VERSIONINFO' variables + # Get VERSIONCHANGE, VERSIONADD, VERSIONDEL, VERSIONINFO variables curl_transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake") include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake") @@ -255,8 +250,6 @@ if(BUILD_SHARED_LIBS) set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "OPENSSL_") elseif(CURL_USE_MBEDTLS) set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "MBEDTLS_") - elseif(CURL_USE_BEARSSL) - set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "BEARSSL_") elseif(CURL_USE_WOLFSSL) set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "WOLFSSL_") elseif(CURL_USE_GNUTLS) diff --git a/lib/Makefile.inc b/lib/Makefile.inc index b384e09623..257e104de9 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -21,10 +21,12 @@ # SPDX-License-Identifier: curl # ########################################################################### +# Shared between CMakeLists.txt and Makefile.am LIB_CURLX_CFILES = \ curlx/base64.c \ curlx/dynbuf.c \ + curlx/inet_ntop.c \ curlx/inet_pton.c \ curlx/multibyte.c \ curlx/nonblock.c \ @@ -32,13 +34,16 @@ LIB_CURLX_CFILES = \ curlx/timediff.c \ curlx/timeval.c \ curlx/version_win32.c \ + curlx/wait.c \ curlx/warnless.c \ curlx/winapi.c LIB_CURLX_HFILES = \ + curlx/binmode.h \ curlx/base64.h \ curlx/curlx.h \ curlx/dynbuf.h \ + curlx/inet_ntop.h \ curlx/inet_pton.h \ curlx/multibyte.h \ curlx/nonblock.h \ @@ -46,6 +51,7 @@ LIB_CURLX_HFILES = \ curlx/timediff.h \ curlx/timeval.h \ curlx/version_win32.h \ + curlx/wait.h \ curlx/warnless.h \ curlx/winapi.h @@ -69,7 +75,6 @@ LIB_VAUTH_HFILES = \ vauth/vauth.h LIB_VTLS_CFILES = \ - vtls/bearssl.c \ vtls/cipher_suite.c \ vtls/gtls.c \ vtls/hostcheck.c \ @@ -80,7 +85,6 @@ LIB_VTLS_CFILES = \ vtls/rustls.c \ vtls/schannel.c \ vtls/schannel_verify.c \ - vtls/sectransp.c \ vtls/vtls.c \ vtls/vtls_scache.c \ vtls/vtls_spack.c \ @@ -88,7 +92,6 @@ LIB_VTLS_CFILES = \ vtls/x509asn1.c LIB_VTLS_HFILES = \ - vtls/bearssl.h \ vtls/cipher_suite.h \ vtls/gtls.h \ vtls/hostcheck.h \ @@ -99,7 +102,6 @@ LIB_VTLS_HFILES = \ vtls/rustls.h \ vtls/schannel.h \ vtls/schannel_int.h \ - vtls/sectransp.h \ vtls/vtls.h \ vtls/vtls_int.h \ vtls/vtls_scache.h \ @@ -208,7 +210,6 @@ LIB_CFILES = \ idn.c \ if2ip.c \ imap.c \ - inet_ntop.c \ krb5.c \ ldap.c \ llist.c \ @@ -345,7 +346,6 @@ LIB_HFILES = \ idn.h \ if2ip.h \ imap.h \ - inet_ntop.h \ llist.h \ macos.h \ memdebug.h \ diff --git a/lib/altsvc.c b/lib/altsvc.c index 602ef61def..59476d7325 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -32,7 +32,6 @@ #include "urldata.h" #include "altsvc.h" #include "curl_get_line.h" -#include "strcase.h" #include "parsedate.h" #include "sendf.h" #include "curlx/warnless.h" @@ -416,7 +415,7 @@ static bool hostcompare(const char *host, const char *check) if(hlen != clen) /* they cannot match if they have different lengths */ return FALSE; - return strncasecompare(host, check, hlen); + return curl_strnequal(host, check, hlen); } /* altsvc_flush() removes all alternatives for this source origin from the @@ -487,8 +486,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, DEBUGASSERT(asi); /* initial check for "clear" */ - if(!curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, ';') && - !curlx_str_single(&p, ';')) { + if(!curlx_str_cspn(&p, &alpn, ";\n\r")) { curlx_str_trimblanks(&alpn); /* "clear" is a magic keyword */ if(curlx_str_casecompare(&alpn, "clear")) { @@ -666,4 +664,8 @@ bool Curl_altsvc_lookup(struct altsvcinfo *asi, return FALSE; } +#if defined(DEBUGBUILD) || defined(UNITTESTS) +#undef time +#endif + #endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_ALTSVC */ diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c index 10f870a15a..007f1d7123 100644 --- a/lib/asyn-ares.c +++ b/lib/asyn-ares.c @@ -48,6 +48,7 @@ #endif #include "urldata.h" +#include "cfilters.h" #include "sendf.h" #include "hostip.h" #include "hash.h" @@ -451,8 +452,7 @@ CURLcode Curl_async_await(struct Curl_easy *data, /* Operation complete, if the lookup was successful we now have the entry in the cache. */ data->state.async.done = TRUE; - if(entry) - *entry = data->state.async.dns; + *entry = data->state.async.dns; if(result) ares_cancel(ares->channel); @@ -758,7 +758,8 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data, (pf == PF_UNSPEC) ? "A+AAAA" : ((pf == PF_INET) ? "A" : "AAAA")); hints.ai_family = pf; - hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ? + hints.ai_socktype = + (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM; /* Since the service is a numerical one, set the hint flags * accordingly to save a call to getservbyname in inside C-Ares diff --git a/lib/asyn-thrdd.c b/lib/asyn-thrdd.c index 9cd25dcd20..1ede868820 100644 --- a/lib/asyn-thrdd.c +++ b/lib/asyn-thrdd.c @@ -55,13 +55,13 @@ #endif #include "urldata.h" +#include "cfilters.h" #include "sendf.h" #include "hostip.h" #include "hash.h" #include "share.h" #include "url.h" #include "multiif.h" -#include "inet_ntop.h" #include "curl_threads.h" #include "strdup.h" @@ -423,6 +423,7 @@ static bool async_thrdd_init(struct Curl_easy *data, data->state.async.done = FALSE; data->state.async.port = port; data->state.async.ip_version = ip_version; + free(data->state.async.hostname); data->state.async.hostname = strdup(hostname); if(!data->state.async.hostname) goto err_exit; @@ -741,7 +742,8 @@ struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data, memset(&hints, 0, sizeof(hints)); hints.ai_family = pf; - hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ? + hints.ai_socktype = + (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM; /* fire up a new resolver thread! */ diff --git a/lib/bufq.c b/lib/bufq.c index 724d62f31c..8783619eed 100644 --- a/lib/bufq.c +++ b/lib/bufq.c @@ -86,44 +86,26 @@ static size_t chunk_read(struct buf_chunk *chunk, } } -static size_t chunk_unwrite(struct buf_chunk *chunk, size_t len) -{ - size_t n = chunk->w_offset - chunk->r_offset; - DEBUGASSERT(chunk->w_offset >= chunk->r_offset); - if(!n) { - return 0; - } - else if(n <= len) { - chunk->r_offset = chunk->w_offset = 0; - return n; - } - else { - chunk->w_offset -= len; - return len; - } -} - -static ssize_t chunk_slurpn(struct buf_chunk *chunk, size_t max_len, - Curl_bufq_reader *reader, - void *reader_ctx, CURLcode *err) +static CURLcode chunk_slurpn(struct buf_chunk *chunk, size_t max_len, + Curl_bufq_reader *reader, + void *reader_ctx, size_t *pnread) { unsigned char *p = &chunk->x.data[chunk->w_offset]; size_t n = chunk->dlen - chunk->w_offset; /* free amount */ - ssize_t nread; + CURLcode result; + *pnread = 0; DEBUGASSERT(chunk->dlen >= chunk->w_offset); - if(!n) { - *err = CURLE_AGAIN; - return -1; - } + if(!n) + return CURLE_AGAIN; if(max_len && n > max_len) n = max_len; - nread = reader(reader_ctx, p, n, err); - if(nread > 0) { - DEBUGASSERT((size_t)nread <= n); - chunk->w_offset += nread; + result = reader(reader_ctx, p, n, pnread); + if(!result) { + DEBUGASSERT(*pnread <= n); + chunk->w_offset += *pnread; } - return nread; + return result; } static void chunk_peek(const struct buf_chunk *chunk, @@ -357,49 +339,6 @@ static void prune_head(struct bufq *q) } } -static struct buf_chunk *chunk_prev(struct buf_chunk *head, - struct buf_chunk *chunk) -{ - while(head) { - if(head == chunk) - return NULL; - if(head->next == chunk) - return head; - head = head->next; - } - return NULL; -} - -static void prune_tail(struct bufq *q) -{ - struct buf_chunk *chunk; - - while(q->tail && chunk_is_empty(q->tail)) { - chunk = q->tail; - q->tail = chunk_prev(q->head, chunk); - if(q->tail) - q->tail->next = NULL; - if(q->head == chunk) - q->head = q->tail; - if(q->pool) { - bufcp_put(q->pool, chunk); - --q->chunk_count; - } - else if((q->chunk_count > q->max_chunks) || - (q->opts & BUFQ_OPT_NO_SPARES)) { - /* SOFT_LIMIT allowed us more than max. free spares until - * we are at max again. Or free them if we are configured - * to not use spares. */ - free(chunk); - --q->chunk_count; - } - else { - chunk->next = q->spare; - q->spare = chunk; - } - } -} - static struct buf_chunk *get_non_full_tail(struct bufq *q) { struct buf_chunk *chunk; @@ -421,90 +360,60 @@ static struct buf_chunk *get_non_full_tail(struct bufq *q) return chunk; } -ssize_t Curl_bufq_write(struct bufq *q, - const unsigned char *buf, size_t len, - CURLcode *err) +CURLcode Curl_bufq_write(struct bufq *q, + const unsigned char *buf, size_t len, + size_t *pnwritten) { struct buf_chunk *tail; - ssize_t nwritten = 0; size_t n; DEBUGASSERT(q->max_chunks > 0); + *pnwritten = 0; while(len) { tail = get_non_full_tail(q); if(!tail) { - if((q->chunk_count < q->max_chunks) || (q->opts & BUFQ_OPT_SOFT_LIMIT)) { - *err = CURLE_OUT_OF_MEMORY; - return -1; - } + if((q->chunk_count < q->max_chunks) || (q->opts & BUFQ_OPT_SOFT_LIMIT)) + /* should have gotten a tail, but did not */ + return CURLE_OUT_OF_MEMORY; break; } n = chunk_append(tail, buf, len); if(!n) break; - nwritten += n; + *pnwritten += n; buf += n; len -= n; } - if(nwritten == 0 && len) { - *err = CURLE_AGAIN; - return -1; - } - *err = CURLE_OK; - return nwritten; + return (!*pnwritten && len) ? CURLE_AGAIN : CURLE_OK; } CURLcode Curl_bufq_cwrite(struct bufq *q, const char *buf, size_t len, size_t *pnwritten) { - ssize_t n; - CURLcode result; - n = Curl_bufq_write(q, (const unsigned char *)buf, len, &result); - *pnwritten = (n < 0) ? 0 : (size_t)n; - return result; + return Curl_bufq_write(q, (const unsigned char *)buf, len, pnwritten); } -CURLcode Curl_bufq_unwrite(struct bufq *q, size_t len) +CURLcode Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len, + size_t *pnread) { - while(len && q->tail) { - len -= chunk_unwrite(q->tail, len); - prune_tail(q); - } - return len ? CURLE_AGAIN : CURLE_OK; -} - -ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len, - CURLcode *err) -{ - ssize_t nread = 0; - size_t n; - - *err = CURLE_OK; + *pnread = 0; while(len && q->head) { - n = chunk_read(q->head, buf, len); + size_t n = chunk_read(q->head, buf, len); if(n) { - nread += n; + *pnread += n; buf += n; len -= n; } prune_head(q); } - if(nread == 0) { - *err = CURLE_AGAIN; - return -1; - } - return nread; + return (!*pnread) ? CURLE_AGAIN : CURLE_OK; } CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len, size_t *pnread) { - ssize_t n; - CURLcode result; - n = Curl_bufq_read(q, (unsigned char *)buf, len, &result); - *pnread = (n < 0) ? 0 : (size_t)n; - return result; + return Curl_bufq_read(q, (unsigned char *)buf, len, pnread); } bool Curl_bufq_peek(struct bufq *q, @@ -556,156 +465,139 @@ void Curl_bufq_skip(struct bufq *q, size_t amount) } } -ssize_t Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer, - void *writer_ctx, CURLcode *err) +CURLcode Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer, + void *writer_ctx, size_t *pwritten) { const unsigned char *buf; size_t blen; - ssize_t nwritten = 0; + CURLcode result = CURLE_OK; + *pwritten = 0; while(Curl_bufq_peek(q, &buf, &blen)) { - ssize_t chunk_written; + size_t chunk_written; - chunk_written = writer(writer_ctx, buf, blen, err); - if(chunk_written < 0) { - if(!nwritten || *err != CURLE_AGAIN) { - /* blocked on first write or real error, fail */ - nwritten = -1; + result = writer(writer_ctx, buf, blen, &chunk_written); + if(result) { + if((result == CURLE_AGAIN) && *pwritten) { + /* blocked on subsequent write, report success */ + result = CURLE_OK; } break; } if(!chunk_written) { - if(!nwritten) { + if(!*pwritten) { /* treat as blocked */ - *err = CURLE_AGAIN; - nwritten = -1; + result = CURLE_AGAIN; } break; } - Curl_bufq_skip(q, (size_t)chunk_written); - nwritten += chunk_written; + *pwritten += chunk_written; + Curl_bufq_skip(q, chunk_written); } - return nwritten; + return result; } -ssize_t Curl_bufq_write_pass(struct bufq *q, - const unsigned char *buf, size_t len, - Curl_bufq_writer *writer, void *writer_ctx, - CURLcode *err) +CURLcode Curl_bufq_write_pass(struct bufq *q, + const unsigned char *buf, size_t len, + Curl_bufq_writer *writer, void *writer_ctx, + size_t *pwritten) { - ssize_t nwritten = 0, n; + CURLcode result = CURLE_OK; + size_t n; - *err = CURLE_OK; + *pwritten = 0; while(len) { if(Curl_bufq_is_full(q)) { /* try to make room in case we are full */ - n = Curl_bufq_pass(q, writer, writer_ctx, err); - if(n < 0) { - if(*err != CURLE_AGAIN) { + result = Curl_bufq_pass(q, writer, writer_ctx, &n); + if(result) { + if(result != CURLE_AGAIN) { /* real error, fail */ - return -1; + return result; } /* would block, bufq is full, give up */ break; } } - /* Add whatever is remaining now to bufq */ - n = Curl_bufq_write(q, buf, len, err); - if(n < 0) { - if(*err != CURLE_AGAIN) { + /* Add to bufq as much as there is room for */ + result = Curl_bufq_write(q, buf, len, &n); + if(result) { + if(result != CURLE_AGAIN) /* real error, fail */ - return -1; - } - /* no room in bufq */ - break; + return result; + if((result == CURLE_AGAIN) && *pwritten) + /* we did write successfully before */ + result = CURLE_OK; + return result; } - /* edge case of writer returning 0 (and len is >0) - * break or we might enter an infinite loop here */ - if(n == 0) + else if(n == 0) + /* edge case of writer returning 0 (and len is >0) + * break or we might enter an infinite loop here */ break; - /* Maybe only part of `data` has been added, continue to loop */ - buf += (size_t)n; - len -= (size_t)n; - nwritten += (size_t)n; + /* Track what we added to bufq */ + buf += n; + len -= n; + *pwritten += n; } - if(!nwritten && len) { - *err = CURLE_AGAIN; - return -1; - } - *err = CURLE_OK; - return nwritten; + return (!*pwritten && len) ? CURLE_AGAIN : CURLE_OK; } -ssize_t Curl_bufq_sipn(struct bufq *q, size_t max_len, - Curl_bufq_reader *reader, void *reader_ctx, - CURLcode *err) +CURLcode Curl_bufq_sipn(struct bufq *q, size_t max_len, + Curl_bufq_reader *reader, void *reader_ctx, + size_t *pnread) { struct buf_chunk *tail = NULL; - ssize_t nread; - *err = CURLE_AGAIN; + *pnread = 0; tail = get_non_full_tail(q); if(!tail) { - if(q->chunk_count < q->max_chunks) { - *err = CURLE_OUT_OF_MEMORY; - return -1; - } + if(q->chunk_count < q->max_chunks) + return CURLE_OUT_OF_MEMORY; /* full, blocked */ - *err = CURLE_AGAIN; - return -1; + return CURLE_AGAIN; } - nread = chunk_slurpn(tail, max_len, reader, reader_ctx, err); - if(nread < 0) { - return -1; - } - else if(nread == 0) { - /* eof */ - *err = CURLE_OK; - } - return nread; + return chunk_slurpn(tail, max_len, reader, reader_ctx, pnread); } /** * Read up to `max_len` bytes and append it to the end of the buffer queue. * if `max_len` is 0, no limit is imposed and the call behaves exactly * the same as `Curl_bufq_slurp()`. - * Returns the total amount of buf read (may be 0) or -1 on other - * reader errors. - * Note that even in case of a -1 chunks may have been read and + * Returns the total amount of buf read (may be 0) in `pnread` or error + * Note that even in case of an error chunks may have been read and * the buffer queue will have different length than before. */ -static ssize_t bufq_slurpn(struct bufq *q, size_t max_len, - Curl_bufq_reader *reader, void *reader_ctx, - CURLcode *err) +static CURLcode bufq_slurpn(struct bufq *q, size_t max_len, + Curl_bufq_reader *reader, void *reader_ctx, + size_t *pnread) { - ssize_t nread = 0, n; + CURLcode result; - *err = CURLE_AGAIN; + *pnread = 0; while(1) { - - n = Curl_bufq_sipn(q, max_len, reader, reader_ctx, err); - if(n < 0) { - if(!nread || *err != CURLE_AGAIN) { + size_t n; + result = Curl_bufq_sipn(q, max_len, reader, reader_ctx, &n); + if(result) { + if(!*pnread || result != CURLE_AGAIN) { /* blocked on first read or real error, fail */ - nread = -1; + return result; } - else - *err = CURLE_OK; + result = CURLE_OK; break; } else if(n == 0) { /* eof */ - *err = CURLE_OK; + result = CURLE_OK; break; } - nread += (size_t)n; + *pnread += n; if(max_len) { - DEBUGASSERT((size_t)n <= max_len); - max_len -= (size_t)n; + DEBUGASSERT(n <= max_len); + max_len -= n; if(!max_len) break; } @@ -713,11 +605,11 @@ static ssize_t bufq_slurpn(struct bufq *q, size_t max_len, if(q->tail && !chunk_is_full(q->tail)) break; } - return nread; + return result; } -ssize_t Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader, - void *reader_ctx, CURLcode *err) +CURLcode Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader, + void *reader_ctx, size_t *pnread) { - return bufq_slurpn(q, 0, reader, reader_ctx, err); + return bufq_slurpn(q, 0, reader, reader_ctx, pnread); } diff --git a/lib/bufq.h b/lib/bufq.h index 60059deb30..7cd1826a95 100644 --- a/lib/bufq.h +++ b/lib/bufq.h @@ -163,31 +163,22 @@ bool Curl_bufq_is_full(const struct bufq *q); /** * Write buf to the end of the buffer queue. The buf is copied * and the amount of copied bytes is returned. - * A return code of -1 indicates an error, setting `err` to the - * cause. An err of CURLE_AGAIN is returned if the buffer queue is full. + * CURLE_AGAIN is returned if the buffer queue is full. */ -ssize_t Curl_bufq_write(struct bufq *q, - const unsigned char *buf, size_t len, - CURLcode *err); - -CURLcode Curl_bufq_cwrite(struct bufq *q, - const char *buf, size_t len, +CURLcode Curl_bufq_write(struct bufq *q, + const unsigned char *buf, size_t len, size_t *pnwritten); -/** - * Remove `len` bytes from the end of the buffer queue again. - * Returns CURLE_AGAIN if less than `len` bytes were in the queue. - */ -CURLcode Curl_bufq_unwrite(struct bufq *q, size_t len); +CURLcode Curl_bufq_cwrite(struct bufq *q, + const char *buf, size_t len, + size_t *pnwritten); /** * Read buf from the start of the buffer queue. The buf is copied * and the amount of copied bytes is returned. - * A return code of -1 indicates an error, setting `err` to the - * cause. An err of CURLE_AGAIN is returned if the buffer queue is empty. */ -ssize_t Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len, - CURLcode *err); +CURLcode Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len, + size_t *pnread); CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len, size_t *pnread); @@ -214,9 +205,9 @@ bool Curl_bufq_peek_at(struct bufq *q, size_t offset, */ void Curl_bufq_skip(struct bufq *q, size_t amount); -typedef ssize_t Curl_bufq_writer(void *writer_ctx, - const unsigned char *buf, size_t len, - CURLcode *err); +typedef CURLcode Curl_bufq_writer(void *writer_ctx, + const unsigned char *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 @@ -226,24 +217,23 @@ typedef ssize_t Curl_bufq_writer(void *writer_ctx, * Note that in case of a -1 chunks may have been written and * the buffer queue will have different length than before. */ -ssize_t Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer, - void *writer_ctx, CURLcode *err); +CURLcode Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer, + void *writer_ctx, size_t *pwritten); -typedef ssize_t Curl_bufq_reader(void *reader_ctx, - unsigned char *buf, size_t len, - CURLcode *err); +typedef CURLcode Curl_bufq_reader(void *reader_ctx, + unsigned char *buf, size_t len, + size_t *pnread); /** * Read date and append it to the end of the buffer queue until the * reader returns blocking or the queue is full. A reader returns - * -1 and CURLE_AGAIN to indicate blocking. - * Returns the total amount of buf read (may be 0) or -1 on other - * reader errors. - * Note that in case of a -1 chunks may have been read and + * CURLE_AGAIN to indicate blocking. + * Returns the total amount of buf read (may be 0) in `pnread` on success. + * Note that in case of an error chunks may have been read and * the buffer queue will have different length than before. */ -ssize_t Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader, - void *reader_ctx, CURLcode *err); +CURLcode Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader, + void *reader_ctx, size_t *pnread); /** * Read *once* up to `max_len` bytes and append it to the buffer. @@ -251,9 +241,9 @@ ssize_t Curl_bufq_slurp(struct bufq *q, Curl_bufq_reader *reader, * Returns the total amount of buf read (may be 0) or -1 on other * reader errors. */ -ssize_t Curl_bufq_sipn(struct bufq *q, size_t max_len, - Curl_bufq_reader *reader, void *reader_ctx, - CURLcode *err); +CURLcode Curl_bufq_sipn(struct bufq *q, size_t max_len, + Curl_bufq_reader *reader, void *reader_ctx, + size_t *pnread); /** * Write buf to the end of the buffer queue. @@ -262,9 +252,9 @@ ssize_t Curl_bufq_sipn(struct bufq *q, size_t max_len, * on or is placed into the buffer, depending on `len` and current * amount buffered, chunk size, etc. */ -ssize_t Curl_bufq_write_pass(struct bufq *q, - const unsigned char *buf, size_t len, - Curl_bufq_writer *writer, void *writer_ctx, - CURLcode *err); +CURLcode Curl_bufq_write_pass(struct bufq *q, + const unsigned char *buf, size_t len, + Curl_bufq_writer *writer, void *writer_ctx, + size_t *pwritten); #endif /* HEADER_CURL_BUFQ_H */ diff --git a/lib/cf-h1-proxy.c b/lib/cf-h1-proxy.c index df6f575454..4aac877fc1 100644 --- a/lib/cf-h1-proxy.c +++ b/lib/cf-h1-proxy.c @@ -252,7 +252,7 @@ static CURLcode send_CONNECT(struct Curl_cfilter *cf, size_t request_len = curlx_dyn_len(&ts->request_data); size_t blen = request_len; CURLcode result = CURLE_OK; - ssize_t nwritten; + size_t nwritten; if(blen <= ts->nsent) goto out; /* we are done */ @@ -260,16 +260,15 @@ static CURLcode send_CONNECT(struct Curl_cfilter *cf, blen -= ts->nsent; buf += ts->nsent; - nwritten = cf->next->cft->do_send(cf->next, data, buf, blen, FALSE, &result); - if(nwritten < 0) { - if(result == CURLE_AGAIN) { + result = cf->next->cft->do_send(cf->next, data, buf, blen, FALSE, &nwritten); + if(result) { + if(result == CURLE_AGAIN) result = CURLE_OK; - } goto out; } - DEBUGASSERT(blen >= (size_t)nwritten); - ts->nsent += (size_t)nwritten; + DEBUGASSERT(blen >= nwritten); + ts->nsent += nwritten; Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)nwritten); out: @@ -375,11 +374,8 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, error = SELECT_OK; *done = FALSE; - if(!Curl_conn_data_pending(data, cf->sockindex)) - return CURLE_OK; - while(ts->keepon) { - ssize_t nread; + size_t nread; char byte; /* Read one byte at a time to avoid a race condition. Wait at most one @@ -397,7 +393,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf, break; } - if(nread <= 0) { + if(!nread) { if(data->set.proxyauth && data->state.authproxy.avail && data->state.aptr.proxyuserpwd) { /* proxy auth was requested and there was proxy auth available, @@ -687,8 +683,8 @@ out: } static void cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) + struct Curl_easy *data, + struct easy_pollset *ps) { struct h1_tunnel_state *ts = cf->ctx; @@ -741,7 +737,6 @@ struct Curl_cftype Curl_cft_h1_proxy = { cf_h1_proxy_connect, cf_h1_proxy_close, Curl_cf_def_shutdown, - Curl_cf_http_proxy_get_host, cf_h1_proxy_adjust_pollset, Curl_cf_def_data_pending, Curl_cf_def_send, @@ -749,7 +744,7 @@ struct Curl_cftype Curl_cft_h1_proxy = { Curl_cf_def_cntrl, Curl_cf_def_conn_is_alive, Curl_cf_def_conn_keep_alive, - Curl_cf_def_query, + Curl_cf_http_proxy_query, }; CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at, diff --git a/lib/cf-h2-proxy.c b/lib/cf-h2-proxy.c index d3bc4b0768..8a853f6b1d 100644 --- a/lib/cf-h2-proxy.c +++ b/lib/cf-h2-proxy.c @@ -28,6 +28,7 @@ #include #include "urldata.h" +#include "url.h" #include "cfilters.h" #include "connect.h" #include "curl_trc.h" @@ -80,7 +81,7 @@ struct tunnel_stream { }; static CURLcode tunnel_stream_init(struct Curl_cfilter *cf, - struct tunnel_stream *ts) + struct tunnel_stream *ts) { const char *hostname; int port; @@ -218,58 +219,28 @@ static void drain_tunnel(struct Curl_cfilter *cf, struct tunnel_stream *tunnel) { struct cf_h2_proxy_ctx *ctx = cf->ctx; - unsigned char bits; - (void)cf; - bits = CURL_CSELECT_IN; if(!tunnel->closed && !tunnel->reset && !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) - bits |= CURL_CSELECT_OUT; - if(data->state.select_bits != bits) { - CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x", - tunnel->stream_id, bits); - data->state.select_bits = bits; - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } + Curl_multi_mark_dirty(data); } -static ssize_t proxy_nw_in_reader(void *reader_ctx, - unsigned char *buf, size_t buflen, - CURLcode *err) -{ - struct Curl_cfilter *cf = reader_ctx; - ssize_t nread; - - if(cf) { - struct Curl_easy *data = CF_DATA_CURRENT(cf); - nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err); - CURL_TRC_CF(data, cf, "[0] nw_in_reader(len=%zu) -> %zd, %d", - buflen, nread, *err); - } - else { - nread = 0; - } - return nread; -} - -static ssize_t proxy_h2_nw_out_writer(void *writer_ctx, - const unsigned char *buf, size_t buflen, - CURLcode *err) +static CURLcode proxy_h2_nw_out_writer(void *writer_ctx, + const unsigned char *buf, size_t buflen, + size_t *pnwritten) { struct Curl_cfilter *cf = writer_ctx; - ssize_t nwritten; - + *pnwritten = 0; if(cf) { struct Curl_easy *data = CF_DATA_CURRENT(cf); - nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, - FALSE, err); - CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %zd, %d", - buflen, nwritten, *err); + CURLcode result; + result = Curl_conn_cf_send(cf->next, data, (const char *)buf, buflen, + FALSE, pnwritten); + CURL_TRC_CF(data, cf, "[0] nw_out_writer(len=%zu) -> %d, %zu", + buflen, result, *pnwritten); + return result; } - else { - nwritten = 0; - } - return nwritten; + return CURLE_FAILED_INIT; } static int proxy_h2_client_new(struct Curl_cfilter *cf, @@ -297,8 +268,8 @@ static int proxy_h2_client_new(struct Curl_cfilter *cf, } static ssize_t on_session_send(nghttp2_session *h2, - const uint8_t *buf, size_t blen, - int flags, void *userp); + const uint8_t *buf, size_t blen, + int flags, void *userp); static int proxy_h2_on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, void *userp); @@ -414,16 +385,16 @@ static CURLcode proxy_h2_nw_out_flush(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_h2_proxy_ctx *ctx = cf->ctx; - ssize_t nwritten; + size_t nwritten; CURLcode result; (void)data; if(Curl_bufq_is_empty(&ctx->outbufq)) return CURLE_OK; - nwritten = Curl_bufq_pass(&ctx->outbufq, proxy_h2_nw_out_writer, cf, - &result); - if(nwritten < 0) { + result = Curl_bufq_pass(&ctx->outbufq, proxy_h2_nw_out_writer, cf, + &nwritten); + if(result) { if(result == CURLE_AGAIN) { CURL_TRC_CF(data, cf, "[0] flush nw send buffer(%zu) -> EAGAIN", Curl_bufq_len(&ctx->outbufq)); @@ -479,7 +450,7 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, { struct cf_h2_proxy_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; - ssize_t nread; + size_t nread; /* Process network input buffer fist */ if(!Curl_bufq_is_empty(&ctx->inbufq)) { @@ -496,10 +467,10 @@ static CURLcode proxy_h2_progress_ingress(struct Curl_cfilter *cf, Curl_bufq_is_empty(&ctx->inbufq) && /* and we consumed our input */ !Curl_bufq_is_full(&ctx->tunnel.recvbuf)) { - nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result); - CURL_TRC_CF(data, cf, "[0] read %zu bytes nw data -> %zd, %d", - Curl_bufq_len(&ctx->inbufq), nread, result); - if(nread < 0) { + 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); + if(result) { if(result != CURLE_AGAIN) { failf(data, "Failed receiving HTTP2 data"); return result; @@ -547,17 +518,18 @@ static ssize_t on_session_send(nghttp2_session *h2, struct Curl_cfilter *cf = userp; struct cf_h2_proxy_ctx *ctx = cf->ctx; struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nwritten; + size_t nwritten; CURLcode result = CURLE_OK; (void)h2; (void)flags; DEBUGASSERT(data); - nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen, - proxy_h2_nw_out_writer, cf, &result); - if(nwritten < 0) { + result = Curl_bufq_write_pass(&ctx->outbufq, buf, blen, + proxy_h2_nw_out_writer, cf, &nwritten); + if(result) { if(result == CURLE_AGAIN) { + ctx->nw_out_blocked = 1; return NGHTTP2_ERR_WOULDBLOCK; } failf(data, "Failed sending HTTP2 data"); @@ -567,7 +539,8 @@ static ssize_t on_session_send(nghttp2_session *h2, if(!nwritten) return NGHTTP2_ERR_WOULDBLOCK; - return nwritten; + return (nwritten > SSIZE_T_MAX) ? + NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nwritten; } #ifndef CURL_DISABLE_VERBOSE_STRINGS @@ -817,7 +790,7 @@ static ssize_t tunnel_send_callback(nghttp2_session *session, struct Curl_easy *data = CF_DATA_CURRENT(cf); struct tunnel_stream *ts; CURLcode result; - ssize_t nread; + size_t nread; (void)source; (void)data; @@ -831,8 +804,8 @@ static ssize_t tunnel_send_callback(nghttp2_session *session, return NGHTTP2_ERR_CALLBACK_FAILURE; DEBUGASSERT(ts == &ctx->tunnel); - nread = Curl_bufq_read(&ts->sendbuf, buf, length, &result); - if(nread < 0) { + result = Curl_bufq_read(&ts->sendbuf, buf, length, &nread); + if(result) { if(result != CURLE_AGAIN) return NGHTTP2_ERR_CALLBACK_FAILURE; return NGHTTP2_ERR_DEFERRED; @@ -842,7 +815,8 @@ static ssize_t tunnel_send_callback(nghttp2_session *session, CURL_TRC_CF(data, cf, "[%d] tunnel_send_callback -> %zd", ts->stream_id, nread); - return nread; + return (nread > SSIZE_T_MAX) ? + NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nread; } static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, @@ -851,7 +825,7 @@ static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, { struct Curl_cfilter *cf = userp; struct cf_h2_proxy_ctx *ctx = cf->ctx; - ssize_t nwritten; + size_t nwritten; CURLcode result; (void)flags; @@ -861,14 +835,15 @@ static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags, if(stream_id != ctx->tunnel.stream_id) return NGHTTP2_ERR_CALLBACK_FAILURE; - nwritten = Curl_bufq_write(&ctx->tunnel.recvbuf, mem, len, &result); - if(nwritten < 0) { + result = Curl_bufq_write(&ctx->tunnel.recvbuf, mem, len, &nwritten); + if(result) { if(result != CURLE_AGAIN) return NGHTTP2_ERR_CALLBACK_FAILURE; #ifdef DEBUGBUILD nwritten = 0; #endif } + /* tunnel.recbuf has soft limit, any success MUST add all data */ DEBUGASSERT((size_t)nwritten == len); return 0; } @@ -1277,212 +1252,179 @@ static void cf_h2_proxy_adjust_pollset(struct Curl_cfilter *cf, } } -static ssize_t h2_handle_tunnel_close(struct Curl_cfilter *cf, - struct Curl_easy *data, - CURLcode *err) +static CURLcode h2_handle_tunnel_close(struct Curl_cfilter *cf, + struct Curl_easy *data, + size_t *pnread) { struct cf_h2_proxy_ctx *ctx = cf->ctx; - ssize_t rv = 0; + *pnread = 0; if(ctx->tunnel.error == NGHTTP2_REFUSED_STREAM) { CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new " "connection", ctx->tunnel.stream_id); connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ - *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ - return -1; + return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ } else if(ctx->tunnel.error != NGHTTP2_NO_ERROR) { failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", ctx->tunnel.stream_id, nghttp2_http2_strerror(ctx->tunnel.error), ctx->tunnel.error); - *err = CURLE_HTTP2_STREAM; - return -1; + return CURLE_HTTP2_STREAM; } else if(ctx->tunnel.reset) { failf(data, "HTTP/2 stream %u was reset", ctx->tunnel.stream_id); - *err = CURLE_RECV_ERROR; - return -1; + return CURLE_RECV_ERROR; } - *err = CURLE_OK; - rv = 0; - CURL_TRC_CF(data, cf, "[%d] handle_tunnel_close -> %zd, %d", - ctx->tunnel.stream_id, rv, *err); - return rv; + CURL_TRC_CF(data, cf, "[%d] handle_tunnel_close -> 0", + ctx->tunnel.stream_id); + return CURLE_OK; } -static ssize_t tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) +static CURLcode tunnel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, size_t *pnread) { struct cf_h2_proxy_ctx *ctx = cf->ctx; - ssize_t nread = -1; + CURLcode result = CURLE_AGAIN; - *err = CURLE_AGAIN; - if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) { - nread = Curl_bufq_read(&ctx->tunnel.recvbuf, - (unsigned char *)buf, len, err); - if(nread < 0) - goto out; - DEBUGASSERT(nread > 0); - } - - if(nread < 0) { + *pnread = 0; + if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) + result = Curl_bufq_cread(&ctx->tunnel.recvbuf, buf, len, pnread); + else { if(ctx->tunnel.closed) { - nread = h2_handle_tunnel_close(cf, data, err); + result = h2_handle_tunnel_close(cf, data, pnread); } else if(ctx->tunnel.reset || (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) || (ctx->rcvd_goaway && ctx->last_stream_id < ctx->tunnel.stream_id)) { - *err = CURLE_RECV_ERROR; - nread = -1; + result = CURLE_RECV_ERROR; } - } - else if(nread == 0) { - *err = CURLE_AGAIN; - nread = -1; + else + result = CURLE_AGAIN; } -out: - CURL_TRC_CF(data, cf, "[%d] tunnel_recv(len=%zu) -> %zd, %d", - ctx->tunnel.stream_id, len, nread, *err); - return nread; + CURL_TRC_CF(data, cf, "[%d] tunnel_recv(len=%zu) -> %d, %zu", + ctx->tunnel.stream_id, len, result, *pnread); + return result; } -static ssize_t cf_h2_proxy_recv(struct Curl_cfilter *cf, - struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) +static CURLcode cf_h2_proxy_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t len, + size_t *pnread) { struct cf_h2_proxy_ctx *ctx = cf->ctx; - ssize_t nread = -1; struct cf_call_data save; CURLcode result; - if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) { - *err = CURLE_RECV_ERROR; - return -1; - } + *pnread = 0; CF_DATA_SAVE(save, cf, data); + if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) { + result = CURLE_RECV_ERROR; + goto out; + } + if(Curl_bufq_is_empty(&ctx->tunnel.recvbuf)) { - *err = proxy_h2_progress_ingress(cf, data); - if(*err) + result = proxy_h2_progress_ingress(cf, data); + if(result) goto out; } - nread = tunnel_recv(cf, data, buf, len, err); + result = tunnel_recv(cf, data, buf, len, pnread); - if(nread > 0) { - CURL_TRC_CF(data, cf, "[%d] increase window by %zd", - ctx->tunnel.stream_id, nread); - nghttp2_session_consume(ctx->h2, ctx->tunnel.stream_id, (size_t)nread); + if(!result) { + CURL_TRC_CF(data, cf, "[%d] increase window by %zu", + ctx->tunnel.stream_id, *pnread); + nghttp2_session_consume(ctx->h2, ctx->tunnel.stream_id, *pnread); } - result = proxy_h2_progress_egress(cf, data); - if(result && (result != CURLE_AGAIN)) { - *err = result; - nread = -1; - } + result = Curl_1st_fatal(result, proxy_h2_progress_egress(cf, data)); out: if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && - (nread >= 0 || *err == CURLE_AGAIN)) { + (!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) -> %zd %d", - ctx->tunnel.stream_id, len, nread, *err); + CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %d, %zu", + ctx->tunnel.stream_id, len, result, *pnread); CF_DATA_RESTORE(cf, save); - return nread; + return result; } -static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *buf, size_t len, bool eos, - CURLcode *err) +static CURLcode cf_h2_proxy_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *buf, size_t len, bool eos, + size_t *pnwritten) { struct cf_h2_proxy_ctx *ctx = cf->ctx; struct cf_call_data save; int rv; - ssize_t nwritten; CURLcode result; (void)eos; - if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) { - *err = CURLE_SEND_ERROR; - return -1; - } + *pnwritten = 0; CF_DATA_SAVE(save, cf, data); - if(ctx->tunnel.closed) { - nwritten = -1; - *err = CURLE_SEND_ERROR; + if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) { + result = CURLE_SEND_ERROR; goto out; } - else { - nwritten = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, err); - if(nwritten < 0 && (*err != CURLE_AGAIN)) - goto out; + + if(ctx->tunnel.closed) { + result = CURLE_SEND_ERROR; + goto out; } + result = Curl_bufq_write(&ctx->tunnel.sendbuf, buf, len, pnwritten); + CURL_TRC_CF(data, cf, "cf_send(), bufq_write %d, %zd", result, *pnwritten); + if(result && (result != CURLE_AGAIN)) + goto out; + if(!Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) { /* req body data is buffered, resume the potentially suspended stream */ rv = nghttp2_session_resume_data(ctx->h2, ctx->tunnel.stream_id); if(nghttp2_is_fatal(rv)) { - *err = CURLE_SEND_ERROR; - nwritten = -1; + result = CURLE_SEND_ERROR; goto out; } } - result = proxy_h2_progress_ingress(cf, data); - if(result) { - *err = result; - nwritten = -1; - goto out; - } + result = Curl_1st_fatal(result, proxy_h2_progress_ingress(cf, data)); + result = Curl_1st_fatal(result, proxy_h2_progress_egress(cf, data)); - /* Call the nghttp2 send loop and flush to write ALL buffered data, - * headers and/or request body completely out to the network */ - result = proxy_h2_progress_egress(cf, data); - if(result && (result != CURLE_AGAIN)) { - *err = result; - nwritten = -1; - goto out; - } - - if(proxy_h2_should_close_session(ctx)) { + if(!result && proxy_h2_should_close_session(ctx)) { /* nghttp2 thinks this session is done. If the stream has not been * closed, this is an error state for out transfer */ if(ctx->tunnel.closed) { - *err = CURLE_SEND_ERROR; - nwritten = -1; + result = CURLE_SEND_ERROR; } else { CURL_TRC_CF(data, cf, "[0] send: nothing to do in this session"); - *err = CURLE_HTTP2; - nwritten = -1; + result = CURLE_HTTP2; } } out: if(!Curl_bufq_is_empty(&ctx->tunnel.recvbuf) && - (nwritten >= 0 || *err == CURLE_AGAIN)) { + (!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_send(len=%zu) -> %zd, %d, " + 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, nwritten, *err, + ctx->tunnel.stream_id, len, result, *pnwritten, nghttp2_session_get_stream_remote_window_size( ctx->h2, ctx->tunnel.stream_id), nghttp2_session_get_remote_window_size(ctx->h2), Curl_bufq_len(&ctx->tunnel.sendbuf), Curl_bufq_len(&ctx->outbufq)); CF_DATA_RESTORE(cf, save); - return nwritten; + return result; } static CURLcode cf_h2_proxy_flush(struct Curl_cfilter *cf, @@ -1533,11 +1475,11 @@ static bool proxy_h2_connisalive(struct Curl_cfilter *cf, not in use by any other transfer, there should not be any data here, only "protocol frames" */ CURLcode result; - ssize_t nread = -1; + size_t nread; *input_pending = FALSE; - nread = Curl_bufq_slurp(&ctx->inbufq, proxy_nw_in_reader, cf, &result); - if(nread != -1) { + result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread); + if(!result) { if(proxy_h2_process_pending_input(cf, data, &result) < 0) /* immediate error, considered dead */ alive = FALSE; @@ -1559,15 +1501,16 @@ static bool cf_h2_proxy_is_alive(struct Curl_cfilter *cf, bool *input_pending) { struct cf_h2_proxy_ctx *ctx = cf->ctx; - CURLcode result; + bool alive; struct cf_call_data save; + *input_pending = FALSE; CF_DATA_SAVE(save, cf, data); - result = (ctx && ctx->h2 && proxy_h2_connisalive(cf, data, input_pending)); + alive = (ctx && ctx->h2 && proxy_h2_connisalive(cf, data, input_pending)); CURL_TRC_CF(data, cf, "[0] conn alive -> %d, input_pending=%d", - result, *input_pending); + alive, *input_pending); CF_DATA_RESTORE(cf, save); - return result; + return alive; } static CURLcode cf_h2_proxy_query(struct Curl_cfilter *cf, @@ -1577,6 +1520,10 @@ static CURLcode cf_h2_proxy_query(struct Curl_cfilter *cf, struct cf_h2_proxy_ctx *ctx = cf->ctx; switch(query) { + case CF_QUERY_HOST_PORT: + *pres1 = (int)cf->conn->http_proxy.port; + *((const char **)pres2) = cf->conn->http_proxy.host.name; + return CURLE_OK; case CF_QUERY_NEED_FLUSH: { if(!Curl_bufq_is_empty(&ctx->outbufq) || !Curl_bufq_is_empty(&ctx->tunnel.sendbuf)) { @@ -1624,7 +1571,6 @@ struct Curl_cftype Curl_cft_h2_proxy = { cf_h2_proxy_connect, cf_h2_proxy_close, cf_h2_proxy_shutdown, - Curl_cf_http_proxy_get_host, cf_h2_proxy_adjust_pollset, cf_h2_proxy_data_pending, cf_h2_proxy_send, diff --git a/lib/cf-haproxy.c b/lib/cf-haproxy.c index 7bc12dbbb1..6183ca2c13 100644 --- a/lib/cf-haproxy.c +++ b/lib/cf-haproxy.c @@ -131,17 +131,17 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf, case HAPROXY_SEND: len = curlx_dyn_len(&ctx->data_out); if(len > 0) { - ssize_t nwritten; - nwritten = Curl_conn_cf_send(cf->next, data, - curlx_dyn_ptr(&ctx->data_out), len, FALSE, - &result); - if(nwritten < 0) { + size_t nwritten; + result = Curl_conn_cf_send(cf->next, data, + curlx_dyn_ptr(&ctx->data_out), len, FALSE, + &nwritten); + if(result) { if(result != CURLE_AGAIN) goto out; result = CURLE_OK; nwritten = 0; } - curlx_dyn_tail(&ctx->data_out, len - (size_t)nwritten); + curlx_dyn_tail(&ctx->data_out, len - nwritten); if(curlx_dyn_len(&ctx->data_out) > 0) { result = CURLE_OK; goto out; @@ -197,7 +197,6 @@ struct Curl_cftype Curl_cft_haproxy = { cf_haproxy_connect, cf_haproxy_close, Curl_cf_def_shutdown, - Curl_cf_def_get_host, cf_haproxy_adjust_pollset, Curl_cf_def_data_pending, Curl_cf_def_send, diff --git a/lib/cf-https-connect.c b/lib/cf-https-connect.c index cd0d226efd..b89ed80e55 100644 --- a/lib/cf-https-connect.c +++ b/lib/cf-https-connect.c @@ -55,6 +55,7 @@ struct cf_hc_baller { CURLcode result; struct curltime started; int reply_ms; + unsigned char transport; enum alpnid alpn_id; BIT(shutdown); }; @@ -117,17 +118,20 @@ struct cf_hc_ctx { CURLcode result; /* overall result */ struct cf_hc_baller ballers[2]; size_t baller_count; - unsigned int soft_eyeballs_timeout_ms; - unsigned int hard_eyeballs_timeout_ms; + timediff_t soft_eyeballs_timeout_ms; + timediff_t hard_eyeballs_timeout_ms; }; static void cf_hc_baller_assign(struct cf_hc_baller *b, - enum alpnid alpn_id) + enum alpnid alpn_id, + unsigned char def_transport) { b->alpn_id = alpn_id; + b->transport = def_transport; switch(b->alpn_id) { case ALPN_h3: b->name = "h3"; + b->transport = TRNSPRT_QUIC; break; case ALPN_h2: b->name = "h2"; @@ -218,6 +222,7 @@ static CURLcode baller_connected(struct Curl_cfilter *cf, winner->name, (int)curlx_timediff(curlx_now(), winner->started)); + /* install the winning filter below this one. */ cf->next = winner->cf; winner->cf = NULL; @@ -268,15 +273,16 @@ static bool time_to_start_next(struct Curl_cfilter *cf, } elapsed_ms = curlx_timediff(now, ctx->started); if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) { - CURL_TRC_CF(data, cf, "hard timeout of %dms reached, starting %s", + CURL_TRC_CF(data, cf, "hard timeout of %" FMT_TIMEDIFF_T "ms reached, " + "starting %s", ctx->hard_eyeballs_timeout_ms, ctx->ballers[idx].name); return TRUE; } if((idx > 0) && (elapsed_ms >= ctx->soft_eyeballs_timeout_ms)) { if(cf_hc_baller_reply_ms(&ctx->ballers[idx - 1], data) < 0) { - CURL_TRC_CF(data, cf, "soft timeout of %dms reached, %s has not " - "seen any data, starting %s", + CURL_TRC_CF(data, cf, "soft timeout of %" FMT_TIMEDIFF_T "ms reached, " + "%s has not seen any data, starting %s", ctx->soft_eyeballs_timeout_ms, ctx->ballers[idx - 1].name, ctx->ballers[idx].name); return TRUE; @@ -311,11 +317,11 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, DEBUGASSERT(!ctx->ballers[i].cf); CURL_TRC_CF(data, cf, "connect, init"); ctx->started = now; - cf_hc_baller_init(&ctx->ballers[0], cf, data, cf->conn->transport); + cf_hc_baller_init(&ctx->ballers[0], cf, data, ctx->ballers[0].transport); if(ctx->baller_count > 1) { Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS); - CURL_TRC_CF(data, cf, "set next attempt to start in %ums", - ctx->soft_eyeballs_timeout_ms); + CURL_TRC_CF(data, cf, "set next attempt to start in %" FMT_TIMEDIFF_T + "ms", ctx->soft_eyeballs_timeout_ms); } ctx->state = CF_HC_CONNECT; FALLTHROUGH(); @@ -330,7 +336,7 @@ static CURLcode cf_hc_connect(struct Curl_cfilter *cf, } if(time_to_start_next(cf, data, 1, now)) { - cf_hc_baller_init(&ctx->ballers[1], cf, data, cf->conn->transport); + cf_hc_baller_init(&ctx->ballers[1], cf, data, ctx->ballers[1].transport); } if((ctx->baller_count > 1) && cf_hc_baller_is_active(&ctx->ballers[1])) { @@ -423,8 +429,8 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf, } static void cf_hc_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) + struct Curl_easy *data, + struct easy_pollset *ps) { if(!cf->connected) { struct cf_hc_ctx *ctx = cf->ctx; @@ -561,7 +567,6 @@ struct Curl_cftype Curl_cft_http_connect = { cf_hc_connect, cf_hc_close, cf_hc_shutdown, - Curl_cf_def_get_host, cf_hc_adjust_pollset, cf_hc_data_pending, Curl_cf_def_send, @@ -574,7 +579,8 @@ struct Curl_cftype Curl_cft_http_connect = { static CURLcode cf_hc_create(struct Curl_cfilter **pcf, struct Curl_easy *data, - enum alpnid *alpnids, size_t alpn_count) + enum alpnid *alpnids, size_t alpn_count, + unsigned char def_transport) { struct Curl_cfilter *cf = NULL; struct cf_hc_ctx *ctx; @@ -596,7 +602,7 @@ static CURLcode cf_hc_create(struct Curl_cfilter **pcf, goto out; } for(i = 0; i < alpn_count; ++i) - cf_hc_baller_assign(&ctx->ballers[i], alpnids[i]); + cf_hc_baller_assign(&ctx->ballers[i], alpnids[i], def_transport); for(; i < CURL_ARRAYSIZE(ctx->ballers); ++i) ctx->ballers[i].alpn_id = ALPN_none; ctx->baller_count = alpn_count; @@ -616,13 +622,14 @@ out: static CURLcode cf_http_connect_add(struct Curl_easy *data, struct connectdata *conn, int sockindex, - enum alpnid *alpn_ids, size_t alpn_count) + enum alpnid *alpn_ids, size_t alpn_count, + unsigned char def_transport) { struct Curl_cfilter *cf; CURLcode result = CURLE_OK; DEBUGASSERT(data); - result = cf_hc_create(&cf, data, alpn_ids, alpn_count); + result = cf_hc_create(&cf, data, alpn_ids, alpn_count, def_transport); if(result) goto out; Curl_conn_cf_add(data, conn, sockindex, cf); @@ -679,7 +686,7 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data, continue; switch(alpn) { case ALPN_h3: - if(Curl_conn_may_http3(data, conn)) + if(Curl_conn_may_http3(data, conn, conn->transport_wanted)) break; /* not possible */ if(data->state.http_neg.allowed & CURL_HTTP_V3x) { CURL_TRC_CF(data, cf, "adding h3 via HTTPS-RR"); @@ -708,7 +715,7 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data, if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) && (data->state.http_neg.wanted & CURL_HTTP_V3x) && !cf_https_alpns_contain(ALPN_h3, alpn_ids, alpn_count)) { - result = Curl_conn_may_http3(data, conn); + result = Curl_conn_may_http3(data, conn, conn->transport_wanted); if(!result) { CURL_TRC_CF(data, cf, "adding wanted h3"); alpn_ids[alpn_count++] = ALPN_h3; @@ -733,7 +740,9 @@ CURLcode Curl_cf_https_setup(struct Curl_easy *data, /* If we identified ALPNs to use, install our filter. Otherwise, * install nothing, so our call will use a default connect setup. */ if(alpn_count) { - result = cf_http_connect_add(data, conn, sockindex, alpn_ids, alpn_count); + result = cf_http_connect_add(data, conn, sockindex, + alpn_ids, alpn_count, + conn->transport_wanted); } out: diff --git a/lib/cf-socket.c b/lib/cf-socket.c index e31977201c..273258feaf 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -73,7 +73,6 @@ #include "url.h" /* for Curl_safefree() */ #include "multiif.h" #include "sockaddr.h" /* required for Curl_sockaddr_storage */ -#include "inet_ntop.h" #include "curlx/inet_pton.h" #include "progress.h" #include "curlx/warnless.h" @@ -395,10 +394,10 @@ static CURLcode socket_open(struct Curl_easy *data, * */ CURLcode Curl_socket_open(struct Curl_easy *data, - const struct Curl_addrinfo *ai, - struct Curl_sockaddr_ex *addr, - int transport, - curl_socket_t *sockfd) + const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, + int transport, + curl_socket_t *sockfd) { struct Curl_sockaddr_ex dummy; CURLcode result; @@ -996,8 +995,6 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data) cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; socket_close(data, cf->conn, !ctx->accepted, ctx->sock); ctx->sock = CURL_SOCKET_BAD; - if(ctx->active && cf->sockindex == FIRSTSOCKET) - cf->conn->remote_addr = NULL; ctx->active = FALSE; memset(&ctx->started_at, 0, sizeof(ctx->started_at)); memset(&ctx->connected_at, 0, sizeof(ctx->connected_at)); @@ -1095,7 +1092,7 @@ static CURLcode set_remote_ip(struct Curl_cfilter *cf, } static CURLcode cf_socket_open(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data) { struct cf_socket_ctx *ctx = cf->ctx; int error = 0; @@ -1378,22 +1375,9 @@ out: return result; } -static void cf_socket_get_host(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char **phost, - const char **pdisplay_host, - int *pport) -{ - struct cf_socket_ctx *ctx = cf->ctx; - (void)data; - *phost = cf->conn->host.name; - *pdisplay_host = cf->conn->host.dispname; - *pport = ctx->ip.remote_port; -} - static void cf_socket_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_socket_ctx *ctx = cf->ctx; @@ -1420,17 +1404,6 @@ static void cf_socket_adjust_pollset(struct Curl_cfilter *cf, } } -static bool cf_socket_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - struct cf_socket_ctx *ctx = cf->ctx; - int readable; - - (void)data; - readable = SOCKET_READABLE(ctx->sock, 0); - return readable > 0 && (readable & CURL_CSELECT_IN); -} - #ifdef USE_WINSOCK #ifndef SIO_IDEAL_SEND_BACKLOG_QUERY @@ -1457,17 +1430,18 @@ static void win_update_sndbuf_size(struct cf_socket_ctx *ctx) #endif /* USE_WINSOCK */ -static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, - CURLcode *err) +static CURLcode cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, bool eos, + size_t *pnwritten) { struct cf_socket_ctx *ctx = cf->ctx; curl_socket_t fdsave; ssize_t nwritten; size_t orig_len = len; + CURLcode result = CURLE_OK; (void)eos; /* unused */ - *err = CURLE_OK; + *pnwritten = 0; fdsave = cf->conn->sock[cf->sockindex]; cf->conn->sock[cf->sockindex] = ctx->sock; @@ -1478,10 +1452,8 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, Curl_rand_bytes(data, FALSE, &c, 1); if(c >= ((100-ctx->wblock_percent)*256/100)) { CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len); - *err = CURLE_AGAIN; - nwritten = -1; cf->conn->sock[cf->sockindex] = fdsave; - return nwritten; + return CURLE_AGAIN; } } if(cf->cft != &Curl_cft_udp && ctx->wpartial_percent > 0 && len > 8) { @@ -1496,15 +1468,14 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, #if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */ if(cf->conn->bits.tcp_fastopen) { nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN, - &cf->conn->remote_addr->curl_sa_addr, - cf->conn->remote_addr->addrlen); + &ctx->addr.curl_sa_addr, ctx->addr.addrlen); cf->conn->bits.tcp_fastopen = FALSE; } else #endif nwritten = swrite(ctx->sock, buf, len); - if(-1 == nwritten) { + if(nwritten < 0) { int sockerr = SOCKERRNO; if( @@ -1521,36 +1492,38 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data, #endif ) { /* this is just a case of EWOULDBLOCK */ - *err = CURLE_AGAIN; + result = CURLE_AGAIN; } else { char buffer[STRERROR_LEN]; failf(data, "Send failure: %s", Curl_strerror(sockerr, buffer, sizeof(buffer))); data->state.os_errno = sockerr; - *err = CURLE_SEND_ERROR; + result = CURLE_SEND_ERROR; } } + else + *pnwritten = (size_t)nwritten; #if defined(USE_WINSOCK) - if(!*err) + if(!result) win_update_sndbuf_size(ctx); #endif - CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, err=%d", - orig_len, (int)nwritten, *err); + CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, %zu", + orig_len, result, *pnwritten); cf->conn->sock[cf->sockindex] = fdsave; - return nwritten; + return result; } -static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) +static CURLcode cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, size_t *pnread) { struct cf_socket_ctx *ctx = cf->ctx; + CURLcode result = CURLE_OK; ssize_t nread; - *err = CURLE_OK; - + *pnread = 0; #ifdef DEBUGBUILD /* simulate network blocking/partial reads */ if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) { @@ -1558,8 +1531,7 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, Curl_rand(data, &c, 1); if(c >= ((100-ctx->rblock_percent)*256/100)) { CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len); - *err = CURLE_AGAIN; - return -1; + return CURLE_AGAIN; } } if(cf->cft != &Curl_cft_udp && ctx->recv_max && ctx->recv_max < len) { @@ -1570,10 +1542,9 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } #endif - *err = CURLE_OK; nread = sread(ctx->sock, buf, len); - if(-1 == nread) { + if(nread < 0) { int sockerr = SOCKERRNO; if( @@ -1589,25 +1560,25 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data, #endif ) { /* this is just a case of EWOULDBLOCK */ - *err = CURLE_AGAIN; + result = CURLE_AGAIN; } else { char buffer[STRERROR_LEN]; - failf(data, "Recv failure: %s", Curl_strerror(sockerr, buffer, sizeof(buffer))); data->state.os_errno = sockerr; - *err = CURLE_RECV_ERROR; + result = CURLE_RECV_ERROR; } } + else + *pnread = (size_t)nread; - CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread, - *err); - if(nread > 0 && !ctx->got_first_byte) { + CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, %zu", len, result, *pnread); + if(!result && !ctx->got_first_byte) { ctx->first_byte_at = curlx_now(); ctx->got_first_byte = TRUE; } - return nread; + return result; } static void cf_socket_update_data(struct Curl_cfilter *cf, @@ -1631,7 +1602,6 @@ static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data) set_local_ip(cf, data); if(cf->sockindex == FIRSTSOCKET) { cf->conn->primary = ctx->ip; - cf->conn->remote_addr = &ctx->addr; #ifdef USE_IPV6 cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6); #endif @@ -1713,6 +1683,15 @@ static CURLcode cf_socket_query(struct Curl_cfilter *cf, DEBUGASSERT(pres2); *((curl_socket_t *)pres2) = ctx->sock; return CURLE_OK; + case CF_QUERY_TRANSPORT: + DEBUGASSERT(pres1); + *pres1 = ctx->transport; + return CURLE_OK; + case CF_QUERY_REMOTE_ADDR: + DEBUGASSERT(pres2); + *((const struct Curl_sockaddr_ex **)pres2) = cf->connected ? + &ctx->addr : NULL; + return CURLE_OK; case CF_QUERY_CONNECT_REPLY_MS: if(ctx->got_first_byte) { timediff_t ms = curlx_timediff(ctx->first_byte_at, ctx->started_at); @@ -1763,9 +1742,8 @@ struct Curl_cftype Curl_cft_tcp = { cf_tcp_connect, cf_socket_close, cf_socket_shutdown, - cf_socket_get_host, cf_socket_adjust_pollset, - cf_socket_data_pending, + Curl_cf_def_data_pending, cf_socket_send, cf_socket_recv, cf_socket_cntrl, @@ -1918,9 +1896,8 @@ struct Curl_cftype Curl_cft_udp = { cf_udp_connect, cf_socket_close, cf_socket_shutdown, - cf_socket_get_host, cf_socket_adjust_pollset, - cf_socket_data_pending, + Curl_cf_def_data_pending, cf_socket_send, cf_socket_recv, cf_socket_cntrl, @@ -1973,9 +1950,8 @@ struct Curl_cftype Curl_cft_unix = { cf_tcp_connect, cf_socket_close, cf_socket_shutdown, - cf_socket_get_host, cf_socket_adjust_pollset, - cf_socket_data_pending, + Curl_cf_def_data_pending, cf_socket_send, cf_socket_recv, cf_socket_cntrl, @@ -2020,7 +1996,7 @@ out: } static timediff_t cf_tcp_accept_timeleft(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data) { struct cf_socket_ctx *ctx = cf->ctx; timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT; @@ -2194,9 +2170,8 @@ struct Curl_cftype Curl_cft_tcp_accept = { cf_tcp_accept_connect, cf_socket_close, cf_socket_shutdown, - cf_socket_get_host, cf_socket_adjust_pollset, - cf_socket_data_pending, + Curl_cf_def_data_pending, cf_socket_send, cf_socket_recv, cf_socket_cntrl, @@ -2222,7 +2197,7 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data, result = CURLE_OUT_OF_MEMORY; goto out; } - ctx->transport = conn->transport; + ctx->transport = TRNSPRT_TCP; ctx->sock = *s; ctx->listening = TRUE; ctx->accepted = FALSE; diff --git a/lib/cf-socket.h b/lib/cf-socket.h index d3e3509842..90daf079a0 100644 --- a/lib/cf-socket.h +++ b/lib/cf-socket.h @@ -68,10 +68,10 @@ CURLcode Curl_parse_interface(const char *input, * */ CURLcode Curl_socket_open(struct Curl_easy *data, - const struct Curl_addrinfo *ai, - struct Curl_sockaddr_ex *addr, - int transport, - curl_socket_t *sockfd); + const struct Curl_addrinfo *ai, + struct Curl_sockaddr_ex *addr, + int transport, + curl_socket_t *sockfd); int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn, curl_socket_t sock); diff --git a/lib/cfilters.c b/lib/cfilters.c index 00090f0c71..01f1e28218 100644 --- a/lib/cfilters.c +++ b/lib/cfilters.c @@ -67,22 +67,9 @@ CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf, static void conn_report_connect_stats(struct Curl_easy *data, struct connectdata *conn); -void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data, - const char **phost, const char **pdisplay_host, - int *pport) -{ - if(cf->next) - cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport); - else { - *phost = cf->conn->host.name; - *pdisplay_host = cf->conn->host.dispname; - *pport = cf->conn->primary.remote_port; - } -} - void Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) + struct Curl_easy *data, + struct easy_pollset *ps) { /* NOP */ (void)cf; @@ -97,21 +84,23 @@ bool Curl_cf_def_data_pending(struct Curl_cfilter *cf, cf->next->cft->has_data_pending(cf->next, data) : FALSE; } -ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, +CURLcode Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, bool eos, - CURLcode *err) + size_t *pnwritten) { - return cf->next ? - cf->next->cft->do_send(cf->next, data, buf, len, eos, err) : - CURLE_RECV_ERROR; + if(cf->next) + return cf->next->cft->do_send(cf->next, data, buf, len, eos, pnwritten); + *pnwritten = 0; + return CURLE_RECV_ERROR; } -ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) +CURLcode Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, size_t *pnread) { - return cf->next ? - cf->next->cft->do_recv(cf->next, data, buf, len, err) : - CURLE_SEND_ERROR; + if(cf->next) + return cf->next->cft->do_recv(cf->next, data, buf, len, pnread); + *pnread = 0; + return CURLE_SEND_ERROR; } bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf, @@ -234,52 +223,102 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done) return result; } -ssize_t Curl_cf_recv(struct Curl_easy *data, int num, char *buf, - size_t len, CURLcode *code) +CURLcode Curl_cf_recv(struct Curl_easy *data, int num, char *buf, + size_t len, size_t *pnread) { struct Curl_cfilter *cf; DEBUGASSERT(data); DEBUGASSERT(data->conn); - *code = CURLE_OK; cf = data->conn->cfilter[num]; - while(cf && !cf->connected) { + while(cf && !cf->connected) cf = cf->next; - } - if(cf) { - ssize_t nread = cf->cft->do_recv(cf, data, buf, len, code); - DEBUGASSERT(nread >= 0 || *code); - DEBUGASSERT(nread < 0 || !*code); - return nread; - } + if(cf) + return cf->cft->do_recv(cf, data, buf, len, pnread); failf(data, "recv: no filter connected"); - *code = CURLE_FAILED_INIT; - return -1; + DEBUGASSERT(0); + *pnread = 0; + return CURLE_FAILED_INIT; } -ssize_t Curl_cf_send(struct Curl_easy *data, int num, - const void *mem, size_t len, bool eos, - CURLcode *code) +CURLcode Curl_cf_send(struct Curl_easy *data, int num, + const void *mem, size_t len, bool eos, + size_t *pnwritten) { struct Curl_cfilter *cf; DEBUGASSERT(data); DEBUGASSERT(data->conn); - *code = CURLE_OK; cf = data->conn->cfilter[num]; - while(cf && !cf->connected) { + while(cf && !cf->connected) cf = cf->next; - } if(cf) { - ssize_t nwritten = cf->cft->do_send(cf, data, mem, len, eos, code); - DEBUGASSERT(nwritten >= 0 || *code); - DEBUGASSERT(nwritten < 0 || !*code || !len); - return nwritten; + return cf->cft->do_send(cf, data, mem, len, eos, pnwritten); } failf(data, "send: no filter connected"); DEBUGASSERT(0); - *code = CURLE_FAILED_INIT; - return -1; + *pnwritten = 0; + return CURLE_FAILED_INIT; +} + +struct cf_io_ctx { + struct Curl_easy *data; + struct Curl_cfilter *cf; +}; + +static CURLcode cf_bufq_reader(void *writer_ctx, + unsigned char *buf, size_t blen, + size_t *pnread) +{ + struct cf_io_ctx *io = writer_ctx; + return Curl_conn_cf_recv(io->cf, io->data, (char *)buf, blen, pnread); +} + +CURLcode Curl_cf_recv_bufq(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct bufq *bufq, + size_t maxlen, + size_t *pnread) +{ + struct cf_io_ctx io; + + if(!cf || !data) { + *pnread = 0; + return CURLE_BAD_FUNCTION_ARGUMENT; + } + io.data = data; + io.cf = cf; + return Curl_bufq_sipn(bufq, maxlen, cf_bufq_reader, &io, pnread); +} + +static CURLcode cf_bufq_writer(void *writer_ctx, + const unsigned char *buf, size_t buflen, + size_t *pnwritten) +{ + struct cf_io_ctx *io = writer_ctx; + return Curl_conn_cf_send(io->cf, io->data, (const char *)buf, + buflen, FALSE, pnwritten); +} + +CURLcode Curl_cf_send_bufq(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct bufq *bufq, + const unsigned char *buf, size_t blen, + size_t *pnwritten) +{ + struct cf_io_ctx io; + + if(!cf || !data) { + *pnwritten = 0; + return CURLE_BAD_FUNCTION_ARGUMENT; + } + io.data = data; + io.cf = cf; + if(buf && blen) + return Curl_bufq_write_pass(bufq, buf, blen, cf_bufq_writer, &io, + pnwritten); + else + return Curl_bufq_pass(bufq, cf_bufq_writer, &io, pnwritten); } CURLcode Curl_cf_create(struct Curl_cfilter **pcf, @@ -381,23 +420,23 @@ void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data) cf->cft->do_close(cf, data); } -ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, - CURLcode *err) +CURLcode Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, bool eos, + size_t *pnwritten) { if(cf) - return cf->cft->do_send(cf, data, buf, len, eos, err); - *err = CURLE_SEND_ERROR; - return -1; + return cf->cft->do_send(cf, data, buf, len, eos, pnwritten); + *pnwritten = 0; + return CURLE_SEND_ERROR; } -ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) +CURLcode Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, size_t *pnread) { if(cf) - return cf->cft->do_recv(cf, data, buf, len, err); - *err = CURLE_RECV_ERROR; - return -1; + return cf->cft->do_recv(cf, data, buf, len, pnread); + *pnread = 0; + return CURLE_RECV_ERROR; } CURLcode Curl_conn_connect(struct Curl_easy *data, @@ -541,6 +580,19 @@ bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex) return conn ? Curl_conn_cf_is_ssl(conn->cfilter[sockindex]) : FALSE; } +bool Curl_conn_get_ssl_info(struct Curl_easy *data, + struct connectdata *conn, int sockindex, + struct curl_tlssessioninfo *info) +{ + if(Curl_conn_is_ssl(conn, sockindex)) { + struct Curl_cfilter *cf = conn->cfilter[sockindex]; + CURLcode result = cf ? cf->cft->query(cf, data, CF_QUERY_SSL_INFO, + NULL, (void *)info) : CURLE_UNKNOWN_OPTION; + return !result; + } + return FALSE; +} + bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex) { struct Curl_cfilter *cf = conn ? conn->cfilter[sockindex] : NULL; @@ -554,6 +606,13 @@ bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex) return FALSE; } +unsigned char Curl_conn_get_transport(struct Curl_easy *data, + struct connectdata *conn) +{ + struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET]; + return Curl_conn_cf_get_transport(cf, data); +} + unsigned char Curl_conn_http_version(struct Curl_easy *data, struct connectdata *conn) { @@ -678,31 +737,35 @@ int Curl_conn_cf_poll(struct Curl_cfilter *cf, return Curl_poll(pfds, npfds, timeout_ms); } -void Curl_conn_get_host(struct Curl_easy *data, int sockindex, - const char **phost, const char **pdisplay_host, - int *pport) +void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex, + const char **phost, int *pport) { - struct Curl_cfilter *cf; + struct Curl_cfilter *cf, *cf_proxy = NULL; DEBUGASSERT(data->conn); cf = data->conn->cfilter[sockindex]; - if(cf) { - cf->cft->get_host(cf, data, phost, pdisplay_host, pport); + /* Find the "lowest" tunneling proxy filter that has not connected yet. */ + while(cf && !cf->connected) { + if((cf->cft->flags & (CF_TYPE_IP_CONNECT|CF_TYPE_PROXY)) == + (CF_TYPE_IP_CONNECT|CF_TYPE_PROXY)) + cf_proxy = cf; + cf = cf->next; } - else { - /* Some filter ask during shutdown for this, mainly for debugging - * purposes. We hand out the defaults, however this is not always - * accurate, as the connection might be tunneled, etc. But all that - * state is already gone here. */ + /* cf_proxy (!= NULL) is not connected yet. It is talking + * to an interim host and any authentication or other things apply + * to this interim host and port. */ + if(!cf_proxy || cf_proxy->cft->query(cf_proxy, data, CF_QUERY_HOST_PORT, + pport, CURL_UNCONST(phost))) { + /* Everything connected or query unsuccessful, the overall + * connection's destination is the answer */ *phost = data->conn->host.name; - *pdisplay_host = data->conn->host.dispname; *pport = data->conn->remote_port; } } CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf, - struct Curl_easy *data, - int event, int arg1, void *arg2) + struct Curl_easy *data, + int event, int arg1, void *arg2) { (void)cf; (void)data; @@ -738,6 +801,26 @@ curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf, return CURL_SOCKET_BAD; } +unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + int transport = 0; + if(cf && !cf->cft->query(cf, data, CF_QUERY_TRANSPORT, &transport, NULL)) + return (unsigned char)transport; + return (unsigned char)(data->conn ? data->conn->transport_wanted : 0); +} + +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 && + !cf->cft->query(cf, data, CF_QUERY_REMOTE_ADDR, NULL, + CURL_UNCONST(&remote_addr))) + return remote_addr; + return NULL; +} + CURLcode Curl_conn_cf_get_ip_info(struct Curl_cfilter *cf, struct Curl_easy *data, int *is_ipv6, struct ip_quadruple *ipquad) @@ -760,6 +843,13 @@ curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex) return data->conn ? data->conn->sock[sockindex] : CURL_SOCKET_BAD; } +const struct Curl_sockaddr_ex * +Curl_conn_get_remote_addr(struct Curl_easy *data, int sockindex) +{ + struct Curl_cfilter *cf = data->conn ? data->conn->cfilter[sockindex] : NULL; + return cf ? cf_get_remote_addr(cf, data) : NULL; +} + void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex) { if(data->conn) { @@ -878,8 +968,8 @@ CURLcode Curl_conn_keep_alive(struct Curl_easy *data, } size_t Curl_conn_get_max_concurrent(struct Curl_easy *data, - struct connectdata *conn, - int sockindex) + struct connectdata *conn, + int sockindex) { CURLcode result; int n = 0; @@ -912,17 +1002,14 @@ int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd) } CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex, - char *buf, size_t blen, ssize_t *n) + char *buf, size_t blen, size_t *pnread) { - CURLcode result = CURLE_OK; - ssize_t nread; - + DEBUGASSERT(data); DEBUGASSERT(data->conn); - nread = data->conn->recv[sockindex](data, sockindex, buf, blen, &result); - DEBUGASSERT(nread >= 0 || result); - DEBUGASSERT(nread < 0 || !result); - *n = (nread >= 0) ? (size_t)nread : 0; - return result; + if(data && data->conn && data->conn->recv[sockindex]) + return data->conn->recv[sockindex](data, sockindex, buf, blen, pnread); + *pnread = 0; + return CURLE_FAILED_INIT; } CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, @@ -930,15 +1017,10 @@ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, size_t *pnwritten) { size_t write_len = blen; - ssize_t nwritten; - CURLcode result = CURLE_OK; - struct connectdata *conn; - DEBUGASSERT(sockindex >= 0 && sockindex < 2); - DEBUGASSERT(pnwritten); DEBUGASSERT(data); DEBUGASSERT(data->conn); - conn = data->conn; + DEBUGASSERT(sockindex >= 0 && sockindex < 2); #ifdef DEBUGBUILD if(write_len) { /* Allow debug builds to override this logic to force short sends @@ -953,11 +1035,11 @@ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex, #endif if(write_len != blen) eos = FALSE; - nwritten = conn->send[sockindex](data, sockindex, buf, write_len, eos, - &result); - DEBUGASSERT((nwritten >= 0) || result); - *pnwritten = (nwritten < 0) ? 0 : (size_t)nwritten; - return result; + if(data && data->conn && data->conn->send[sockindex]) + return data->conn->send[sockindex](data, sockindex, buf, write_len, eos, + pnwritten); + *pnwritten = 0; + return CURLE_FAILED_INIT; } void Curl_pollset_reset(struct Curl_easy *data, @@ -974,8 +1056,8 @@ void Curl_pollset_reset(struct Curl_easy *data, * */ void Curl_pollset_change(struct Curl_easy *data, - struct easy_pollset *ps, curl_socket_t sock, - int add_flags, int remove_flags) + struct easy_pollset *ps, curl_socket_t sock, + int add_flags, int remove_flags) { unsigned int i; @@ -1085,3 +1167,16 @@ void Curl_pollset_check(struct Curl_easy *data, } *pwant_read = *pwant_write = FALSE; } + +bool Curl_pollset_want_read(struct Curl_easy *data, + struct easy_pollset *ps, + curl_socket_t sock) +{ + unsigned int i; + (void)data; + for(i = 0; i < ps->num; ++i) { + if((ps->sockets[i] == sock) && (ps->actions[i] & CURL_POLL_IN)) + return TRUE; + } + return FALSE; +} diff --git a/lib/cfilters.h b/lib/cfilters.h index 4c604db38f..39d906d673 100644 --- a/lib/cfilters.h +++ b/lib/cfilters.h @@ -26,11 +26,13 @@ #include "curlx/timediff.h" +struct bufq; struct Curl_cfilter; struct Curl_easy; struct Curl_dns_entry; struct connectdata; struct ip_quadruple; +struct curl_tlssessioninfo; /* Callback to destroy resources held by this filter instance. * Implementations MUST NOT chain calls to cf->next. @@ -53,23 +55,6 @@ typedef CURLcode Curl_cft_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done); -/* Return the hostname and port the connection goes to. - * This may change with the connection state of filters when tunneling - * is involved. - * @param cf the filter to ask - * @param data the easy handle currently active - * @param phost on return, points to the relevant, real hostname. - * this is owned by the connection. - * @param pdisplay_host on return, points to the printable hostname. - * this is owned by the connection. - * @param pport on return, contains the port number - */ -typedef void Curl_cft_get_host(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char **phost, - const char **pdisplay_host, - int *pport); - struct easy_pollset; /* Passing in an easy_pollset for monitoring of sockets, let @@ -102,18 +87,18 @@ typedef void Curl_cft_adjust_pollset(struct Curl_cfilter *cf, typedef bool Curl_cft_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data); -typedef ssize_t Curl_cft_send(struct Curl_cfilter *cf, +typedef CURLcode Curl_cft_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* transfer */ const void *buf, /* data to write */ size_t len, /* amount to write */ bool eos, /* last chunk */ - CURLcode *err); /* error to return */ + size_t *pnwritten); /* how much sent */ -typedef ssize_t Curl_cft_recv(struct Curl_cfilter *cf, +typedef CURLcode Curl_cft_recv(struct Curl_cfilter *cf, struct Curl_easy *data, /* transfer */ char *buf, /* store data here */ size_t len, /* amount to read */ - CURLcode *err); /* error to return */ + size_t *pnread); /* how much received */ typedef bool Curl_cft_conn_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -166,6 +151,13 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf, * - CF_QUERY_NEED_FLUSH: TRUE iff any of the filters have unsent data * - CF_QUERY_IP_INFO: res1 says if connection used IPv6, res2 is the * ip quadruple + * - CF_QUERY_HOST_PORT: the remote hostname and port a filter talks to + * - CF_QUERY_SSL_INFO: fill out the passed curl_tlssessioninfo with the + * internal from the SSL secured connection when + * available. + * - CF_QUERY_SSL_CTX_INFO: same as CF_QUERY_SSL_INFO, but give the SSL_CTX + * when available, or the same internal pointer + * when the TLS stack does not differentiate. */ /* query res1 res2 */ #define CF_QUERY_MAX_CONCURRENT 1 /* number - */ @@ -177,6 +169,13 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf, #define CF_QUERY_NEED_FLUSH 7 /* TRUE/FALSE - */ #define CF_QUERY_IP_INFO 8 /* TRUE/FALSE struct ip_quadruple */ #define CF_QUERY_HTTP_VERSION 9 /* number (10/11/20/30) - */ +/* pass in a `const struct Curl_sockaddr_ex **` as `pres2`. Gets set + * to NULL when not connected. */ +#define CF_QUERY_REMOTE_ADDR 10 /* - `Curl_sockaddr_ex *` */ +#define CF_QUERY_HOST_PORT 11 /* port const char * */ +#define CF_QUERY_SSL_INFO 12 /* - struct curl_tlssessioninfo * */ +#define CF_QUERY_SSL_CTX_INFO 13 /* - struct curl_tlssessioninfo * */ +#define CF_QUERY_TRANSPORT 14 /* TRNSPRT_* - * */ /** * Query the cfilter for properties. Filters ignorant of a query will @@ -213,7 +212,6 @@ struct Curl_cftype { Curl_cft_connect *do_connect; /* establish connection */ Curl_cft_close *do_close; /* close conn */ Curl_cft_shutdown *do_shutdown; /* shutdown conn */ - Curl_cft_get_host *get_host; /* host filter talks to */ Curl_cft_adjust_pollset *adjust_pollset; /* adjust transfer poll set */ Curl_cft_data_pending *has_data_pending;/* conn has data pending */ Curl_cft_send *do_send; /* send data */ @@ -241,19 +239,16 @@ void Curl_cf_def_destroy_this(struct Curl_cfilter *cf, /* Default implementations for the type functions, implementing pass-through * the filter chain. */ -void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data, - const char **phost, const char **pdisplay_host, - int *pport); void Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps); bool Curl_cf_def_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data); -ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, +CURLcode Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, bool eos, - CURLcode *err); -ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err); + size_t *pnwritten); +CURLcode Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, size_t *pnread); CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf, struct Curl_easy *data, int event, int arg1, void *arg2); @@ -326,11 +321,11 @@ 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); -ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, - CURLcode *err); -ssize_t Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err); +CURLcode Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, bool eos, + size_t *pnwritten); +CURLcode Curl_conn_cf_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, size_t *pnread); CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf, struct Curl_easy *data, bool ignore_result, @@ -356,6 +351,9 @@ CURLcode Curl_conn_cf_get_ip_info(struct Curl_cfilter *cf, bool Curl_conn_cf_needs_flush(struct Curl_cfilter *cf, struct Curl_easy *data); +unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf, + struct Curl_easy *data); + #define CURL_CF_SSL_DEFAULT -1 #define CURL_CF_SSL_DISABLE 0 #define CURL_CF_SSL_ENABLE 1 @@ -395,6 +393,15 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex); */ bool Curl_conn_is_ssl(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 FALSE. + */ +bool Curl_conn_get_ssl_info(struct Curl_easy *data, + struct connectdata *conn, int sockindex, + struct curl_tlssessioninfo *info); + /** * Connection provides multiplexing of easy handles at `socketindex`. */ @@ -407,6 +414,10 @@ bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex); unsigned char Curl_conn_http_version(struct Curl_easy *data, struct connectdata *conn); +/* Get the TRNSPRT_* the connection is using */ +unsigned char Curl_conn_get_transport(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. @@ -444,13 +455,17 @@ CURLcode Curl_conn_flush(struct Curl_easy *data, int sockindex); */ curl_socket_t Curl_conn_get_socket(struct Curl_easy *data, int sockindex); +/* Return a pointer to the connected socket address or NULL. */ +const struct Curl_sockaddr_ex * +Curl_conn_get_remote_addr(struct Curl_easy *data, int sockindex); + /** * Tell filters to forget about the socket at sockindex. */ void Curl_conn_forget_socket(struct Curl_easy *data, int sockindex); /** - * Adjust the pollset for the filter chain startgin at `cf`. + * Adjust the pollset for the filter chain starting at `cf`. */ void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -475,20 +490,41 @@ int Curl_conn_cf_poll(struct Curl_cfilter *cf, /** * Receive data through the filter chain at `sockindex` for connection * `data->conn`. Copy at most `len` bytes into `buf`. Return the - * actual number of bytes copied or a negative value on error. - * The error code is placed into `*code`. + * actual number of bytes copied in `*pnread`or an error. */ -ssize_t Curl_cf_recv(struct Curl_easy *data, int sockindex, char *buf, - size_t len, CURLcode *code); +CURLcode Curl_cf_recv(struct Curl_easy *data, int sockindex, char *buf, + size_t len, size_t *pnread); /** * Send `len` bytes of data from `buf` through the filter chain `sockindex` * at connection `data->conn`. Return the actual number of bytes written - * or a negative value on error. - * The error code is placed into `*code`. + * in `*pnwritten` or on error. */ -ssize_t Curl_cf_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t len, bool eos, CURLcode *code); +CURLcode Curl_cf_send(struct Curl_easy *data, int sockindex, + const void *buf, size_t len, bool eos, + size_t *pnwritten); + +/** + * Receive bytes from connection filter `cf` into `bufq`. + * Convenience wrappter around `Curl_bufq_sipn()`, + * so users do not have to implement a callback. + */ +CURLcode Curl_cf_recv_bufq(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct bufq *bufq, + size_t maxlen, + size_t *pnread); + +/** + * Send bytes in `bufq` using connection filter `cf`. + * A convenience wrapper around `Curl_bufq_write_pass()`, + * so users do not have to implement a callback. + */ +CURLcode Curl_cf_send_bufq(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct bufq *bufq, + const unsigned char *buf, size_t blen, + size_t *pnwritten); /** * Notify connection filters that they need to setup data for @@ -535,9 +571,17 @@ CURLcode Curl_conn_keep_alive(struct Curl_easy *data, #ifdef UNITTESTS void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data); #endif -void Curl_conn_get_host(struct Curl_easy *data, int sockindex, - const char **phost, const char **pdisplay_host, - int *pport); + +/** + * 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`. + * During connect, when tunneling proxies are involved (http or socks), + * it will be the name and port the proxy currently negotiates with. + */ +void Curl_conn_get_current_host(struct Curl_easy *data, int sockindex, + const char **phost, int *pport); /** * Get the maximum number of parallel transfers the connection @@ -567,7 +611,7 @@ int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd); */ CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex, char *buf, size_t buffersize, - ssize_t *pnread); + size_t *pnread); /* * Send data on the connection, using FIRSTSOCKET/SECONDARYSOCKET. @@ -621,6 +665,13 @@ void Curl_pollset_check(struct Curl_easy *data, struct easy_pollset *ps, curl_socket_t sock, bool *pwant_read, bool *pwant_write); +/** + * Return TRUE if the pollset contains socket with CURL_POLL_IN. + */ +bool Curl_pollset_want_read(struct Curl_easy *data, + struct easy_pollset *ps, + curl_socket_t sock); + /** * Types and macros used to keep the current easy handle in filter calls, * allowing for nested invocations. See #10336. diff --git a/lib/conncache.c b/lib/conncache.c index f5e2ea8725..fd6776e34c 100644 --- a/lib/conncache.c +++ b/lib/conncache.c @@ -42,7 +42,6 @@ #include "sigpipe.h" #include "connect.h" #include "select.h" -#include "strcase.h" #include "curlx/strparse.h" #include "uint-table.h" diff --git a/lib/conncache.h b/lib/conncache.h index 1314b65c60..ac07111a7d 100644 --- a/lib/conncache.h +++ b/lib/conncache.h @@ -106,7 +106,7 @@ typedef bool Curl_cpool_done_match_cb(bool result, void *userdata); * Find a connection in the pool matching `destination`. * All callbacks are invoked while the pool's lock is held. * @param data current transfer - * @param destination match agaonst `conn->destination` in pool + * @param destination match against `conn->destination` in pool * @param conn_cb must be present, called for each connection in the * bundle until it returns TRUE * @return combined result of last conn_db and result_cb or FALSE if no diff --git a/lib/connect.c b/lib/connect.c index 1dcdde3fc5..ac16c3399b 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -66,7 +66,7 @@ #include "url.h" /* for Curl_safefree() */ #include "multiif.h" #include "sockaddr.h" /* required for Curl_sockaddr_storage */ -#include "inet_ntop.h" +#include "curlx/inet_ntop.h" #include "curlx/inet_pton.h" #include "vtls/vtls.h" /* for vtsl cfilters */ #include "progress.h" @@ -78,7 +78,6 @@ #include "vquic/vquic.h" /* for quic cfilters */ #include "http_proxy.h" #include "socks.h" -#include "strcase.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -90,15 +89,15 @@ enum alpnid Curl_alpn2alpnid(const char *name, size_t len) { if(len == 2) { - if(strncasecompare(name, "h1", 2)) + if(curl_strnequal(name, "h1", 2)) return ALPN_h1; - if(strncasecompare(name, "h2", 2)) + if(curl_strnequal(name, "h2", 2)) return ALPN_h2; - if(strncasecompare(name, "h3", 2)) + if(curl_strnequal(name, "h3", 2)) return ALPN_h3; } else if(len == 8) { - if(strncasecompare(name, "http/1.1", 8)) + if(curl_strnequal(name, "http/1.1", 8)) return ALPN_h1; } return ALPN_none; /* unknown, probably rubbish input */ @@ -172,11 +171,11 @@ void Curl_shutdown_start(struct Curl_easy *data, int sockindex, } data->conn->shutdown.start[sockindex] = *nowp; data->conn->shutdown.timeout_ms = (timeout_ms > 0) ? - (unsigned int)timeout_ms : + (timediff_t)timeout_ms : ((data->set.shutdowntimeout > 0) ? data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS); /* Set a timer, unless we operate on the admin handle */ - if(data->mid && data->conn->shutdown.timeout_ms) + if(data->mid && (data->conn->shutdown.timeout_ms > 0)) Curl_expire_ex(data, nowp, data->conn->shutdown.timeout_ms, EXPIRE_SHUTDOWN); } @@ -187,7 +186,8 @@ timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex, struct curltime now; timediff_t left_ms; - if(!conn->shutdown.start[sockindex].tv_sec || !conn->shutdown.timeout_ms) + if(!conn->shutdown.start[sockindex].tv_sec || + (conn->shutdown.timeout_ms <= 0)) return 0; /* not started or no limits */ if(!nowp) { @@ -254,8 +254,8 @@ addr_next_match(const struct Curl_addrinfo *addr, int family) return NULL; } -/* retrieves ip address and port from a sockaddr structure. - note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */ +/* 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, int *port) { @@ -272,7 +272,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, switch(sa->sa_family) { case AF_INET: si = (struct sockaddr_in *)(void *) sa; - if(Curl_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) { + if(curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) { unsigned short us_port = ntohs(si->sin_port); *port = us_port; return TRUE; @@ -281,7 +281,8 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen, #ifdef USE_IPV6 case AF_INET6: si6 = (struct sockaddr_in6 *)(void *) sa; - if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr, addr, MAX_IPADR_LEN)) { + if(curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr, + MAX_IPADR_LEN)) { unsigned short us_port = ntohs(si6->sin6_port); *port = us_port; return TRUE; @@ -389,7 +390,6 @@ struct eyeballer { expire_id timeout_id; /* ID for Curl_expire() */ CURLcode result; int error; - BIT(rewinded); /* if we rewinded the addr list */ BIT(has_started); /* attempts have started */ BIT(is_done); /* out of addresses/time */ BIT(connected); /* cf has connected */ @@ -455,7 +455,7 @@ static CURLcode eyeballer_new(struct eyeballer **pballer, } static void baller_close(struct eyeballer *baller, - struct Curl_easy *data) + struct Curl_easy *data) { if(baller && baller->cf) { Curl_conn_cf_discard_chain(&baller->cf, data); @@ -463,7 +463,7 @@ static void baller_close(struct eyeballer *baller, } static void baller_free(struct eyeballer *baller, - struct Curl_easy *data) + struct Curl_easy *data) { if(baller) { baller_close(baller, data); @@ -473,7 +473,6 @@ static void baller_free(struct eyeballer *baller, static void baller_rewind(struct eyeballer *baller) { - baller->rewinded = TRUE; baller->addr = baller->first; baller->inconclusive = FALSE; } @@ -682,7 +681,7 @@ evaluate: /* next attempt was started */ CURL_TRC_CF(data, cf, "%s trying next", baller->name); ++ongoing; - Curl_expire(data, 0, EXPIRE_RUN_NOW); + Curl_multi_mark_dirty(data); } } } @@ -832,7 +831,7 @@ static CURLcode start_connect(struct Curl_cfilter *cf, addr1 = addr_first_match(dns->addr, ai_family1); /* no ip address families, probably AF_UNIX or something, use the * address family given to us */ - if(!addr1 && !addr0 && dns->addr) { + if(!addr1 && !addr0 && dns->addr) { ai_family0 = dns->addr->ai_family; addr0 = addr_first_match(dns->addr, ai_family0); } @@ -932,8 +931,8 @@ static CURLcode cf_he_shutdown(struct Curl_cfilter *cf, } static void cf_he_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_he_ctx *ctx = cf->ctx; size_t i; @@ -993,11 +992,11 @@ static CURLcode cf_he_connect(struct Curl_cfilter *cf, struct ip_quadruple ipquad; int is_ipv6; if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) { - const char *host, *disphost; + const char *host; int port; - cf->next->cft->get_host(cf->next, data, &host, &disphost, &port); + Curl_conn_get_current_host(data, cf->sockindex, &host, &port); CURL_TRC_CF(data, cf, "Connected to %s (%s) port %u", - disphost, ipquad.remote_ip, ipquad.remote_port); + host, ipquad.remote_ip, ipquad.remote_port); } } data->info.numconnects++; /* to track the # of connections made */ @@ -1046,8 +1045,8 @@ static bool cf_he_data_pending(struct Curl_cfilter *cf, } static struct curltime get_max_baller_time(struct Curl_cfilter *cf, - struct Curl_easy *data, - int query) + struct Curl_easy *data, + int query) { struct cf_he_ctx *ctx = cf->ctx; struct curltime t, tmax; @@ -1134,7 +1133,6 @@ struct Curl_cftype Curl_cft_happy_eyeballs = { cf_he_connect, cf_he_close, cf_he_shutdown, - Curl_cf_def_get_host, cf_he_adjust_pollset, cf_he_data_pending, Curl_cf_def_send, @@ -1398,7 +1396,6 @@ struct Curl_cftype Curl_cft_setup = { cf_setup_connect, cf_setup_close, Curl_cf_def_shutdown, - Curl_cf_def_get_host, Curl_cf_def_adjust_pollset, Curl_cf_def_data_pending, Curl_cf_def_send, @@ -1518,7 +1515,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, ssl_mode); + result = cf_setup_add(data, conn, sockindex, + conn->transport_wanted, ssl_mode); if(result) goto out; } diff --git a/lib/content_encoding.c b/lib/content_encoding.c index f2e77eede1..9ea4a047c8 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -52,7 +52,6 @@ #include "http.h" #include "content_encoding.h" #include "strdup.h" -#include "strcase.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -343,7 +342,7 @@ static CURLcode gzip_do_write(struct Curl_easy *data, } static void gzip_do_close(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct zlib_writer *zp = (struct zlib_writer *) writer; z_stream *z = &zp->z; /* zlib state structure */ @@ -464,7 +463,7 @@ static CURLcode brotli_do_write(struct Curl_easy *data, } static void brotli_do_close(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct brotli_writer *bp = (struct brotli_writer *) writer; (void) data; @@ -567,7 +566,7 @@ static CURLcode zstd_do_write(struct Curl_easy *data, } static void zstd_do_close(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { struct zstd_writer *zp = (struct zstd_writer *) writer; (void)data; @@ -636,7 +635,7 @@ void Curl_all_content_encodings(char *buf, size_t blen) for(cep = general_unencoders; *cep; cep++) { ce = *cep; - if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) + if(!curl_strequal(ce->name, CONTENT_ENCODING_DEFAULT)) len += strlen(ce->name) + 2; } @@ -648,7 +647,7 @@ void Curl_all_content_encodings(char *buf, size_t blen) char *p = buf; for(cep = general_unencoders; *cep; cep++) { ce = *cep; - if(!strcasecompare(ce->name, CONTENT_ENCODING_DEFAULT)) { + if(!curl_strequal(ce->name, CONTENT_ENCODING_DEFAULT)) { strcpy(p, ce->name); p += strlen(p); *p++ = ','; @@ -688,7 +687,7 @@ static CURLcode error_do_write(struct Curl_easy *data, } static void error_do_close(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { (void) data; (void) writer; @@ -713,8 +712,8 @@ static const struct Curl_cwtype *find_unencode_writer(const char *name, if(phase == CURL_CW_TRANSFER_DECODE) { for(cep = transfer_unencoders; *cep; cep++) { const struct Curl_cwtype *ce = *cep; - if((strncasecompare(name, ce->name, len) && !ce->name[len]) || - (ce->alias && strncasecompare(name, ce->alias, len) + if((curl_strnequal(name, ce->name, len) && !ce->name[len]) || + (ce->alias && curl_strnequal(name, ce->alias, len) && !ce->alias[len])) return ce; } @@ -722,8 +721,8 @@ static const struct Curl_cwtype *find_unencode_writer(const char *name, /* look among the general decoders */ for(cep = general_unencoders; *cep; cep++) { const struct Curl_cwtype *ce = *cep; - if((strncasecompare(name, ce->name, len) && !ce->name[len]) || - (ce->alias && strncasecompare(name, ce->alias, len) && !ce->alias[len])) + if((curl_strnequal(name, ce->name, len) && !ce->name[len]) || + (ce->alias && curl_strnequal(name, ce->alias, len) && !ce->alias[len])) return ce; } return NULL; @@ -761,12 +760,12 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data, CURL_TRC_WRITE(data, "looking for %s decoder: %.*s", is_transfer ? "transfer" : "content", (int)namelen, name); is_chunked = (is_transfer && (namelen == 7) && - strncasecompare(name, "chunked", 7)); + curl_strnequal(name, "chunked", 7)); /* if we skip the decoding in this phase, do not look further. * Exception is "chunked" transfer-encoding which always must happen */ if((is_transfer && !data->set.http_transfer_encoding && !is_chunked) || (!is_transfer && data->set.http_ce_skip)) { - bool is_identity = strncasecompare(name, "identity", 8); + bool is_identity = curl_strnequal(name, "identity", 8); /* not requested, ignore */ CURL_TRC_WRITE(data, "decoder not requested, ignored: %.*s", (int)namelen, name); diff --git a/lib/cookie.c b/lib/cookie.c index 1a8426ca27..9221871e4b 100644 --- a/lib/cookie.c +++ b/lib/cookie.c @@ -135,9 +135,9 @@ static bool cookie_tailmatch(const char *cookie_domain, if(hostname_len < cookie_domain_len) return FALSE; - if(!strncasecompare(cookie_domain, - hostname + hostname_len-cookie_domain_len, - cookie_domain_len)) + if(!curl_strnequal(cookie_domain, + hostname + hostname_len-cookie_domain_len, + cookie_domain_len)) return FALSE; /* @@ -409,7 +409,7 @@ static void remove_expired(struct CookieInfo *ci) /* Make sure domain contains a dot or is localhost. */ static bool bad_domain(const char *domain, size_t len) { - if((len == 9) && strncasecompare(domain, "localhost", 9)) + if((len == 9) && curl_strnequal(domain, "localhost", 9)) return FALSE; else { /* there must be a dot present, but that dot must not be a trailing dot */ @@ -815,7 +815,7 @@ parse_netscape(struct Cookie *co, * domain can access the variable. Set TRUE when the cookie says * .example.com and to false when the domain is complete www.example.com */ - co->tailmatch = !!strncasecompare(ptr, "TRUE", len); + co->tailmatch = !!curl_strnequal(ptr, "TRUE", len); break; case 2: /* The file format allows the path field to remain not filled in */ @@ -842,7 +842,7 @@ parse_netscape(struct Cookie *co, FALLTHROUGH(); case 3: co->secure = FALSE; - if(strncasecompare(ptr, "TRUE", len)) { + if(curl_strnequal(ptr, "TRUE", len)) { if(secure || ci->running) co->secure = TRUE; else @@ -859,9 +859,9 @@ parse_netscape(struct Cookie *co, return CERR_OUT_OF_MEMORY; else { /* For Netscape file format cookies we check prefix on the name */ - if(strncasecompare("__Secure-", co->name, 9)) + if(curl_strnequal("__Secure-", co->name, 9)) co->prefix_secure = TRUE; - else if(strncasecompare("__Host-", co->name, 7)) + else if(curl_strnequal("__Host-", co->name, 7)) co->prefix_host = TRUE; } break; @@ -917,7 +917,7 @@ is_public_suffix(struct Curl_easy *data, Curl_psl_release(data); } else - infof(data, "libpsl problem, rejecting cookie for satety"); + infof(data, "libpsl problem, rejecting cookie for safety"); } if(!acceptable) { @@ -954,7 +954,7 @@ replace_existing(struct Curl_easy *data, bool matching_domains = FALSE; if(clist->domain && co->domain) { - if(strcasecompare(clist->domain, co->domain)) + if(curl_strequal(clist->domain, co->domain)) /* The domains are identical */ matching_domains = TRUE; } @@ -981,7 +981,7 @@ replace_existing(struct Curl_easy *data, else cllen = strlen(clist->spath); - if(strncasecompare(clist->spath, co->spath, cllen)) { + if(curl_strnequal(clist->spath, co->spath, cllen)) { infof(data, "cookie '%s' for domain '%s' dropped, would " "overlay an existing cookie", co->name, co->domain); return CERR_BAD_SECURE; @@ -993,7 +993,7 @@ replace_existing(struct Curl_easy *data, /* the names are identical */ if(clist->domain && co->domain) { - if(strcasecompare(clist->domain, co->domain) && + if(curl_strequal(clist->domain, co->domain) && (clist->tailmatch == co->tailmatch)) /* The domains are identical */ replace_old = TRUE; @@ -1005,7 +1005,7 @@ replace_existing(struct Curl_easy *data, /* the domains were identical */ if(clist->spath && co->spath && - !strcasecompare(clist->spath, co->spath)) + !curl_strequal(clist->spath, co->spath)) replace_old = FALSE; else if(!clist->spath != !co->spath) replace_old = FALSE; @@ -1337,7 +1337,7 @@ int Curl_cookie_getlist(struct Curl_easy *data, if(!co->domain || (co->tailmatch && !is_ip && cookie_tailmatch(co->domain, strlen(co->domain), host)) || - ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) { + ((!co->tailmatch || is_ip) && curl_strequal(host, co->domain)) ) { /* * the right part of the host matches the domain stuff in the * cookie data diff --git a/lib/cshutdn.c b/lib/cshutdn.c index f05b87d277..c8b40dc321 100644 --- a/lib/cshutdn.c +++ b/lib/cshutdn.c @@ -40,7 +40,6 @@ #include "sigpipe.h" #include "connect.h" #include "select.h" -#include "strcase.h" #include "curlx/strparse.h" /* The last 3 #include files should be in this order */ @@ -54,12 +53,6 @@ static void cshutdn_run_conn_handler(struct Curl_easy *data, { if(!conn->bits.shutdown_handler) { - /* Cleanup NTLM connection-related data */ - Curl_http_auth_cleanup_ntlm(conn); - - /* Cleanup NEGOTIATE connection-related data */ - Curl_http_auth_cleanup_negotiate(conn); - if(conn->handler && conn->handler->disconnect) { /* Some disconnect handlers do a blocking wait on server responses. * FTP/IMAP/SMTP and SFTP are among them. When using the internal @@ -121,8 +114,8 @@ static void cshutdn_run_once(struct Curl_easy *data, } void Curl_cshutdn_run_once(struct Curl_easy *data, - struct connectdata *conn, - bool *done) + struct connectdata *conn, + bool *done) { DEBUGASSERT(!data->conn); Curl_attach_connection(data, conn); @@ -219,8 +212,8 @@ bool Curl_cshutdn_close_oldest(struct Curl_easy *data, #define NUM_POLLS_ON_STACK 10 static CURLcode cshutdn_wait(struct cshutdn *cshutdn, - struct Curl_easy *data, - int timeout_ms) + struct Curl_easy *data, + int timeout_ms) { struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; struct curl_pollfds cpfds; @@ -241,7 +234,7 @@ out: static void cshutdn_perform(struct cshutdn *cshutdn, - struct Curl_easy *data) + struct Curl_easy *data) { struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list); struct Curl_llist_node *enext; @@ -402,8 +395,8 @@ size_t Curl_cshutdn_dest_count(struct Curl_easy *data, static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn, - struct Curl_easy *data, - struct connectdata *conn) + struct Curl_easy *data, + struct connectdata *conn) { CURLMcode mresult; @@ -418,8 +411,8 @@ static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn, void Curl_cshutdn_add(struct cshutdn *cshutdn, - struct connectdata *conn, - size_t conns_in_pool) + struct connectdata *conn, + size_t conns_in_pool) { struct Curl_easy *data = cshutdn->multi->admin; size_t max_total = (cshutdn->multi->max_total_connections > 0) ? @@ -451,8 +444,8 @@ void Curl_cshutdn_add(struct cshutdn *cshutdn, static void cshutdn_multi_socket(struct cshutdn *cshutdn, - struct Curl_easy *data, - curl_socket_t s) + struct Curl_easy *data, + curl_socket_t s) { struct Curl_llist_node *e; struct connectdata *conn; diff --git a/lib/curl_config.h.cmake b/lib/curl_config.h.cmake index efaa8a4594..0838ddccfb 100644 --- a/lib/curl_config.h.cmake +++ b/lib/curl_config.h.cmake @@ -586,18 +586,9 @@ /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_SELECT_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_SYS_SOCKET_H 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_SOCKIO_H 1 -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the header file. */ -#cmakedefine HAVE_SYS_TIME_H 1 - /* Define to 1 if you have the header file. */ #cmakedefine HAVE_SYS_TYPES_H 1 @@ -685,18 +676,12 @@ ${SIZEOF_TIME_T_CODE} /* if GnuTLS is enabled */ #cmakedefine USE_GNUTLS 1 -/* if Secure Transport is enabled */ -#cmakedefine USE_SECTRANSP 1 - /* if SSL session export support is available */ #cmakedefine USE_SSLS_EXPORT 1 /* if mbedTLS is enabled */ #cmakedefine USE_MBEDTLS 1 -/* if BearSSL is enabled */ -#cmakedefine USE_BEARSSL 1 - /* if Rustls is enabled */ #cmakedefine USE_RUSTLS 1 @@ -800,18 +785,6 @@ ${SIZEOF_TIME_T_CODE} /* Number of bits in a file offset, on hosts where this is settable. */ #cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS} -/* Define for large files, on AIX-style hosts. */ -#cmakedefine _LARGE_FILES ${_LARGE_FILES} - -/* define this if you need it to compile thread-safe code */ -#cmakedefine _THREAD_SAFE ${_THREAD_SAFE} - -/* Define to empty if `const' does not conform to ANSI C. */ -#cmakedefine const ${const} - -/* Define to `unsigned int' if does not define. */ -#cmakedefine size_t ${size_t} - /* the signed version of size_t */ #cmakedefine ssize_t ${ssize_t} diff --git a/lib/curl_des.c b/lib/curl_des.c index 15836f58b9..a202dd3fe4 100644 --- a/lib/curl_des.c +++ b/lib/curl_des.c @@ -26,7 +26,6 @@ #if defined(USE_CURL_NTLM_CORE) && \ (defined(USE_GNUTLS) || \ - defined(USE_SECTRANSP) || \ defined(USE_OS400CRYPTO) || \ defined(USE_WIN32_CRYPTO)) diff --git a/lib/curl_des.h b/lib/curl_des.h index 2dd498da24..c50aaf45b1 100644 --- a/lib/curl_des.h +++ b/lib/curl_des.h @@ -28,7 +28,6 @@ #if defined(USE_CURL_NTLM_CORE) && \ (defined(USE_GNUTLS) || \ - defined(USE_SECTRANSP) || \ defined(USE_OS400CRYPTO) || \ defined(USE_WIN32_CRYPTO)) diff --git a/lib/curl_get_line.c b/lib/curl_get_line.c index 2bb57492b3..4b1c6c3e09 100644 --- a/lib/curl_get_line.c +++ b/lib/curl_get_line.c @@ -32,6 +32,15 @@ /* The last #include file should be: */ #include "memdebug.h" +static int appendnl(struct dynbuf *buf) +{ + CURLcode result = curlx_dyn_addn(buf, "\n", 1); + if(result) + /* too long line or out of memory */ + return 0; /* error */ + return 1; /* all good */ +} + /* * Curl_get_line() makes sure to only return complete whole lines that end * newlines. @@ -43,9 +52,10 @@ int Curl_get_line(struct dynbuf *buf, FILE *input) curlx_dyn_reset(buf); while(1) { char *b = fgets(buffer, sizeof(buffer), input); + size_t rlen; if(b) { - size_t rlen = strlen(b); + rlen = strlen(b); if(!rlen) break; @@ -59,19 +69,24 @@ int Curl_get_line(struct dynbuf *buf, FILE *input) /* end of the line */ return 1; /* all good */ - else if(feof(input)) { + else if(feof(input)) /* append a newline */ - result = curlx_dyn_addn(buf, "\n", 1); - if(result) - /* too long line or out of memory */ - return 0; /* error */ + return appendnl(buf); + } + else { + rlen = curlx_dyn_len(buf); + if(rlen) { + b = curlx_dyn_ptr(buf); + + if(b[rlen-1] != '\n') + /* append a newline */ + return appendnl(buf); + return 1; /* all good */ } + else + break; } - else if(curlx_dyn_len(buf)) - return 1; /* all good */ - else - break; } return 0; } diff --git a/lib/curl_gssapi.c b/lib/curl_gssapi.c index f83701ad64..4690218050 100644 --- a/lib/curl_gssapi.c +++ b/lib/curl_gssapi.c @@ -52,17 +52,260 @@ gss_OID_desc Curl_krb5_mech_oid CURL_ALIGN8 = { 9, CURL_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02") }; -OM_uint32 Curl_gss_init_sec_context( - struct Curl_easy *data, - OM_uint32 *minor_status, - gss_ctx_id_t *context, - gss_name_t target_name, - gss_OID mech_type, - gss_channel_bindings_t input_chan_bindings, - gss_buffer_t input_token, - gss_buffer_t output_token, - const bool mutual_auth, - OM_uint32 *ret_flags) +#ifdef DEBUGBUILD +enum min_err_code { + STUB_GSS_OK = 0, + STUB_GSS_NO_MEMORY, + STUB_GSS_INVALID_ARGS, + STUB_GSS_INVALID_CREDS, + STUB_GSS_INVALID_CTX, + STUB_GSS_SERVER_ERR, + STUB_GSS_NO_MECH, + STUB_GSS_LAST +}; + +/* libcurl is also passing this struct to these functions, which are not yet + * stubbed: + * gss_inquire_context() + * gss_unwrap() + * gss_wrap() + */ +struct stub_gss_ctx_id_t_desc { + enum { STUB_GSS_NONE, STUB_GSS_KRB5, STUB_GSS_NTLM1, STUB_GSS_NTLM3 } sent; + int have_krb5; + int have_ntlm; + OM_uint32 flags; + char creds[250]; +}; + +static OM_uint32 +stub_gss_init_sec_context(OM_uint32 *min, + gss_cred_id_t initiator_cred_handle, + struct stub_gss_ctx_id_t_desc **context, + gss_name_t target_name, + const gss_OID mech_type, + OM_uint32 req_flags, + OM_uint32 time_req, + const gss_channel_bindings_t input_chan_bindings, + gss_buffer_desc *input_token, + gss_OID *actual_mech_type, + gss_buffer_desc *output_token, + OM_uint32 *ret_flags, + OM_uint32 *time_rec) +{ + struct stub_gss_ctx_id_t_desc *ctx = NULL; + + /* The token will be encoded in base64 */ + size_t length = sizeof(ctx->creds) * 3 / 4; + size_t used = 0; + char *token = NULL; + const char *creds = NULL; + + (void)initiator_cred_handle; + (void)mech_type; + (void)time_req; + (void)input_chan_bindings; + (void)actual_mech_type; + + if(!min) + return GSS_S_FAILURE; + + *min = 0; + + if(!context || !target_name || !output_token) { + *min = STUB_GSS_INVALID_ARGS; + return GSS_S_FAILURE; + } + + creds = getenv("CURL_STUB_GSS_CREDS"); + if(!creds || strlen(creds) >= sizeof(ctx->creds)) { + *min = STUB_GSS_INVALID_CREDS; + return GSS_S_FAILURE; + } + + ctx = *context; + if(ctx && strcmp(ctx->creds, creds)) { + *min = STUB_GSS_INVALID_CREDS; + return GSS_S_FAILURE; + } + + output_token->length = 0; + output_token->value = NULL; + + if(input_token && input_token->length) { + if(!ctx) { + *min = STUB_GSS_INVALID_CTX; + return GSS_S_FAILURE; + } + + /* Server response, either D (RA==) or C (Qw==) */ + if(((char *) input_token->value)[0] == 'D') { + /* Done */ + switch(ctx->sent) { + case STUB_GSS_KRB5: + case STUB_GSS_NTLM3: + if(ret_flags) + *ret_flags = ctx->flags; + if(time_rec) + *time_rec = GSS_C_INDEFINITE; + return GSS_S_COMPLETE; + default: + *min = STUB_GSS_SERVER_ERR; + return GSS_S_FAILURE; + } + } + + if(((char *) input_token->value)[0] != 'C') { + /* We only support Done or Continue */ + *min = STUB_GSS_SERVER_ERR; + return GSS_S_FAILURE; + } + + /* Continue */ + switch(ctx->sent) { + case STUB_GSS_KRB5: + /* We sent KRB5 and it failed, let's try NTLM */ + if(ctx->have_ntlm) { + ctx->sent = STUB_GSS_NTLM1; + break; + } + else { + *min = STUB_GSS_SERVER_ERR; + return GSS_S_FAILURE; + } + case STUB_GSS_NTLM1: + ctx->sent = STUB_GSS_NTLM3; + break; + default: + *min = STUB_GSS_SERVER_ERR; + return GSS_S_FAILURE; + } + } + else { + if(ctx) { + *min = STUB_GSS_INVALID_CTX; + return GSS_S_FAILURE; + } + + ctx = calloc(1, sizeof(*ctx)); + if(!ctx) { + *min = STUB_GSS_NO_MEMORY; + return GSS_S_FAILURE; + } + + if(strstr(creds, "KRB5")) + ctx->have_krb5 = 1; + + if(strstr(creds, "NTLM")) + ctx->have_ntlm = 1; + + if(ctx->have_krb5) + ctx->sent = STUB_GSS_KRB5; + else if(ctx->have_ntlm) + ctx->sent = STUB_GSS_NTLM1; + else { + free(ctx); + *min = STUB_GSS_NO_MECH; + return GSS_S_FAILURE; + } + + strcpy(ctx->creds, creds); + ctx->flags = req_flags; + } + + /* To avoid memdebug macro replacement, wrap the name in parentheses to call + the original version. It is freed via the GSS API gss_release_buffer(). */ + token = (malloc)(length); + if(!token) { + free(ctx); + *min = STUB_GSS_NO_MEMORY; + return GSS_S_FAILURE; + } + + { + gss_buffer_desc target_desc; + gss_OID name_type = GSS_C_NO_OID; + OM_uint32 minor_status; + OM_uint32 major_status; + major_status = gss_display_name(&minor_status, target_name, + &target_desc, &name_type); + if(GSS_ERROR(major_status)) { + (free)(token); + free(ctx); + *min = STUB_GSS_NO_MEMORY; + return GSS_S_FAILURE; + } + + if(strlen(creds) + target_desc.length + 5 >= sizeof(ctx->creds)) { + (free)(token); + free(ctx); + *min = STUB_GSS_NO_MEMORY; + return GSS_S_FAILURE; + } + + /* Token format: creds:target:type:padding */ + used = msnprintf(token, length, "%s:%.*s:%d:", creds, + (int)target_desc.length, (const char *)target_desc.value, + ctx->sent); + + gss_release_buffer(&minor_status, &target_desc); + } + + if(used >= length) { + (free)(token); + free(ctx); + *min = STUB_GSS_NO_MEMORY; + return GSS_S_FAILURE; + } + + /* Overwrite null-terminator */ + memset(token + used, 'A', length - used); + + *context = ctx; + + output_token->value = token; + output_token->length = length; + + return GSS_S_CONTINUE_NEEDED; +} + +static OM_uint32 +stub_gss_delete_sec_context(OM_uint32 *min, + struct stub_gss_ctx_id_t_desc **context, + gss_buffer_t output_token) +{ + (void)output_token; + + if(!min) + return GSS_S_FAILURE; + + if(!context) { + *min = STUB_GSS_INVALID_CTX; + return GSS_S_FAILURE; + } + if(!*context) { + *min = STUB_GSS_INVALID_CTX; + return GSS_S_FAILURE; + } + + free(*context); + *context = NULL; + *min = 0; + + return GSS_S_COMPLETE; +} +#endif /* DEBUGBUILD */ + +OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data, + OM_uint32 *minor_status, + gss_ctx_id_t *context, + gss_name_t target_name, + gss_OID mech_type, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_buffer_t output_token, + const bool mutual_auth, + OM_uint32 *ret_flags) { OM_uint32 req_flags = GSS_C_REPLAY_FLAG; @@ -74,13 +317,30 @@ OM_uint32 Curl_gss_init_sec_context( req_flags |= GSS_C_DELEG_POLICY_FLAG; #else infof(data, "WARNING: support for CURLGSSAPI_DELEGATION_POLICY_FLAG not " - "compiled in"); + "compiled in"); #endif } if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_FLAG) req_flags |= GSS_C_DELEG_FLAG; +#ifdef DEBUGBUILD + if(getenv("CURL_STUB_GSS_CREDS")) + return stub_gss_init_sec_context(minor_status, + GSS_C_NO_CREDENTIAL, /* cred_handle */ + (struct stub_gss_ctx_id_t_desc **)context, + target_name, + mech_type, + req_flags, + 0, /* time_req */ + input_chan_bindings, + input_token, + NULL, /* actual_mech_type */ + output_token, + ret_flags, + NULL /* time_rec */); +#endif /* DEBUGBUILD */ + return gss_init_sec_context(minor_status, GSS_C_NO_CREDENTIAL, /* cred_handle */ context, @@ -96,6 +356,20 @@ OM_uint32 Curl_gss_init_sec_context( NULL /* time_rec */); } +OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min, + gss_ctx_id_t *context, + gss_buffer_t output_token) +{ +#ifdef DEBUGBUILD + if(getenv("CURL_STUB_GSS_CREDS")) + return stub_gss_delete_sec_context(min, + (struct stub_gss_ctx_id_t_desc **)context, + output_token); +#endif /* DEBUGBUILD */ + + return gss_delete_sec_context(min, context, output_token); +} + #define GSS_LOG_BUFFER_LEN 1024 static size_t display_gss_error(OM_uint32 status, int type, char *buf, size_t len) { diff --git a/lib/curl_gssapi.h b/lib/curl_gssapi.h index 7b9a534ea2..2659f23460 100644 --- a/lib/curl_gssapi.h +++ b/lib/curl_gssapi.h @@ -32,17 +32,20 @@ extern gss_OID_desc Curl_spnego_mech_oid; extern gss_OID_desc Curl_krb5_mech_oid; /* Common method for using GSS-API */ -OM_uint32 Curl_gss_init_sec_context( - struct Curl_easy *data, - OM_uint32 *minor_status, - gss_ctx_id_t *context, - gss_name_t target_name, - gss_OID mech_type, - gss_channel_bindings_t input_chan_bindings, - gss_buffer_t input_token, - gss_buffer_t output_token, - const bool mutual_auth, - OM_uint32 *ret_flags); +OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data, + OM_uint32 *minor_status, + gss_ctx_id_t *context, + gss_name_t target_name, + gss_OID mech_type, + gss_channel_bindings_t input_chan_bindings, + gss_buffer_t input_token, + gss_buffer_t output_token, + const bool mutual_auth, + OM_uint32 *ret_flags); + +OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min, + gss_ctx_id_t *context_handle, + gss_buffer_t output_token); /* Helper to log a GSS-API error status */ void Curl_gss_log_error(struct Curl_easy *data, const char *prefix, diff --git a/lib/curl_memory.h b/lib/curl_memory.h index bc3e944fea..b4536c1b3f 100644 --- a/lib/curl_memory.h +++ b/lib/curl_memory.h @@ -57,7 +57,6 @@ #ifdef HEADER_CURL_MEMDEBUG_H /* cleanup after memdebug.h */ -#ifdef MEMDEBUG_NODEFINES #ifdef CURLDEBUG #undef strdup @@ -69,28 +68,28 @@ #undef recv #ifdef _WIN32 -# ifdef UNICODE -# undef wcsdup -# undef _wcsdup -# undef _tcsdup -# else -# undef _tcsdup -# endif +#undef _tcsdup #endif #undef socket #undef accept +#ifdef HAVE_ACCEPT4 +#undef accept4 +#endif #ifdef HAVE_SOCKETPAIR #undef socketpair #endif /* sclose is probably already defined, redefine it! */ #undef sclose +#define sclose(x) CURL_SCLOSE(x) #undef fopen +#ifdef CURL_FOPEN +#define fopen(fname, mode) CURL_FOPEN(fname, mode) +#endif #undef fdopen #undef fclose -#endif /* MEMDEBUG_NODEFINES */ #endif /* CURLDEBUG */ #undef HEADER_CURL_MEMDEBUG_H @@ -122,9 +121,6 @@ extern curl_free_callback Curl_cfree; extern curl_realloc_callback Curl_crealloc; extern curl_strdup_callback Curl_cstrdup; extern curl_calloc_callback Curl_ccalloc; -#if defined(_WIN32) && defined(UNICODE) -extern curl_wcsdup_callback Curl_cwcsdup; -#endif #ifndef CURLDEBUG @@ -149,18 +145,13 @@ extern curl_wcsdup_callback Curl_cwcsdup; #define free(ptr) Curl_cfree(ptr) #ifdef _WIN32 -# ifdef UNICODE -# undef wcsdup -# define wcsdup(ptr) Curl_cwcsdup(ptr) -# undef _wcsdup -# define _wcsdup(ptr) Curl_cwcsdup(ptr) -# undef _tcsdup -# define _tcsdup(ptr) Curl_cwcsdup(ptr) -# else -# undef _tcsdup -# define _tcsdup(ptr) Curl_cstrdup(ptr) -# endif +#undef _tcsdup +#ifdef UNICODE +#define _tcsdup(ptr) Curl_wcsdup(ptr) +#else +#define _tcsdup(ptr) Curl_cstrdup(ptr) #endif +#endif /* _WIN32 */ #endif /* CURLDEBUG */ #endif /* HEADER_CURL_MEMORY_H */ diff --git a/lib/curl_ntlm_core.c b/lib/curl_ntlm_core.c index d6cd44d963..c35953177c 100644 --- a/lib/curl_ntlm_core.c +++ b/lib/curl_ntlm_core.c @@ -40,9 +40,8 @@ 3. USE_GNUTLS 4. - 5. USE_MBEDTLS - 6. USE_SECTRANSP - 7. USE_OS400CRYPTO - 8. USE_WIN32_CRYPTO + 6. USE_OS400CRYPTO + 7. USE_WIN32_CRYPTO This ensures that: - the same SSL branch gets activated throughout this source @@ -107,11 +106,6 @@ # include -#elif defined(USE_SECTRANSP) - -# include -# include - #elif defined(USE_OS400CRYPTO) # include "cipher.mih" /* mih/cipher */ #elif defined(USE_WIN32_CRYPTO) @@ -209,29 +203,6 @@ static bool encrypt_des(const unsigned char *in, unsigned char *out, return mbedtls_des_crypt_ecb(&ctx, in, out) == 0; } -#elif defined(USE_SECTRANSP) - -static bool encrypt_des(const unsigned char *in, unsigned char *out, - const unsigned char *key_56) -{ - char key[8]; - size_t out_len; - CCCryptorStatus err; - - /* Expand the 56-bit key to 64 bits */ - extend_key_56_to_64(key_56, key); - - /* Set the key parity to odd */ - Curl_des_set_odd_parity((unsigned char *) key, sizeof(key)); - - /* Perform the encryption */ - err = CCCrypt(kCCEncrypt, kCCAlgorithmDES, kCCOptionECBMode, key, - kCCKeySizeDES, NULL, in, 8 /* inbuflen */, out, - 8 /* outbuflen */, &out_len); - - return err == kCCSuccess; -} - #elif defined(USE_OS400CRYPTO) static bool encrypt_des(const unsigned char *in, unsigned char *out, @@ -339,8 +310,8 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys, des_encrypt(&des, 8, results + 8, plaintext); setup_des_key(keys + 14, &des); des_encrypt(&des, 8, results + 16, plaintext); -#elif defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \ - || defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) +#elif defined(USE_MBEDTLS) || defined(USE_OS400CRYPTO) || \ + defined(USE_WIN32_CRYPTO) encrypt_des(plaintext, results, keys); encrypt_des(plaintext, results + 8, keys + 7); encrypt_des(plaintext, results + 16, keys + 14); @@ -387,8 +358,8 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password, des_encrypt(&des, 8, lmbuffer, magic); setup_des_key(pw + 7, &des); des_encrypt(&des, 8, lmbuffer + 8, magic); -#elif defined(USE_MBEDTLS) || defined(USE_SECTRANSP) \ - || defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) +#elif defined(USE_MBEDTLS) || defined(USE_OS400CRYPTO) || \ + defined(USE_WIN32_CRYPTO) encrypt_des(magic, lmbuffer, pw); encrypt_des(magic, lmbuffer + 8, pw + 7); #endif @@ -466,7 +437,7 @@ struct ms_filetime { static void time2filetime(struct ms_filetime *ft, time_t t) { #if SIZEOF_TIME_T > 4 - t = (t + CURL_OFF_T_C(11644473600)) * 10000000; + t = (t + (curl_off_t)11644473600) * 10000000; ft->dwLowDateTime = (unsigned int) (t & 0xFFFFFFFF); ft->dwHighDateTime = (unsigned int) (t >> 32); #else diff --git a/lib/curl_ntlm_core.h b/lib/curl_ntlm_core.h index e2e4b1bd43..7571ad5432 100644 --- a/lib/curl_ntlm_core.h +++ b/lib/curl_ntlm_core.h @@ -28,6 +28,10 @@ #if defined(USE_CURL_NTLM_CORE) +#include "vauth/vauth.h" + +struct ntlmdata; + /* Helpers to generate function byte arguments in little endian order */ #define SHORTPAIR(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)) #define LONGQUARTET(x) ((int)((x) & 0xff)), ((int)(((x) >> 8) & 0xff)), \ diff --git a/lib/curl_rtmp.c b/lib/curl_rtmp.c index 62632c1e9c..0be1f7c768 100644 --- a/lib/curl_rtmp.c +++ b/lib/curl_rtmp.c @@ -326,51 +326,54 @@ static CURLcode rtmp_disconnect(struct Curl_easy *data, return CURLE_OK; } -static ssize_t rtmp_recv(struct Curl_easy *data, int sockindex, char *buf, - size_t len, CURLcode *err) +static CURLcode rtmp_recv(struct Curl_easy *data, int sockindex, char *buf, + size_t len, size_t *pnread) { struct connectdata *conn = data->conn; RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN); + CURLcode result = CURLE_OK; ssize_t nread; (void)sockindex; /* unused */ - if(!r) { - *err = CURLE_FAILED_INIT; - return -1; - } + *pnread = 0; + if(!r) + return CURLE_FAILED_INIT; nread = RTMP_Read(r, buf, curlx_uztosi(len)); if(nread < 0) { if(r->m_read.status == RTMP_READ_COMPLETE || r->m_read.status == RTMP_READ_EOF) { data->req.size = data->req.bytecount; - nread = 0; } else - *err = CURLE_RECV_ERROR; + result = CURLE_RECV_ERROR; } - return nread; + else + *pnread = (size_t)nread; + + return result; } -static ssize_t rtmp_send(struct Curl_easy *data, int sockindex, - const void *buf, size_t len, bool eos, CURLcode *err) +static CURLcode rtmp_send(struct Curl_easy *data, int sockindex, + const void *buf, size_t len, bool eos, + size_t *pnwritten) { struct connectdata *conn = data->conn; RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN); - ssize_t num; + ssize_t nwritten; (void)sockindex; /* unused */ (void)eos; /* unused */ - if(!r) { - *err = CURLE_FAILED_INIT; - return -1; - } + *pnwritten = 0; + if(!r) + return CURLE_FAILED_INIT; - num = RTMP_Write(r, (const char *)buf, curlx_uztosi(len)); - if(num < 0) - *err = CURLE_SEND_ERROR; + nwritten = RTMP_Write(r, (const char *)buf, curlx_uztosi(len)); + if(nwritten < 0) + return CURLE_SEND_ERROR; - return num; + *pnwritten = (size_t)nwritten; + return CURLE_OK; } void Curl_rtmp_version(char *version, size_t len) diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c index 4fcbaac263..766de44b85 100644 --- a/lib/curl_sasl.c +++ b/lib/curl_sasl.c @@ -76,44 +76,6 @@ static const struct { { ZERO_NULL, 0, 0 } }; -/* - * Curl_sasl_cleanup() - * - * This is used to cleanup any libraries or curl modules used by the sasl - * functions. - * - * Parameters: - * - * conn [in] - The connection data. - * authused [in] - The authentication mechanism used. - */ -void Curl_sasl_cleanup(struct connectdata *conn, unsigned short authused) -{ - (void)conn; - (void)authused; - -#if defined(USE_KERBEROS5) - /* Cleanup the gssapi structure */ - if(authused == SASL_MECH_GSSAPI) { - Curl_auth_cleanup_gssapi(&conn->krb5); - } -#endif - -#if defined(USE_GSASL) - /* Cleanup the GSASL structure */ - if(authused & (SASL_MECH_SCRAM_SHA_1 | SASL_MECH_SCRAM_SHA_256)) { - Curl_auth_gsasl_cleanup(&conn->gsasl); - } -#endif - -#if defined(USE_NTLM) - /* Cleanup the NTLM structure */ - if(authused == SASL_MECH_NTLM) { - Curl_auth_cleanup_ntlm(&conn->ntlm); - } -#endif -} - /* * Curl_sasl_decode_mech() * @@ -334,6 +296,241 @@ bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data) return FALSE; } +struct sasl_ctx { + struct SASL *sasl; + struct connectdata *conn; + const char *user; + unsigned short enabledmechs; + const char *mech; + saslstate state1; + saslstate state2; + struct bufref resp; + CURLcode result; +}; + +static bool sasl_choose_external(struct Curl_easy *data, struct sasl_ctx *sctx) +{ + if((sctx->enabledmechs & SASL_MECH_EXTERNAL) && !sctx->conn->passwd[0]) { + 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); + return TRUE; + } + return FALSE; +} + +#ifdef USE_KERBEROS5 +static bool sasl_choose_krb5(struct Curl_easy *data, struct sasl_ctx *sctx) +{ + if(sctx->user && + (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; + + sctx->sasl->mutual_auth = FALSE; + sctx->mech = SASL_MECH_STRING_GSSAPI; + sctx->state1 = SASL_GSSAPI; + sctx->state2 = SASL_GSSAPI_TOKEN; + sctx->sasl->authused = SASL_MECH_GSSAPI; + + 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, + sctx->sasl->mutual_auth, NULL, + krb5, &sctx->resp); + } + return TRUE; + } + return FALSE; +} +#endif /* USE_KERBEROS5 */ + +#ifdef USE_GSASL +static bool sasl_choose_gsasl(struct Curl_easy *data, struct sasl_ctx *sctx) +{ + struct gsasldata *gsasl; + struct bufref nullmsg; + + if(sctx->user && + (sctx->enabledmechs & (SASL_MECH_SCRAM_SHA_256|SASL_MECH_SCRAM_SHA_1))) { + gsasl = Curl_auth_gsasl_get(sctx->conn); + if(!gsasl) { + sctx->result = CURLE_OUT_OF_MEMORY; + return TRUE; /* attempted, but failed */ + } + + if((sctx->enabledmechs & SASL_MECH_SCRAM_SHA_256) && + Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_256, + gsasl)) { + sctx->mech = SASL_MECH_STRING_SCRAM_SHA_256; + sctx->sasl->authused = SASL_MECH_SCRAM_SHA_256; + } + else if((sctx->enabledmechs & SASL_MECH_SCRAM_SHA_1) && + Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_1, + gsasl)) { + sctx->mech = SASL_MECH_STRING_SCRAM_SHA_1; + sctx->sasl->authused = SASL_MECH_SCRAM_SHA_1; + } + else + return FALSE; + + 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); + if(!sctx->result && (sctx->sasl->force_ir || data->set.sasl_ir)) + sctx->result = Curl_auth_gsasl_token(data, &nullmsg, gsasl, &sctx->resp); + return TRUE; + } + return FALSE; +} + +#endif /* USE_GSASL */ + +#ifndef CURL_DISABLE_DIGEST_AUTH +static bool sasl_choose_digest(struct Curl_easy *data, struct sasl_ctx *sctx) +{ + (void)data; + if(!sctx->user) + return FALSE; + else if((sctx->enabledmechs & SASL_MECH_DIGEST_MD5) && + Curl_auth_is_digest_supported()) { + sctx->mech = SASL_MECH_STRING_DIGEST_MD5; + sctx->state1 = SASL_DIGESTMD5; + sctx->sasl->authused = SASL_MECH_DIGEST_MD5; + return TRUE; + } + else if(sctx->enabledmechs & SASL_MECH_CRAM_MD5) { + sctx->mech = SASL_MECH_STRING_CRAM_MD5; + sctx->state1 = SASL_CRAMMD5; + sctx->sasl->authused = SASL_MECH_CRAM_MD5; + return TRUE; + } + return FALSE; +} +#endif /* !CURL_DISABLE_DIGEST_AUTH */ + +#ifdef USE_NTLM +static bool sasl_choose_ntlm(struct Curl_easy *data, struct sasl_ctx *sctx) +{ + if(!sctx->user) + return FALSE; + else 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 *hostname; + int port; + + Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); + + sctx->mech = SASL_MECH_STRING_NTLM; + sctx->state1 = SASL_NTLM; + sctx->state2 = SASL_NTLM_TYPE2MSG; + sctx->sasl->authused = SASL_MECH_NTLM; + + 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, + service, hostname, + ntlm, &sctx->resp); + } + return TRUE; + } + return FALSE; +} +#endif /* USE_NTLM */ + +static bool sasl_choose_oauth(struct Curl_easy *data, struct sasl_ctx *sctx) +{ + const char *oauth_bearer = data->set.str[STRING_BEARER]; + + if(sctx->user && oauth_bearer && + (sctx->enabledmechs & SASL_MECH_OAUTHBEARER)) { + const char *hostname; + int port; + Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); + + sctx->mech = SASL_MECH_STRING_OAUTHBEARER; + sctx->state1 = SASL_OAUTH2; + sctx->state2 = SASL_OAUTH2_RESP; + sctx->sasl->authused = SASL_MECH_OAUTHBEARER; + + 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); + return TRUE; + } + return FALSE; +} + +static bool sasl_choose_oauth2(struct Curl_easy *data, struct sasl_ctx *sctx) +{ + const char *oauth_bearer = data->set.str[STRING_BEARER]; + + if(sctx->user && oauth_bearer && + (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); + return TRUE; + } + return FALSE; +} + +static bool sasl_choose_plain(struct Curl_easy *data, struct sasl_ctx *sctx) +{ + if(sctx->user && (sctx->enabledmechs & SASL_MECH_PLAIN)) { + sctx->mech = SASL_MECH_STRING_PLAIN; + sctx->state1 = SASL_PLAIN; + sctx->sasl->authused = SASL_MECH_PLAIN; + + 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); + return TRUE; + } + return FALSE; +} + +static bool sasl_choose_login(struct Curl_easy *data, struct sasl_ctx *sctx) +{ + if(sctx->user && (sctx->enabledmechs & SASL_MECH_LOGIN)) { + sctx->mech = SASL_MECH_STRING_LOGIN; + sctx->state1 = SASL_LOGIN; + sctx->state2 = SASL_LOGIN_PASSWD; + 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); + return TRUE; + } + return FALSE; +} + /* * Curl_sasl_start() * @@ -342,185 +539,66 @@ bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data) CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data, bool force_ir, saslprogress *progress) { - CURLcode result = CURLE_OK; - struct connectdata *conn = data->conn; - unsigned short enabledmechs; - const char *mech = NULL; - struct bufref resp; - saslstate state1 = SASL_STOP; - saslstate state2 = SASL_FINAL; - const char *hostname, *disp_hostname; - int port; -#if defined(USE_KERBEROS5) || defined(USE_NTLM) - 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 nullmsg; + struct sasl_ctx sctx; - Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port); - Curl_bufref_init(&nullmsg); - Curl_bufref_init(&resp); sasl->force_ir = force_ir; /* Latch for future use */ sasl->authused = 0; /* No mechanism used yet */ - enabledmechs = sasl->authmechs & sasl->prefmech; *progress = SASL_IDLE; + memset(&sctx, 0, sizeof(sctx)); + sctx.sasl = sasl; + sctx.conn = data->conn; + sctx.user = data->state.aptr.user; + Curl_bufref_init(&sctx.resp); + sctx.enabledmechs = sasl->authmechs & sasl->prefmech; + sctx.state1 = SASL_STOP; + sctx.state2 = SASL_FINAL; + /* Calculate the supported authentication mechanism, by decreasing order of security, as well as the initial response where appropriate */ - if((enabledmechs & SASL_MECH_EXTERNAL) && !conn->passwd[0]) { - mech = SASL_MECH_STRING_EXTERNAL; - state1 = SASL_EXTERNAL; - sasl->authused = SASL_MECH_EXTERNAL; - - if(force_ir || data->set.sasl_ir) - Curl_auth_create_external_message(conn->user, &resp); - } - else if(data->state.aptr.user) { + if(sasl_choose_external(data, &sctx) || #if defined(USE_KERBEROS5) - if((enabledmechs & SASL_MECH_GSSAPI) && Curl_auth_is_gssapi_supported() && - Curl_auth_user_contains_domain(conn->user)) { - sasl->mutual_auth = FALSE; - mech = SASL_MECH_STRING_GSSAPI; - state1 = SASL_GSSAPI; - state2 = SASL_GSSAPI_TOKEN; - sasl->authused = SASL_MECH_GSSAPI; - - if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_gssapi_user_message(data, conn->user, - conn->passwd, - service, - conn->host.name, - sasl->mutual_auth, - NULL, &conn->krb5, - &resp); - } - else + sasl_choose_krb5(data, &sctx) || #endif #ifdef USE_GSASL - if((enabledmechs & SASL_MECH_SCRAM_SHA_256) && - Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_256, - &conn->gsasl)) { - mech = SASL_MECH_STRING_SCRAM_SHA_256; - sasl->authused = SASL_MECH_SCRAM_SHA_256; - state1 = SASL_GSASL; - state2 = SASL_GSASL; - - result = Curl_auth_gsasl_start(data, conn->user, - conn->passwd, &conn->gsasl); - if(result == CURLE_OK && (force_ir || data->set.sasl_ir)) - result = Curl_auth_gsasl_token(data, &nullmsg, &conn->gsasl, &resp); - } - else if((enabledmechs & SASL_MECH_SCRAM_SHA_1) && - Curl_auth_gsasl_is_supported(data, SASL_MECH_STRING_SCRAM_SHA_1, - &conn->gsasl)) { - mech = SASL_MECH_STRING_SCRAM_SHA_1; - sasl->authused = SASL_MECH_SCRAM_SHA_1; - state1 = SASL_GSASL; - state2 = SASL_GSASL; - - result = Curl_auth_gsasl_start(data, conn->user, - conn->passwd, &conn->gsasl); - if(result == CURLE_OK && (force_ir || data->set.sasl_ir)) - result = Curl_auth_gsasl_token(data, &nullmsg, &conn->gsasl, &resp); - } - else + sasl_choose_gsasl(data, &sctx) || #endif #ifndef CURL_DISABLE_DIGEST_AUTH - if((enabledmechs & SASL_MECH_DIGEST_MD5) && - Curl_auth_is_digest_supported()) { - mech = SASL_MECH_STRING_DIGEST_MD5; - state1 = SASL_DIGESTMD5; - sasl->authused = SASL_MECH_DIGEST_MD5; - } - else if(enabledmechs & SASL_MECH_CRAM_MD5) { - mech = SASL_MECH_STRING_CRAM_MD5; - state1 = SASL_CRAMMD5; - sasl->authused = SASL_MECH_CRAM_MD5; - } - else + sasl_choose_digest(data, &sctx) || #endif #ifdef USE_NTLM - if((enabledmechs & SASL_MECH_NTLM) && Curl_auth_is_ntlm_supported()) { - mech = SASL_MECH_STRING_NTLM; - state1 = SASL_NTLM; - state2 = SASL_NTLM_TYPE2MSG; - sasl->authused = SASL_MECH_NTLM; - - if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_ntlm_type1_message(data, - conn->user, conn->passwd, - service, - hostname, - &conn->ntlm, &resp); - } - else + sasl_choose_ntlm(data, &sctx) || #endif - if((enabledmechs & SASL_MECH_OAUTHBEARER) && oauth_bearer) { - mech = SASL_MECH_STRING_OAUTHBEARER; - state1 = SASL_OAUTH2; - state2 = SASL_OAUTH2_RESP; - sasl->authused = SASL_MECH_OAUTHBEARER; - - if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_oauth_bearer_message(conn->user, - hostname, - port, - oauth_bearer, - &resp); - } - else if((enabledmechs & SASL_MECH_XOAUTH2) && oauth_bearer) { - mech = SASL_MECH_STRING_XOAUTH2; - state1 = SASL_OAUTH2; - sasl->authused = SASL_MECH_XOAUTH2; - - if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_xoauth_bearer_message(conn->user, - oauth_bearer, - &resp); - } - else if(enabledmechs & SASL_MECH_PLAIN) { - mech = SASL_MECH_STRING_PLAIN; - state1 = SASL_PLAIN; - sasl->authused = SASL_MECH_PLAIN; - - if(force_ir || data->set.sasl_ir) - result = Curl_auth_create_plain_message(conn->sasl_authzid, - conn->user, conn->passwd, - &resp); - } - else if(enabledmechs & SASL_MECH_LOGIN) { - mech = SASL_MECH_STRING_LOGIN; - state1 = SASL_LOGIN; - state2 = SASL_LOGIN_PASSWD; - sasl->authused = SASL_MECH_LOGIN; - - if(force_ir || data->set.sasl_ir) - Curl_auth_create_login_message(conn->user, &resp); - } + sasl_choose_oauth(data, &sctx) || + sasl_choose_oauth2(data, &sctx) || + sasl_choose_plain(data, &sctx) || + sasl_choose_login(data, &sctx)) { + /* selected, either we have a mechanism or a failure */ + DEBUGASSERT(sctx.mech || sctx.result); } - if(!result && mech) { - sasl->curmech = mech; - if(Curl_bufref_ptr(&resp)) - result = build_message(sasl, &resp); + if(!sctx.result && sctx.mech) { + sasl->curmech = sctx.mech; + if(Curl_bufref_ptr(&sctx.resp)) + sctx.result = build_message(sasl, &sctx.resp); if(sasl->params->maxirlen && - strlen(mech) + Curl_bufref_len(&resp) > sasl->params->maxirlen) - Curl_bufref_free(&resp); + strlen(sctx.mech) + Curl_bufref_len(&sctx.resp) > + sasl->params->maxirlen) + Curl_bufref_free(&sctx.resp); - if(!result) - result = sasl->params->sendauth(data, mech, &resp); + if(!sctx.result) + sctx.result = sasl->params->sendauth(data, sctx.mech, &sctx.resp); - if(!result) { + if(!sctx.result) { *progress = SASL_INPROGRESS; - sasl_state(sasl, data, Curl_bufref_ptr(&resp) ? state2 : state1); + sasl_state(sasl, data, Curl_bufref_ptr(&sctx.resp) ? + sctx.state2 : sctx.state1); } } - Curl_bufref_free(&resp); - return result; + Curl_bufref_free(&sctx.resp); + return sctx.result; } /* @@ -535,7 +613,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, struct connectdata *conn = data->conn; saslstate newstate = SASL_FINAL; struct bufref resp; - const char *hostname, *disp_hostname; + const char *hostname; int port; #if defined(USE_KERBEROS5) || defined(USE_NTLM) \ || !defined(CURL_DISABLE_DIGEST_AUTH) @@ -546,7 +624,7 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, const char *oauth_bearer = data->set.str[STRING_BEARER]; struct bufref serverdata; - Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port); + Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port); Curl_bufref_init(&serverdata); Curl_bufref_init(&resp); *progress = SASL_INPROGRESS; @@ -587,8 +665,11 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, #ifdef USE_GSASL case SASL_GSASL: result = get_server_message(sasl, data, &serverdata); - if(!result) - result = Curl_auth_gsasl_token(data, &serverdata, &conn->gsasl, &resp); + if(!result) { + struct gsasldata *gsasl = Curl_auth_gsasl_get(conn); + result = !gsasl ? CURLE_OUT_OF_MEMORY : + Curl_auth_gsasl_token(data, &serverdata, gsasl, &resp); + } if(!result && Curl_bufref_len(&resp) > 0) newstate = SASL_GSASL; break; @@ -615,50 +696,57 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, #endif #ifdef USE_NTLM - case SASL_NTLM: + case SASL_NTLM: { /* Create the type-1 message */ - result = Curl_auth_create_ntlm_type1_message(data, - conn->user, conn->passwd, - service, hostname, - &conn->ntlm, &resp); + 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, + ntlm, &resp); newstate = SASL_NTLM_TYPE2MSG; break; - case SASL_NTLM_TYPE2MSG: + } + case SASL_NTLM_TYPE2MSG: { /* Decode the type-2 message */ - result = get_server_message(sasl, data, &serverdata); + struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, FALSE); + result = !ntlm ? CURLE_FAILED_INIT : + get_server_message(sasl, data, &serverdata); if(!result) - result = Curl_auth_decode_ntlm_type2_message(data, &serverdata, - &conn->ntlm); + result = Curl_auth_decode_ntlm_type2_message(data, &serverdata, ntlm); if(!result) result = Curl_auth_create_ntlm_type3_message(data, conn->user, - conn->passwd, &conn->ntlm, + conn->passwd, ntlm, &resp); break; + } #endif #if defined(USE_KERBEROS5) - case SASL_GSSAPI: - result = Curl_auth_create_gssapi_user_message(data, conn->user, - conn->passwd, - service, - conn->host.name, + 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, sasl->mutual_auth, NULL, - &conn->krb5, - &resp); + krb5, &resp); newstate = SASL_GSSAPI_TOKEN; break; + } case SASL_GSSAPI_TOKEN: result = get_server_message(sasl, data, &serverdata); if(!result) { - if(sasl->mutual_auth) { + struct kerberos5data *krb5 = Curl_auth_krb5_get(conn); + if(!krb5) + result = CURLE_OUT_OF_MEMORY; + 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, NULL, NULL, sasl->mutual_auth, &serverdata, - &conn->krb5, - &resp); + krb5, &resp); newstate = SASL_GSSAPI_NO_DATA; } else @@ -666,19 +754,22 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data, result = Curl_auth_create_gssapi_security_message(data, conn->sasl_authzid, &serverdata, - &conn->krb5, - &resp); + krb5, &resp); } break; case SASL_GSSAPI_NO_DATA: /* Decode the security challenge and create the response message */ result = get_server_message(sasl, data, &serverdata); - if(!result) - result = Curl_auth_create_gssapi_security_message(data, - conn->sasl_authzid, - &serverdata, - &conn->krb5, - &resp); + if(!result) { + struct kerberos5data *krb5 = Curl_auth_krb5_get(conn); + if(!krb5) + result = CURLE_OUT_OF_MEMORY; + else + result = Curl_auth_create_gssapi_security_message(data, + conn->sasl_authzid, + &serverdata, + krb5, &resp); + } break; #endif diff --git a/lib/curl_sasl.h b/lib/curl_sasl.h index 59983f7c66..0674d56f86 100644 --- a/lib/curl_sasl.h +++ b/lib/curl_sasl.h @@ -135,10 +135,6 @@ struct SASL { (wordlen == (sizeof(mech) - 1) / sizeof(char) && \ !memcmp(line, mech, wordlen)) -/* This is used to cleanup any libraries or curl modules used by the sasl - functions */ -void Curl_sasl_cleanup(struct connectdata *conn, unsigned short authused); - /* Convert a mechanism name to a token */ unsigned short Curl_sasl_decode_mech(const char *ptr, size_t maxlen, size_t *len); diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 1d484c61ab..4dc46f7a73 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -224,46 +224,46 @@ #ifdef HTTP_ONLY # ifndef CURL_DISABLE_DICT -# define CURL_DISABLE_DICT +# define CURL_DISABLE_DICT # endif # ifndef CURL_DISABLE_FILE -# define CURL_DISABLE_FILE +# define CURL_DISABLE_FILE # endif # ifndef CURL_DISABLE_FTP -# define CURL_DISABLE_FTP +# define CURL_DISABLE_FTP # endif # ifndef CURL_DISABLE_GOPHER -# define CURL_DISABLE_GOPHER +# define CURL_DISABLE_GOPHER # endif # ifndef CURL_DISABLE_IMAP -# define CURL_DISABLE_IMAP +# define CURL_DISABLE_IMAP # endif # ifndef CURL_DISABLE_LDAP -# define CURL_DISABLE_LDAP +# define CURL_DISABLE_LDAP # endif # ifndef CURL_DISABLE_LDAPS -# define CURL_DISABLE_LDAPS +# define CURL_DISABLE_LDAPS # endif # ifndef CURL_DISABLE_MQTT -# define CURL_DISABLE_MQTT +# define CURL_DISABLE_MQTT # endif # ifndef CURL_DISABLE_POP3 -# define CURL_DISABLE_POP3 +# define CURL_DISABLE_POP3 # endif # ifndef CURL_DISABLE_RTSP -# define CURL_DISABLE_RTSP +# define CURL_DISABLE_RTSP # endif # ifndef CURL_DISABLE_SMB -# define CURL_DISABLE_SMB +# define CURL_DISABLE_SMB # endif # ifndef CURL_DISABLE_SMTP -# define CURL_DISABLE_SMTP +# define CURL_DISABLE_SMTP # endif # ifndef CURL_DISABLE_TELNET -# define CURL_DISABLE_TELNET +# define CURL_DISABLE_TELNET # endif # ifndef CURL_DISABLE_TFTP -# define CURL_DISABLE_TFTP +# define CURL_DISABLE_TFTP # endif #endif @@ -470,64 +470,48 @@ #include #endif -/* - * Large file (>2Gb) support using Win32 functions. - */ - -#ifdef USE_WIN32_LARGE_FILES +#ifdef _WIN32 # ifdef HAVE_IO_H # include # endif # include # include -# undef lseek -# define lseek(fdes,offset,whence) _lseeki64(fdes, offset, whence) -# undef fstat -# define fstat(fdes,stp) _fstati64(fdes, stp) -# undef stat -# define stat(fname,stp) curlx_win32_stat(fname, stp) -# define struct_stat struct _stati64 -# define LSEEK_ERROR (__int64)-1 -# define open curlx_win32_open -# define fopen(fname,mode) curlx_win32_fopen(fname, mode) - int curlx_win32_open(const char *filename, int oflag, ...); - int curlx_win32_stat(const char *path, struct_stat *buffer); - FILE *curlx_win32_fopen(const char *filename, const char *mode); -#endif - -#ifdef __DJGPP__ -/* Requires DJGPP 2.04 */ +# ifdef USE_WIN32_LARGE_FILES + /* Large file (>2Gb) support using Win32 functions. */ +# undef lseek +# define lseek(fdes, offset, whence) _lseeki64(fdes, offset, whence) +# undef fstat +# define fstat(fdes,stp) _fstati64(fdes, stp) +# undef stat +# define struct_stat struct _stati64 +# define LSEEK_ERROR (__int64)-1 +# else + /* Small file (<2Gb) support using Win32 functions. */ +# ifndef UNDER_CE +# undef lseek +# define lseek(fdes, offset, whence) _lseek(fdes, (long)offset, whence) +# define fstat(fdes, stp) _fstat(fdes, stp) +# define struct_stat struct _stat +# endif +# define LSEEK_ERROR (long)-1 +# endif +# ifndef UNDER_CE + int curlx_win32_stat(const char *path, struct_stat *buffer); + int curlx_win32_open(const char *filename, int oflag, ...); + FILE *curlx_win32_fopen(const char *filename, const char *mode); +# define stat(fname, stp) curlx_win32_stat(fname, stp) +# define open curlx_win32_open +# define CURL_FOPEN(fname, mode) curlx_win32_fopen(fname, mode) +# define fopen(fname, mode) CURL_FOPEN(fname, mode) +# endif +#elif defined(__DJGPP__) + /* Requires DJGPP 2.04 */ # include # undef lseek # define lseek(fdes,offset,whence) llseek(fdes, offset, whence) # define LSEEK_ERROR (offset_t)-1 #endif -/* - * Small file (<2Gb) support using Win32 functions. - */ - -#if defined(_WIN32) && !defined(USE_WIN32_LARGE_FILES) -# ifdef HAVE_IO_H -# include -# endif -# include -# include -# ifndef UNDER_CE -# undef lseek -# define lseek(fdes,offset,whence) _lseek(fdes, (long)offset, whence) -# define fstat(fdes,stp) _fstat(fdes, stp) -# define stat(fname,stp) curlx_win32_stat(fname, stp) -# define struct_stat struct _stat -# define open curlx_win32_open -# define fopen(fname,mode) curlx_win32_fopen(fname, mode) - int curlx_win32_stat(const char *path, struct_stat *buffer); - int curlx_win32_open(const char *filename, int oflag, ...); - FILE *curlx_win32_fopen(const char *filename, const char *mode); -# endif -# define LSEEK_ERROR (long)-1 -#endif - #ifndef struct_stat #define struct_stat struct stat #endif @@ -582,7 +566,7 @@ # endif # endif # ifndef SIZEOF_OFF_T -# define SIZEOF_OFF_T 4 +# define SIZEOF_OFF_T 4 # endif #endif @@ -590,9 +574,9 @@ #error "too small curl_off_t" #else /* assume SIZEOF_CURL_OFF_T == 8 */ -# define CURL_OFF_T_MAX CURL_OFF_T_C(0x7FFFFFFFFFFFFFFF) +# define CURL_OFF_T_MAX 0x7FFFFFFFFFFFFFFF #endif -#define CURL_OFF_T_MIN (-CURL_OFF_T_MAX - CURL_OFF_T_C(1)) +#define CURL_OFF_T_MIN (-CURL_OFF_T_MAX - 1) #if (SIZEOF_CURL_OFF_T != 8) # error "curl_off_t must be exactly 64 bits" @@ -677,12 +661,8 @@ # define select(n,r,w,x,t) select_s(n,r,w,x,t) # define ioctl(x,y,z) ioctlsocket(x,y,(char *)(z)) # include -# ifdef word -# undef word -# endif -# ifdef byte -# undef byte -# endif +# undef word +# undef byte # endif /* MSDOS */ @@ -739,8 +719,8 @@ #endif #if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \ - defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ - defined(USE_BEARSSL) || defined(USE_RUSTLS) + defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ + defined(USE_RUSTLS) #define USE_SSL /* SSL support has been enabled */ #endif @@ -775,7 +755,7 @@ /* Single point where USE_NTLM definition might be defined */ #ifndef CURL_DISABLE_NTLM # if defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \ - defined(USE_GNUTLS) || defined(USE_SECTRANSP) || \ + defined(USE_GNUTLS) || \ defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \ (defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT)) # define USE_CURL_NTLM_CORE diff --git a/lib/curl_setup_once.h b/lib/curl_setup_once.h index c1051e0fae..bec45de888 100644 --- a/lib/curl_setup_once.h +++ b/lib/curl_setup_once.h @@ -41,11 +41,9 @@ #include #endif -#ifdef HAVE_SYS_STAT_H #include -#endif -#ifdef HAVE_SYS_TIME_H +#if !defined(_WIN32) || defined(__MINGW32__) #include #endif @@ -96,7 +94,7 @@ # endif #endif -#ifdef HAVE_SYS_SOCKET_H +#ifndef _WIN32 #include #endif @@ -197,17 +195,19 @@ struct timeval { */ #ifdef HAVE_CLOSESOCKET -# define sclose(x) closesocket((x)) +# define CURL_SCLOSE(x) closesocket((x)) #elif defined(HAVE_CLOSESOCKET_CAMEL) -# define sclose(x) CloseSocket((x)) +# define CURL_SCLOSE(x) CloseSocket((x)) #elif defined(MSDOS) /* Watt-32 */ -# define sclose(x) close_s((x)) +# define CURL_SCLOSE(x) close_s((x)) #elif defined(USE_LWIPSOCK) -# define sclose(x) lwip_close((x)) +# define CURL_SCLOSE(x) lwip_close((x)) #else -# define sclose(x) close((x)) +# define CURL_SCLOSE(x) close((x)) #endif +#define sclose(x) CURL_SCLOSE(x) + /* * Stack-independent version of fcntl() on sockets: */ diff --git a/lib/curl_sha512_256.c b/lib/curl_sha512_256.c index 2d8a23f19a..81bfddc711 100644 --- a/lib/curl_sha512_256.c +++ b/lib/curl_sha512_256.c @@ -34,9 +34,7 @@ * * GnuTLS * * wolfSSL * * Schannel SSPI - * * Secure Transport (Darwin) * * mbedTLS - * * BearSSL * * Rustls * Skip the backend if it does not support the required algorithm */ diff --git a/lib/curl_sspi.c b/lib/curl_sspi.c index cd577e8a5e..5f19e960b9 100644 --- a/lib/curl_sspi.c +++ b/lib/curl_sspi.c @@ -28,6 +28,7 @@ #include #include "curl_sspi.h" +#include "strdup.h" #include "curlx/multibyte.h" #include "system_win32.h" #include "curlx/version_win32.h" @@ -37,23 +38,6 @@ #include "curl_memory.h" #include "memdebug.h" -/* We use our own typedef here since some headers might lack these */ -typedef PSecurityFunctionTable (APIENTRY *INITSECURITYINTERFACE_FN)(VOID); - -/* See definition of SECURITY_ENTRYPOINT in sspi.h */ -#ifdef UNICODE -# ifdef UNDER_CE -# define SECURITYENTRYPOINT L"InitSecurityInterfaceW" -# else -# define SECURITYENTRYPOINT "InitSecurityInterfaceW" -# endif -#else -# define SECURITYENTRYPOINT "InitSecurityInterfaceA" -#endif - -/* Handle of security.dll or secur32.dll, depending on Windows version */ -HMODULE Curl_hSecDll = NULL; - /* Pointer to SSPI dispatch table */ PSecurityFunctionTable Curl_pSecFn = NULL; @@ -76,31 +60,14 @@ PSecurityFunctionTable Curl_pSecFn = NULL; */ CURLcode Curl_sspi_global_init(void) { - INITSECURITYINTERFACE_FN pInitSecurityInterface; - /* If security interface is not yet initialized try to do this */ - if(!Curl_hSecDll) { - /* Security Service Provider Interface (SSPI) functions are located in - * security.dll on WinNT 4.0 and in secur32.dll on Win9x. Win2K and XP - * have both these DLLs (security.dll forwards calls to secur32.dll) */ - - /* Load SSPI dll into the address space of the calling process */ - if(curlx_verify_windows_version(4, 0, 0, PLATFORM_WINNT, VERSION_EQUAL)) - Curl_hSecDll = Curl_load_library(TEXT("security.dll")); - else - Curl_hSecDll = Curl_load_library(TEXT("secur32.dll")); - if(!Curl_hSecDll) - return CURLE_FAILED_INIT; - - /* Get address of the InitSecurityInterfaceA function from the SSPI dll */ - pInitSecurityInterface = - CURLX_FUNCTION_CAST(INITSECURITYINTERFACE_FN, - (GetProcAddress(Curl_hSecDll, SECURITYENTRYPOINT))); - if(!pInitSecurityInterface) - return CURLE_FAILED_INIT; - + if(!Curl_pSecFn) { /* Get pointer to Security Service Provider Interface dispatch table */ - Curl_pSecFn = pInitSecurityInterface(); +#ifdef __MINGW32CE__ + Curl_pSecFn = InitSecurityInterfaceW(); +#else + Curl_pSecFn = InitSecurityInterface(); +#endif if(!Curl_pSecFn) return CURLE_FAILED_INIT; } @@ -119,9 +86,7 @@ CURLcode Curl_sspi_global_init(void) */ void Curl_sspi_global_cleanup(void) { - if(Curl_hSecDll) { - FreeLibrary(Curl_hSecDll); - Curl_hSecDll = NULL; + if(Curl_pSecFn) { Curl_pSecFn = NULL; } } diff --git a/lib/curl_sspi.h b/lib/curl_sspi.h index 8fdf8ef249..540cdfd19c 100644 --- a/lib/curl_sspi.h +++ b/lib/curl_sspi.h @@ -57,7 +57,6 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp, void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity); /* Forward-declaration of global variables defined in curl_sspi.c */ -extern HMODULE Curl_hSecDll; extern PSecurityFunctionTable Curl_pSecFn; /* Provide some definitions missing in old headers */ diff --git a/lib/curl_trc.c b/lib/curl_trc.c index 566cdc533e..334b6e2a1d 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -31,7 +31,6 @@ #include "easyif.h" #include "cfilters.h" #include "multiif.h" -#include "strcase.h" #include "cf-socket.h" #include "connect.h" @@ -206,7 +205,7 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...) static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat, const char *opt_id, int opt_id_idx, - const char * const fmt, va_list ap) CURL_PRINTF(5, 0); + const char * const fmt, va_list ap) CURL_PRINTF(5, 0); static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat, diff --git a/lib/vtls/bearssl.h b/lib/curlx/binmode.h similarity index 64% rename from lib/vtls/bearssl.h rename to lib/curlx/binmode.h index 8bb254f7da..3f356edd59 100644 --- a/lib/vtls/bearssl.h +++ b/lib/curlx/binmode.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_BEARSSL_H -#define HEADER_CURL_BEARSSL_H +#ifndef HEADER_CURL_TOOL_BINMODE_H +#define HEADER_CURL_TOOL_BINMODE_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Michael Forney, + * Copyright (C) Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -23,12 +23,17 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ - #include "../curl_setup.h" -#ifdef USE_BEARSSL +#if (defined(HAVE_SETMODE) || defined(HAVE__SETMODE)) && defined(O_BINARY) +/* Requires io.h and/or fcntl.h when available */ +#ifdef HAVE__SETMODE +# define CURLX_SET_BINMODE(stream) (void)_setmode(fileno(stream), O_BINARY) +#else +# define CURLX_SET_BINMODE(stream) (void)setmode(fileno(stream), O_BINARY) +#endif +#else +# define CURLX_SET_BINMODE(stream) (void)stream; Curl_nop_stmt +#endif -extern const struct Curl_ssl Curl_ssl_bearssl; - -#endif /* USE_BEARSSL */ -#endif /* HEADER_CURL_BEARSSL_H */ +#endif /* HEADER_CURL_TOOL_BINMODE_H */ diff --git a/lib/curlx/curlx.h b/lib/curlx/curlx.h index 983c7b5c75..9f7bd3a975 100644 --- a/lib/curlx/curlx.h +++ b/lib/curlx/curlx.h @@ -31,6 +31,9 @@ * be. */ +#include "binmode.h" +/* "binmode.h" provides macro CURLX_SET_BINMODE() */ + #include "nonblock.h" /* "nonblock.h" provides curlx_nonblock() */ @@ -65,10 +68,16 @@ #include "timeval.h" #include "timediff.h" +#include "wait.h" +/* for curlx_wait_ms */ + #include "winapi.h" /* for curlx_winapi_strerror */ #include "inet_pton.h" /* for curlx_inet_pton */ +#include "inet_ntop.h" +/* for curlx_inet_ntop */ + #endif /* HEADER_CURL_CURLX_H */ diff --git a/lib/inet_ntop.c b/lib/curlx/inet_ntop.c similarity index 86% rename from lib/inet_ntop.c rename to lib/curlx/inet_ntop.c index bb2ec57872..e5ff45cb1b 100644 --- a/lib/inet_ntop.c +++ b/lib/curlx/inet_ntop.c @@ -20,7 +20,7 @@ * Original code by Paul Vixie. "curlified" by Gisle Vanem. */ -#include "curl_setup.h" +#include "../curl_setup.h" #ifndef HAVE_INET_NTOP @@ -35,7 +35,6 @@ #endif #include "inet_ntop.h" -#include "curl_printf.h" #define IN6ADDRSZ 16 /* #define INADDRSZ 4 */ @@ -65,8 +64,9 @@ static char *inet_ntop4(const unsigned char *src, char *dst, size_t size) DEBUGASSERT(size >= 16); - tmp[0] = '\0'; - (void)msnprintf(tmp, sizeof(tmp), "%d.%d.%d.%d", + /* this sprintf() does not overflow the buffer. Avoids snprintf to work more + widely. Avoids the msnprintf family to work as a curlx function. */ + (void)(sprintf)(tmp, "%d.%d.%d.%d", ((int)((unsigned char)src[0])) & 0xff, ((int)((unsigned char)src[1])) & 0xff, ((int)((unsigned char)src[2])) & 0xff, @@ -162,7 +162,21 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size) tp += strlen(tp); break; } - tp += msnprintf(tp, 5, "%x", words[i]); + else { + /* Lower-case digits. Can't use the set from mprintf.c since this + needs to work as a curlx function */ + static const unsigned char ldigits[] = "0123456789abcdef"; + + unsigned int w = words[i]; + /* output lowercase 16bit hex number but ignore leading zeroes */ + if(w & 0xf000) + *tp++ = ldigits[(w & 0xf000) >> 12]; + if(w & 0xff00) + *tp++ = ldigits[(w & 0x0f00) >> 8]; + if(w & 0xfff0) + *tp++ = ldigits[(w & 0x00f0) >> 4]; + *tp++ = ldigits[(w & 0x000f)]; + } } /* Was it a trailing run of 0x00's? @@ -196,7 +210,7 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size) * code. This is to avoid losing the actual last Winsock error. When this * function returns NULL, check errno not SOCKERRNO. */ -char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size) +char *curlx_inet_ntop(int af, const void *src, char *buf, size_t size) { switch(af) { case AF_INET: diff --git a/lib/inet_ntop.h b/lib/curlx/inet_ntop.h similarity index 86% rename from lib/inet_ntop.h rename to lib/curlx/inet_ntop.h index 9923daaed1..68f8c8d132 100644 --- a/lib/inet_ntop.h +++ b/lib/curlx/inet_ntop.h @@ -24,26 +24,26 @@ * ***************************************************************************/ -#include "curl_setup.h" +#include "../curl_setup.h" -char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t size); +char *curlx_inet_ntop(int af, const void *addr, char *buf, size_t size); #ifdef HAVE_INET_NTOP #ifdef HAVE_NETINET_IN_H #include #endif -#ifdef HAVE_SYS_SOCKET_H +#ifndef _WIN32 #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef __AMIGA__ -#define Curl_inet_ntop(af,addr,buf,size) \ +#define curlx_inet_ntop(af,addr,buf,size) \ (char *)inet_ntop(af, CURL_UNCONST(addr), (unsigned char *)buf, \ (curl_socklen_t)(size)) #else -#define Curl_inet_ntop(af,addr,buf,size) \ +#define curlx_inet_ntop(af,addr,buf,size) \ inet_ntop(af, addr, buf, (curl_socklen_t)(size)) #endif #endif diff --git a/lib/curlx/inet_pton.h b/lib/curlx/inet_pton.h index a9dc43085f..610b329f51 100644 --- a/lib/curlx/inet_pton.h +++ b/lib/curlx/inet_pton.h @@ -32,7 +32,7 @@ int curlx_inet_pton(int, const char *, void *); #ifdef HAVE_NETINET_IN_H #include #endif -#ifdef HAVE_SYS_SOCKET_H +#ifndef _WIN32 #include #endif #ifdef HAVE_ARPA_INET_H diff --git a/lib/curlx/strparse.c b/lib/curlx/strparse.c index b8b2a14d3c..ecdf5b9615 100644 --- a/lib/curlx/strparse.c +++ b/lib/curlx/strparse.c @@ -23,7 +23,10 @@ ***************************************************************************/ #include "strparse.h" -#include "../strcase.h" + +#ifndef WITHOUT_LIBCURL +#include /* for curl_strnequal() */ +#endif void curlx_str_init(struct Curl_str *out) { @@ -40,7 +43,7 @@ void curlx_str_assign(struct Curl_str *out, const char *str, size_t len) /* Get a word until the first DELIM or end of string. At least one byte long. return non-zero on error */ int curlx_str_until(const char **linep, struct Curl_str *out, - const size_t max, char delim) + const size_t max, char delim) { const char *s = *linep; size_t len = 0; @@ -64,7 +67,7 @@ int curlx_str_until(const char **linep, struct Curl_str *out, /* Get a word until the first space or end of string. At least one byte long. return non-zero on error */ int curlx_str_word(const char **linep, struct Curl_str *out, - const size_t max) + const size_t max) { return curlx_str_until(linep, out, max, ' '); } @@ -72,7 +75,7 @@ int curlx_str_word(const char **linep, struct Curl_str *out, /* Get a word until a newline byte or end of string. At least one byte long. return non-zero on error */ int curlx_str_untilnl(const char **linep, struct Curl_str *out, - const size_t max) + const size_t max) { const char *s = *linep; size_t len = 0; @@ -96,7 +99,7 @@ int curlx_str_untilnl(const char **linep, struct Curl_str *out, /* Get a "quoted" word. No escaping possible. return non-zero on error */ int curlx_str_quotedword(const char **linep, struct Curl_str *out, - const size_t max) + const size_t max) { const char *s = *linep; size_t len = 0; @@ -238,7 +241,7 @@ int curlx_str_newline(const char **linep) int curlx_str_casecompare(struct Curl_str *str, const char *check) { size_t clen = check ? strlen(check) : 0; - return ((str->len == clen) && strncasecompare(str->str, check, clen)); + return ((str->len == clen) && curl_strnequal(str->str, check, clen)); } #endif diff --git a/lib/curlx/strparse.h b/lib/curlx/strparse.h index 17bfdb8071..6a0bf28d6c 100644 --- a/lib/curlx/strparse.h +++ b/lib/curlx/strparse.h @@ -55,17 +55,17 @@ int curlx_str_word(const char **linep, struct Curl_str *out, const size_t max); /* Get a word until the first DELIM or end of string return non-zero on error */ int curlx_str_until(const char **linep, struct Curl_str *out, const size_t max, - char delim); + char delim); /* Get a word until a newline byte or end of string. At least one byte long. return non-zero on error */ int curlx_str_untilnl(const char **linep, struct Curl_str *out, - const size_t max); + const size_t max); /* Get a "quoted" word. No escaping possible. return non-zero on error */ int curlx_str_quotedword(const char **linep, struct Curl_str *out, - const size_t max); + const size_t max); /* Advance over a single character. return non-zero on error */ diff --git a/lib/curlx/wait.c b/lib/curlx/wait.c new file mode 100644 index 0000000000..f568fb6948 --- /dev/null +++ b/lib/curlx/wait.c @@ -0,0 +1,97 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , 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 HAVE_SELECT +#error "We cannot compile without select() support." +#endif + +#include + +#ifdef HAVE_SYS_SELECT_H +#include +#elif defined(HAVE_UNISTD_H) +#include +#endif + +#ifdef MSDOS +#include /* delay() */ +#endif + +#include "timediff.h" +#include "wait.h" + +/* + * Internal function used for waiting a specific amount of ms in + * Curl_socket_check() and Curl_poll() when no file descriptor is provided to + * wait on, just being used to delay execution. Winsock select() and poll() + * timeout mechanisms need a valid socket descriptor in a not null file + * descriptor set to work. Waiting indefinitely with this function is not + * allowed, a zero or negative timeout value will return immediately. Timeout + * resolution, accuracy, as well as maximum supported value is system + * dependent, neither factor is a critical issue for the intended use of this + * function in the library. + * + * Return values: + * -1 = system call error, or invalid timeout value + * 0 = specified timeout has elapsed, or interrupted + */ +int curlx_wait_ms(timediff_t timeout_ms) +{ + int r = 0; + + if(!timeout_ms) + return 0; + if(timeout_ms < 0) { + SET_SOCKERRNO(SOCKEINVAL); + return -1; + } +#if defined(MSDOS) + delay((unsigned int)timeout_ms); +#elif defined(_WIN32) + /* prevent overflow, timeout_ms is typecast to ULONG/DWORD. */ +#if TIMEDIFF_T_MAX >= ULONG_MAX + if(timeout_ms >= ULONG_MAX) + timeout_ms = ULONG_MAX-1; + /* do not use ULONG_MAX, because that is equal to INFINITE */ +#endif + Sleep((DWORD)timeout_ms); +#else + /* avoid using poll() for this since it behaves incorrectly with no sockets + on Apple operating systems */ + { + struct timeval pending_tv; + r = select(0, NULL, NULL, NULL, curlx_mstotv(&pending_tv, timeout_ms)); + } +#endif /* _WIN32 */ + if(r) { + if((r == -1) && (SOCKERRNO == SOCKEINTR)) + /* make EINTR from select or poll not a "lethal" error */ + r = 0; + else + r = -1; + } + return r; +} diff --git a/lib/vtls/sectransp.h b/lib/curlx/wait.h similarity index 80% rename from lib/vtls/sectransp.h rename to lib/curlx/wait.h index c82dc18445..208bc20acc 100644 --- a/lib/vtls/sectransp.h +++ b/lib/curlx/wait.h @@ -1,5 +1,5 @@ -#ifndef HEADER_CURL_SECTRANSP_H -#define HEADER_CURL_SECTRANSP_H +#ifndef HEADER_CURL_WAIT_H +#define HEADER_CURL_WAIT_H /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | @@ -7,7 +7,6 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) Nick Zitzmann, . * Copyright (C) Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which @@ -24,11 +23,9 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ + #include "../curl_setup.h" -#ifdef USE_SECTRANSP +int curlx_wait_ms(timediff_t timeout_ms); -extern const struct Curl_ssl Curl_ssl_sectransp; - -#endif /* USE_SECTRANSP */ -#endif /* HEADER_CURL_SECTRANSP_H */ +#endif /* HEADER_CURL_WAIT_H */ diff --git a/lib/curlx/warnless.c b/lib/curlx/warnless.c index 5ca92450d8..c6cb7c6e5e 100644 --- a/lib/curlx/warnless.c +++ b/lib/curlx/warnless.c @@ -35,11 +35,6 @@ #endif /* __INTEL_COMPILER && __unix__ */ -#ifdef _WIN32 -#undef read -#undef write -#endif - #include #define CURL_MASK_UCHAR ((unsigned char)~0) @@ -295,21 +290,3 @@ size_t curlx_sitouz(int sinum) # pragma warning(pop) #endif } - -#ifdef _WIN32 - -ssize_t curlx_read(int fd, void *buf, size_t count) -{ - return (ssize_t)read(fd, buf, curlx_uztoui(count)); -} - -ssize_t curlx_write(int fd, const void *buf, size_t count) -{ - return (ssize_t)write(fd, buf, curlx_uztoui(count)); -} - -#endif /* _WIN32 */ - -/* Ensure that warnless.h redefinitions continue to have an effect - in "unity" builds. */ -#undef HEADER_CURL_WARNLESS_H_REDEFS diff --git a/lib/curlx/warnless.h b/lib/curlx/warnless.h index c78b61169b..0a0c608073 100644 --- a/lib/curlx/warnless.h +++ b/lib/curlx/warnless.h @@ -57,24 +57,11 @@ unsigned short curlx_uitous(unsigned int uinum); size_t curlx_sitouz(int sinum); -#ifdef _WIN32 - -ssize_t curlx_read(int fd, void *buf, size_t count); - -ssize_t curlx_write(int fd, const void *buf, size_t count); - -#endif /* _WIN32 */ - -#endif /* HEADER_CURL_WARNLESS_H */ - -#ifndef HEADER_CURL_WARNLESS_H_REDEFS -#define HEADER_CURL_WARNLESS_H_REDEFS - #ifdef _WIN32 #undef read -#define read(fd, buf, count) curlx_read(fd, buf, count) +#define read(fd, buf, count) (ssize_t)_read(fd, buf, curlx_uztoui(count)) #undef write -#define write(fd, buf, count) curlx_write(fd, buf, count) +#define write(fd, buf, count) (ssize_t)_write(fd, buf, curlx_uztoui(count)) #endif -#endif /* HEADER_CURL_WARNLESS_H_REDEFS */ +#endif /* HEADER_CURL_WARNLESS_H */ diff --git a/lib/cw-out.c b/lib/cw-out.c index 097ef85e8b..ee7dc65dff 100644 --- a/lib/cw-out.c +++ b/lib/cw-out.c @@ -31,6 +31,7 @@ #include "headers.h" #include "multiif.h" #include "sendf.h" +#include "transfer.h" #include "cw-out.h" #include "cw-pause.h" @@ -234,11 +235,9 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx, failf(data, "Write callback asked for PAUSE when not supported"); return CURLE_WRITE_ERROR; } - /* mark the connection as RECV paused */ - data->req.keepon |= KEEP_RECV_PAUSE; ctx->paused = TRUE; CURL_TRC_WRITE(data, "[OUT] PAUSE requested by client"); - break; + return Curl_xfer_pause_recv(data, TRUE); } else if(CURL_WRITEFUNC_ERROR == nwritten) { failf(data, "client returned ERROR on write of %zu bytes", wlen); diff --git a/lib/dict.c b/lib/dict.c index 637f349e70..7f13e6f7ea 100644 --- a/lib/dict.c +++ b/lib/dict.c @@ -60,7 +60,6 @@ #include "progress.h" #include "dict.h" #include "curl_printf.h" -#include "strcase.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" @@ -198,9 +197,9 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) if(result) return result; - if(strncasecompare(path, DICT_MATCH, sizeof(DICT_MATCH)-1) || - strncasecompare(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) || - strncasecompare(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) { + if(curl_strnequal(path, DICT_MATCH, sizeof(DICT_MATCH)-1) || + curl_strnequal(path, DICT_MATCH2, sizeof(DICT_MATCH2)-1) || + curl_strnequal(path, DICT_MATCH3, sizeof(DICT_MATCH3)-1)) { word = strchr(path, ':'); if(word) { @@ -245,9 +244,9 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done) } Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE); /* no upload */ } - else if(strncasecompare(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) || - strncasecompare(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) || - strncasecompare(path, DICT_DEFINE3, sizeof(DICT_DEFINE3)-1)) { + else if(curl_strnequal(path, DICT_DEFINE, sizeof(DICT_DEFINE)-1) || + curl_strnequal(path, DICT_DEFINE2, sizeof(DICT_DEFINE2)-1) || + curl_strnequal(path, DICT_DEFINE3, sizeof(DICT_DEFINE3)-1)) { word = strchr(path, ':'); if(word) { diff --git a/lib/doh.c b/lib/doh.c index 9f408402a0..f96ae0824a 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -257,7 +257,7 @@ static void doh_probe_done(struct Curl_easy *data, if(!dohp->pending) { /* DoH completed, run the transfer picking up the results */ - Curl_expire(data, 0, EXPIRE_RUN_NOW); + Curl_multi_mark_dirty(data); } } } @@ -377,8 +377,6 @@ static CURLcode doh_probe_run(struct Curl_easy *data, options should be added to check doh proxy insecure separately, CURLOPT_DOH_PROXY_SSL_VERIFYHOST and CURLOPT_DOH_PROXY_SSL_VERIFYPEER. */ - if(data->set.ssl.falsestart) - ERROR_CHECK_SETOPT(CURLOPT_SSL_FALSESTART, 1L); if(data->set.str[STRING_SSL_CAFILE]) { ERROR_CHECK_SETOPT(CURLOPT_CAINFO, data->set.str[STRING_SSL_CAFILE]); @@ -590,7 +588,7 @@ static void doh_store_a(const unsigned char *doh, int index, } static void doh_store_aaaa(const unsigned char *doh, int index, - struct dohentry *d) + struct dohentry *d) { /* silently ignore addresses over the limit */ if(d->numaddr < DOH_MAX_ADDR) { diff --git a/lib/dynhds.c b/lib/dynhds.c index 5c52d7411a..dcb9193a8b 100644 --- a/lib/dynhds.c +++ b/lib/dynhds.c @@ -150,7 +150,7 @@ struct dynhds_entry *Curl_dynhds_get(struct dynhds *dynhds, const char *name, size_t i; for(i = 0; i < dynhds->hds_len; ++i) { if(dynhds->hds[i]->namelen == namelen && - strncasecompare(dynhds->hds[i]->name, name, namelen)) { + curl_strnequal(dynhds->hds[i]->name, name, namelen)) { return dynhds->hds[i]; } } @@ -297,7 +297,7 @@ size_t Curl_dynhds_count_name(struct dynhds *dynhds, size_t i; for(i = 0; i < dynhds->hds_len; ++i) { if((namelen == dynhds->hds[i]->namelen) && - strncasecompare(name, dynhds->hds[i]->name, namelen)) + curl_strnequal(name, dynhds->hds[i]->name, namelen)) ++n; } } @@ -325,7 +325,7 @@ size_t Curl_dynhds_remove(struct dynhds *dynhds, size_t i, len; for(i = 0; i < dynhds->hds_len; ++i) { if((namelen == dynhds->hds[i]->namelen) && - strncasecompare(name, dynhds->hds[i]->name, namelen)) { + curl_strnequal(name, dynhds->hds[i]->name, namelen)) { ++n; --dynhds->hds_len; dynhds->strs_len -= (dynhds->hds[i]->namelen + diff --git a/lib/easy.c b/lib/easy.c index 3f86786252..9c1f1c6969 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -67,6 +67,7 @@ #include "amigaos.h" #include "macos.h" #include "curlx/warnless.h" +#include "curlx/wait.h" #include "sigpipe.h" #include "vssh/ssh.h" #include "setopt.h" @@ -128,9 +129,6 @@ curl_free_callback Curl_cfree = (curl_free_callback)free; curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc; curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup; curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; -#if defined(_WIN32) && defined(UNICODE) -curl_wcsdup_callback Curl_cwcsdup = Curl_wcsdup; -#endif #if defined(_MSC_VER) && defined(_DLL) # pragma warning(pop) @@ -156,9 +154,6 @@ static CURLcode global_init(long flags, bool memoryfuncs) Curl_crealloc = (curl_realloc_callback)realloc; Curl_cstrdup = (curl_strdup_callback)system_strdup; Curl_ccalloc = (curl_calloc_callback)calloc; -#if defined(_WIN32) && defined(UNICODE) - Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; -#endif } if(Curl_trc_init()) { @@ -611,7 +606,7 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) #endif pollrc = 0; if(ev->ms > 0) - Curl_wait_ms(ev->ms); + curlx_wait_ms(ev->ms); } ev->msbump = FALSE; /* reset here */ @@ -733,7 +728,7 @@ static CURLcode easy_transfer(struct Curl_multi *multi) /* - * easy_perform() is the external interface that performs a blocking + * easy_perform() is the internal interface that performs a blocking * transfer as previously setup. * * CONCEPT: This function creates a multi handle, adds the easy handle to it, @@ -1128,13 +1123,12 @@ void curl_easy_reset(CURL *d) */ CURLcode curl_easy_pause(CURL *d, int action) { - struct SingleRequest *k; CURLcode result = CURLE_OK; - int oldstate; - int newstate; bool recursive = FALSE; - bool keep_changed, unpause_read, not_all_paused; + bool changed = FALSE; struct Curl_easy *data = d; + bool recv_paused, recv_paused_new; + bool send_paused, send_paused_new; if(!GOOD_EASY_HANDLE(data) || !data->conn) /* crazy input, do not continue */ @@ -1142,62 +1136,37 @@ CURLcode curl_easy_pause(CURL *d, int action) if(Curl_is_in_callback(data)) recursive = TRUE; - k = &data->req; - oldstate = k->keepon & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE); - /* first switch off both pause bits then set the new pause bits */ - newstate = (k->keepon &~ (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) | - ((action & CURLPAUSE_RECV) ? KEEP_RECV_PAUSE : 0) | - ((action & CURLPAUSE_SEND) ? KEEP_SEND_PAUSE : 0); + recv_paused = Curl_xfer_recv_is_paused(data); + recv_paused_new = (action & CURLPAUSE_RECV); + send_paused = Curl_xfer_send_is_paused(data); + send_paused_new = (action & CURLPAUSE_SEND); - keep_changed = ((newstate & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) != oldstate); - not_all_paused = (newstate & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) != - (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE); - unpause_read = ((k->keepon & ~newstate & KEEP_SEND_PAUSE) && - (data->mstate == MSTATE_PERFORMING || - data->mstate == MSTATE_RATELIMITING)); - /* Unpausing writes is detected on the next run in - * transfer.c:Curl_sendrecv(). This is because this may result - * in a transfer error if the application's callbacks fail */ + if(send_paused != send_paused_new) { + changed = TRUE; + result = Curl_1st_err(result, Curl_xfer_pause_send(data, send_paused_new)); + } - /* Set the new keepon state, so it takes effect no matter what error - * may happen afterwards. */ - k->keepon = newstate; + if(recv_paused != recv_paused_new) { + changed = TRUE; + result = Curl_1st_err(result, Curl_xfer_pause_recv(data, recv_paused_new)); + } /* If not completely pausing both directions now, run again in any case. */ - if(not_all_paused) { + if(!Curl_xfer_is_blocked(data)) { Curl_expire(data, 0, EXPIRE_RUN_NOW); /* reset the too-slow time keeper */ data->state.keeps_speed.tv_sec = 0; - /* Simulate socket events on next run for unpaused directions */ - if(!(newstate & KEEP_SEND_PAUSE)) - data->state.select_bits |= CURL_CSELECT_OUT; - if(!(newstate & KEEP_RECV_PAUSE)) - data->state.select_bits |= CURL_CSELECT_IN; /* On changes, tell application to update its timers. */ - if(keep_changed && data->multi) { - if(Curl_update_timer(data->multi)) { + if(changed && data->multi) { + if(Curl_update_timer(data->multi) && !result) result = CURLE_ABORTED_BY_CALLBACK; - goto out; - } } } - if(unpause_read) { - result = Curl_creader_unpause(data); - if(result) - goto out; - } - - if(!(k->keepon & KEEP_RECV_PAUSE) && Curl_cwriter_is_paused(data)) { - Curl_conn_ev_data_pause(data, FALSE); - result = Curl_cwriter_unpause(data); - } - -out: - if(!result && !data->state.done && keep_changed && data->multi) + if(!result && changed && !data->state.done && data->multi) /* pause/unpausing may result in multi event changes */ - if(Curl_multi_ev_assess_xfer(data->multi, data)) + if(Curl_multi_ev_assess_xfer(data->multi, data) && !result) result = CURLE_ABORTED_BY_CALLBACK; if(recursive) @@ -1241,7 +1210,6 @@ static CURLcode easy_connection(struct Curl_easy *data, CURLcode curl_easy_recv(CURL *d, void *buffer, size_t buflen, size_t *n) { CURLcode result; - ssize_t n1; struct connectdata *c; struct Curl_easy *data = d; @@ -1258,13 +1226,7 @@ CURLcode curl_easy_recv(CURL *d, void *buffer, size_t buflen, size_t *n) Curl_attach_connection(data, c); *n = 0; - result = Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, &n1); - - if(result) - return result; - - *n = (size_t)n1; - return CURLE_OK; + return Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, n); } #ifndef CURL_DISABLE_WEBSOCKETS diff --git a/lib/easygetopt.c b/lib/easygetopt.c index 5d30d39a49..0113121632 100644 --- a/lib/easygetopt.c +++ b/lib/easygetopt.c @@ -1,9 +1,9 @@ /*************************************************************************** * _ _ ____ _ - * Project ___| | | | _ | | + * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ - * ___|___/|_| ______| + * \___|\___/|_| \_\_____| * * Copyright (C) Daniel Stenberg, , et al. * @@ -23,7 +23,6 @@ ***************************************************************************/ #include "curl_setup.h" -#include "strcase.h" #include "easyoptions.h" #ifndef CURL_DISABLE_GETOPTIONS @@ -37,7 +36,7 @@ static const struct curl_easyoption *lookup(const char *name, CURLoption id) const struct curl_easyoption *o = &Curl_easyopts[0]; do { if(name) { - if(strcasecompare(o->name, name)) + if(curl_strequal(o->name, name)) return o; } else { diff --git a/lib/escape.c b/lib/escape.c index 3cd906dc6c..a292ba3b62 100644 --- a/lib/escape.c +++ b/lib/escape.c @@ -85,7 +85,7 @@ char *curl_easy_escape(CURL *data, const char *string, else { /* encode it */ unsigned char out[3]={'%'}; - Curl_hexbyte(&out[1], in, FALSE); + Curl_hexbyte(&out[1], in); if(curlx_dyn_addn(&d, out, 3)) return NULL; } @@ -212,7 +212,8 @@ void Curl_hexencode(const unsigned char *src, size_t len, /* input length */ DEBUGASSERT(src && len && (olen >= 3)); if(src && len && (olen >= 3)) { while(len-- && (olen >= 3)) { - Curl_hexbyte(out, *src, TRUE); + out[0] = Curl_ldigits[*src >> 4]; + out[1] = Curl_ldigits[*src & 0x0F]; ++src; out += 2; olen -= 2; @@ -225,14 +226,11 @@ void Curl_hexencode(const unsigned char *src, size_t len, /* input length */ /* Curl_hexbyte * - * Output a single unsigned char as a two-digit hex number, lowercase or - * uppercase + * Output a single unsigned char as a two-digit UPPERCASE hex number. */ void Curl_hexbyte(unsigned char *dest, /* must fit two bytes */ - unsigned char val, - bool lowercase) + unsigned char val) { - const unsigned char *t = lowercase ? Curl_ldigits : Curl_udigits; - dest[0] = t[val >> 4]; - dest[1] = t[val & 0x0F]; + dest[0] = Curl_udigits[val >> 4]; + dest[1] = Curl_udigits[val & 0x0F]; } diff --git a/lib/escape.h b/lib/escape.h index 1f2bac8fac..a43fc38ed3 100644 --- a/lib/escape.h +++ b/lib/escape.h @@ -42,7 +42,6 @@ void Curl_hexencode(const unsigned char *src, size_t len, /* input length */ unsigned char *out, size_t olen); /* output buffer size */ void Curl_hexbyte(unsigned char *dest, /* must fit two bytes */ - unsigned char val, - bool lowercase); + unsigned char val); #endif /* HEADER_CURL_ESCAPE_H */ diff --git a/lib/formdata.c b/lib/formdata.c index 2aa8eee94a..5a4b52ea57 100644 --- a/lib/formdata.c +++ b/lib/formdata.c @@ -34,7 +34,6 @@ struct Curl_easy; #include "urldata.h" /* for struct Curl_easy */ #include "mime.h" #include "vtls/vtls.h" -#include "strcase.h" #include "sendf.h" #include "strdup.h" #include "rand.h" diff --git a/lib/ftp.c b/lib/ftp.c index 2c6138e980..06c486bc91 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -60,7 +60,7 @@ #include "cf-socket.h" #include "connect.h" #include "strerror.h" -#include "inet_ntop.h" +#include "curlx/inet_ntop.h" #include "curlx/inet_pton.h" #include "select.h" #include "parsedate.h" /* for the week day and month names */ @@ -114,12 +114,14 @@ static const char * const ftp_state_names[]={ "QUOTE", "RETR_PREQUOTE", "STOR_PREQUOTE", + "LIST_PREQUOTE", "POSTQUOTE", "CWD", "MKD", "MDTM", "TYPE", "LIST_TYPE", + "RETR_LIST_TYPE", "RETR_TYPE", "STOR_TYPE", "SIZE", @@ -765,7 +767,12 @@ static CURLcode ftp_state_user(struct Curl_easy *data, static CURLcode ftp_state_pwd(struct Curl_easy *data, struct ftp_conn *ftpc) { - CURLcode result = Curl_pp_sendf(data, &ftpc->pp, "%s", "PWD"); + CURLcode result; +#ifdef DEBUGBUILD + if(!data->id && getenv("CURL_FTP_PWD_STOP")) + return CURLE_OK; +#endif + result = Curl_pp_sendf(data, &ftpc->pp, "%s", "PWD"); if(!result) ftp_state(data, ftpc, FTP_PWD); @@ -978,6 +985,12 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, port_min = port_max = 0; if(addrlen) { + const struct Curl_sockaddr_ex *remote_addr = + Curl_conn_get_remote_addr(data, FIRSTSOCKET); + + DEBUGASSERT(remote_addr); + if(!remote_addr) + goto out; DEBUGASSERT(addr); if(addrlen >= sizeof(ipstr)) goto out; @@ -985,9 +998,9 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, ipstr[addrlen] = 0; /* attempt to get the address of the given interface name */ - switch(Curl_if2ip(conn->remote_addr->family, + switch(Curl_if2ip(remote_addr->family, #ifdef USE_IPV6 - Curl_ipv6_scope(&conn->remote_addr->curl_sa_addr), + Curl_ipv6_scope(&remote_addr->curl_sa_addr), conn->scope_id, #endif ipstr, hbuf, sizeof(hbuf))) { @@ -1020,11 +1033,11 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, switch(sa->sa_family) { #ifdef USE_IPV6 case AF_INET6: - r = Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf)); + r = curlx_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf)); break; #endif default: - r = Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf)); + r = curlx_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf)); break; } if(!r) { @@ -1052,7 +1065,8 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data, /* step 2, create a socket for the requested address */ error = 0; for(ai = res; ai; ai = ai->ai_next) { - if(Curl_socket_open(data, ai, NULL, conn->transport, &portsock)) { + if(Curl_socket_open(data, ai, NULL, + Curl_conn_get_transport(data, conn), &portsock)) { error = SOCKERRNO; continue; } @@ -1245,7 +1259,7 @@ out: } data->conn->bits.do_more = FALSE; Curl_pgrsTime(data, TIMER_STARTACCEPT); - Curl_expire(data, data->set.accepttimeout ? + Curl_expire(data, (data->set.accepttimeout > 0) ? data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT, EXPIRE_FTP_ACCEPT); } @@ -1448,6 +1462,14 @@ static CURLcode ftp_state_list(struct Curl_easy *data, return result; } +static CURLcode ftp_state_list_prequote(struct Curl_easy *data, + struct ftp_conn *ftpc, + struct FTP *ftp) +{ + /* We have sent the TYPE, now we must send the list of prequote strings */ + return ftp_state_quote(data, ftpc, ftp, TRUE, FTP_LIST_PREQUOTE); +} + static CURLcode ftp_state_retr_prequote(struct Curl_easy *data, struct ftp_conn *ftpc, struct FTP *ftp) @@ -1636,6 +1658,7 @@ static CURLcode ftp_state_quote(struct Curl_easy *data, break; case FTP_RETR_PREQUOTE: case FTP_STOR_PREQUOTE: + case FTP_LIST_PREQUOTE: item = data->set.prequote; break; case FTP_POSTQUOTE: @@ -1725,6 +1748,10 @@ static CURLcode ftp_state_quote(struct Curl_easy *data, break; case FTP_POSTQUOTE: break; + case FTP_LIST_PREQUOTE: + ftp_state(data, ftpc, FTP_LIST_TYPE); + result = ftp_state_list(data, ftpc, ftp); + break; } } @@ -2198,6 +2225,8 @@ static CURLcode ftp_state_type_resp(struct Curl_easy *data, result = ftp_state_retr_prequote(data, ftpc, ftp); else if(instate == FTP_STOR_TYPE) result = ftp_state_stor_prequote(data, ftpc, ftp); + else if(instate == FTP_RETR_LIST_TYPE) + result = ftp_state_list_prequote(data, ftpc, ftp); return result; } @@ -2961,7 +2990,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; /* Check for special servers here. */ - if(strcasecompare(os, "OS/400")) { + if(curl_strequal(os, "OS/400")) { /* Force OS400 name format 1. */ result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1"); if(result) { @@ -3002,6 +3031,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, case FTP_POSTQUOTE: case FTP_RETR_PREQUOTE: case FTP_STOR_PREQUOTE: + case FTP_LIST_PREQUOTE: if((ftpcode >= 400) && !ftpc->count2) { /* failure response code, and not allowed to fail */ failf(data, "QUOT command failed with %03d", ftpcode); @@ -3071,6 +3101,7 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, case FTP_LIST_TYPE: case FTP_RETR_TYPE: case FTP_STOR_TYPE: + case FTP_RETR_LIST_TYPE: result = ftp_state_type_resp(data, ftpc, ftp, ftpcode, ftpc->state); break; @@ -3125,8 +3156,8 @@ static CURLcode ftp_pp_statemachine(struct Curl_easy *data, /* called repeatedly until done from multi.c */ static CURLcode ftp_statemach(struct Curl_easy *data, - struct ftp_conn *ftpc, - bool *done) + struct ftp_conn *ftpc, + bool *done) { CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE); @@ -3675,7 +3706,8 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) if(result) ; - else if(data->state.list_only || !ftpc->file) { + else if((data->state.list_only || !ftpc->file) && + !(data->set.prequote)) { /* The specified path ends with a slash, and therefore we think this is a directory that is requested, use LIST. But before that we need to set ASCII transfer mode. */ @@ -3689,8 +3721,14 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep) /* otherwise just fall through */ } else { - result = ftp_nb_type(data, ftpc, ftp, data->state.prefer_ascii, - FTP_RETR_TYPE); + if(data->set.prequote && !ftpc->file) { + result = ftp_nb_type(data, ftpc, ftp, TRUE, + FTP_RETR_LIST_TYPE); + } + else { + result = ftp_nb_type(data, ftpc, ftp, data->state.prefer_ascii, + FTP_RETR_TYPE); + } if(result) return result; } @@ -4117,7 +4155,7 @@ static CURLcode ftp_disconnect(struct Curl_easy *data, will try to send the QUIT command, otherwise it will just return. */ ftpc->shutdown = TRUE; - if(dead_connection) + if(dead_connection || Curl_pp_needs_flush(data, &ftpc->pp)) ftpc->ctl_valid = FALSE; /* The FTP session may or may not have been allocated/setup at this point! */ diff --git a/lib/ftp.h b/lib/ftp.h index c31aa93286..52661981ef 100644 --- a/lib/ftp.h +++ b/lib/ftp.h @@ -62,12 +62,14 @@ enum { FTP_QUOTE, /* waiting for a response to a command sent in a quote list */ FTP_RETR_PREQUOTE, FTP_STOR_PREQUOTE, + FTP_LIST_PREQUOTE, FTP_POSTQUOTE, FTP_CWD, /* change dir */ FTP_MKD, /* if the dir did not exist */ FTP_MDTM, /* to figure out the datestamp */ FTP_TYPE, /* to set type when doing a head-like request */ FTP_LIST_TYPE, /* set type when about to do a dir list */ + FTP_RETR_LIST_TYPE, FTP_RETR_TYPE, /* set type when about to RETR a file */ FTP_STOR_TYPE, /* set type when about to STOR a file */ FTP_SIZE, /* get the remote file's size for head-like request */ diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c index 70939d6a76..360f7ae4fc 100644 --- a/lib/ftplistparser.c +++ b/lib/ftplistparser.c @@ -396,407 +396,500 @@ static CURLcode unix_filetype(const char c, curlfiletype *t) return CURLE_OK; } +static CURLcode parse_unix_totalsize(struct ftp_parselist_data *parser, + struct fileinfo *infop, + const char c) +{ + size_t len = curlx_dyn_len(&infop->buf); + char *mem = curlx_dyn_ptr(&infop->buf); + switch(parser->state.UNIX.sub.total_dirsize) { + case PL_UNIX_TOTALSIZE_INIT: + if(c == 't') { + parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING; + parser->item_length++; + } + else { + parser->state.UNIX.main = PL_UNIX_FILETYPE; + /* continue to fall through */ + } + break; + case PL_UNIX_TOTALSIZE_READING: + parser->item_length++; + if(c == '\r') { + parser->item_length--; + if(len) + curlx_dyn_setlen(&infop->buf, --len); + } + else if(c == '\n') { + mem[parser->item_length - 1] = 0; + if(!strncmp("total ", mem, 6)) { + const char *endptr = mem + 6; + /* here we can deal with directory size, pass the leading + whitespace and then the digits */ + curlx_str_passblanks(&endptr); + while(ISDIGIT(*endptr)) + endptr++; + if(*endptr) { + return CURLE_FTP_BAD_FILE_LIST; + } + parser->state.UNIX.main = PL_UNIX_FILETYPE; + curlx_dyn_reset(&infop->buf); + } + else + return CURLE_FTP_BAD_FILE_LIST; + + } + break; + } + return CURLE_OK; +} + +static CURLcode parse_unix_permission(struct ftp_parselist_data *parser, + struct fileinfo *infop, + const char c) +{ + char *mem = curlx_dyn_ptr(&infop->buf); + parser->item_length++; + if((parser->item_length <= 9) && !strchr("rwx-tTsS", c)) + return CURLE_FTP_BAD_FILE_LIST; + + else if(parser->item_length == 10) { + unsigned int perm; + if(c != ' ') + return CURLE_FTP_BAD_FILE_LIST; + + mem[10] = 0; /* terminate permissions */ + perm = ftp_pl_get_permission(mem + parser->item_offset); + if(perm & FTP_LP_MALFORMATED_PERM) + return CURLE_FTP_BAD_FILE_LIST; + + parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM; + parser->file_data->info.perm = perm; + parser->offsets.perm = parser->item_offset; + + parser->item_length = 0; + parser->state.UNIX.main = PL_UNIX_HLINKS; + parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE; + } + return CURLE_OK; +} + +static CURLcode parse_unix_hlinks(struct ftp_parselist_data *parser, + struct fileinfo *infop, + const char c) +{ + size_t len = curlx_dyn_len(&infop->buf); + char *mem = curlx_dyn_ptr(&infop->buf); + + switch(parser->state.UNIX.sub.hlinks) { + case PL_UNIX_HLINKS_PRESPACE: + if(c != ' ') { + if(ISDIGIT(c) && len) { + parser->item_offset = len - 1; + parser->item_length = 1; + parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER; + } + else + return CURLE_FTP_BAD_FILE_LIST; + } + break; + case PL_UNIX_HLINKS_NUMBER: + parser->item_length ++; + if(c == ' ') { + const char *p = &mem[parser->item_offset]; + curl_off_t hlinks; + mem[parser->item_offset + parser->item_length - 1] = 0; + + if(!curlx_str_number(&p, &hlinks, LONG_MAX)) { + parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT; + parser->file_data->info.hardlinks = (long)hlinks; + } + parser->item_length = 0; + parser->item_offset = 0; + parser->state.UNIX.main = PL_UNIX_USER; + parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE; + } + else if(!ISDIGIT(c)) + return CURLE_FTP_BAD_FILE_LIST; + + break; + } + return CURLE_OK; +} + +static CURLcode parse_unix_user(struct ftp_parselist_data *parser, + struct fileinfo *infop, + const char c) +{ + size_t len = curlx_dyn_len(&infop->buf); + char *mem = curlx_dyn_ptr(&infop->buf); + switch(parser->state.UNIX.sub.user) { + case PL_UNIX_USER_PRESPACE: + if(c != ' ' && len) { + parser->item_offset = len - 1; + parser->item_length = 1; + parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING; + } + break; + case PL_UNIX_USER_PARSING: + parser->item_length++; + if(c == ' ') { + mem[parser->item_offset + parser->item_length - 1] = 0; + parser->offsets.user = parser->item_offset; + parser->state.UNIX.main = PL_UNIX_GROUP; + parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE; + parser->item_offset = 0; + parser->item_length = 0; + } + break; + } + return CURLE_OK; +} + +static CURLcode parse_unix_group(struct ftp_parselist_data *parser, + struct fileinfo *infop, + const char c) +{ + size_t len = curlx_dyn_len(&infop->buf); + char *mem = curlx_dyn_ptr(&infop->buf); + switch(parser->state.UNIX.sub.group) { + case PL_UNIX_GROUP_PRESPACE: + if(c != ' ' && len) { + parser->item_offset = len - 1; + parser->item_length = 1; + parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME; + } + break; + case PL_UNIX_GROUP_NAME: + parser->item_length++; + if(c == ' ') { + mem[parser->item_offset + parser->item_length - 1] = 0; + parser->offsets.group = parser->item_offset; + parser->state.UNIX.main = PL_UNIX_SIZE; + parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE; + parser->item_offset = 0; + parser->item_length = 0; + } + break; + } + return CURLE_OK; +} + +static CURLcode parse_unix_size(struct ftp_parselist_data *parser, + struct fileinfo *infop, + const char c) +{ + size_t len = curlx_dyn_len(&infop->buf); + char *mem = curlx_dyn_ptr(&infop->buf); + switch(parser->state.UNIX.sub.size) { + case PL_UNIX_SIZE_PRESPACE: + if(c != ' ') { + if(ISDIGIT(c) && len) { + parser->item_offset = len - 1; + parser->item_length = 1; + parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER; + } + else + return CURLE_FTP_BAD_FILE_LIST; + } + break; + case PL_UNIX_SIZE_NUMBER: + parser->item_length++; + if(c == ' ') { + const char *p = mem + parser->item_offset; + curl_off_t fsize; + mem[parser->item_offset + parser->item_length - 1] = 0; + if(!curlx_str_numblanks(&p, &fsize)) { + if(p[0] == '\0' && fsize != CURL_OFF_T_MAX) { + parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE; + parser->file_data->info.size = fsize; + } + parser->item_length = 0; + parser->item_offset = 0; + parser->state.UNIX.main = PL_UNIX_TIME; + parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1; + } + } + else if(!ISDIGIT(c)) + return CURLE_FTP_BAD_FILE_LIST; + + break; + } + return CURLE_OK; +} + +static CURLcode parse_unix_time(struct ftp_parselist_data *parser, + struct fileinfo *infop, + const char c) +{ + size_t len = curlx_dyn_len(&infop->buf); + char *mem = curlx_dyn_ptr(&infop->buf); + struct curl_fileinfo *finfo = &infop->info; + + switch(parser->state.UNIX.sub.time) { + case PL_UNIX_TIME_PREPART1: + if(c != ' ') { + if(ISALNUM(c) && len) { + parser->item_offset = len -1; + parser->item_length = 1; + parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1; + } + else + return CURLE_FTP_BAD_FILE_LIST; + } + break; + case PL_UNIX_TIME_PART1: + parser->item_length++; + if(c == ' ') + parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2; + + else if(!ISALNUM(c) && c != '.') + return CURLE_FTP_BAD_FILE_LIST; + + break; + case PL_UNIX_TIME_PREPART2: + parser->item_length++; + if(c != ' ') { + if(ISALNUM(c)) + parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2; + else + return CURLE_FTP_BAD_FILE_LIST; + } + break; + case PL_UNIX_TIME_PART2: + parser->item_length++; + if(c == ' ') + parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3; + else if(!ISALNUM(c) && c != '.') + return CURLE_FTP_BAD_FILE_LIST; + break; + case PL_UNIX_TIME_PREPART3: + parser->item_length++; + if(c != ' ') { + if(ISALNUM(c)) + parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3; + else + return CURLE_FTP_BAD_FILE_LIST; + } + break; + case PL_UNIX_TIME_PART3: + parser->item_length++; + if(c == ' ') { + mem[parser->item_offset + parser->item_length -1] = 0; + parser->offsets.time = parser->item_offset; + if(finfo->filetype == CURLFILETYPE_SYMLINK) { + parser->state.UNIX.main = PL_UNIX_SYMLINK; + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE; + } + else { + parser->state.UNIX.main = PL_UNIX_FILENAME; + parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE; + } + } + else if(!ISALNUM(c) && c != '.' && c != ':') + return CURLE_FTP_BAD_FILE_LIST; + break; + } + return CURLE_OK; +} + +static CURLcode parse_unix_filename(struct Curl_easy *data, + struct ftp_parselist_data *parser, + struct fileinfo *infop, + const char c) +{ + size_t len = curlx_dyn_len(&infop->buf); + char *mem = curlx_dyn_ptr(&infop->buf); + CURLcode result = CURLE_OK; + + switch(parser->state.UNIX.sub.filename) { + case PL_UNIX_FILENAME_PRESPACE: + if(c != ' ' && len) { + parser->item_offset = len - 1; + parser->item_length = 1; + parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME; + } + break; + case PL_UNIX_FILENAME_NAME: + parser->item_length++; + if(c == '\r') + parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL; + + else if(c == '\n') { + mem[parser->item_offset + parser->item_length - 1] = 0; + parser->offsets.filename = parser->item_offset; + parser->state.UNIX.main = PL_UNIX_FILETYPE; + result = ftp_pl_insert_finfo(data, infop); + } + break; + case PL_UNIX_FILENAME_WINDOWSEOL: + if(c == '\n') { + mem[parser->item_offset + parser->item_length - 1] = 0; + parser->offsets.filename = parser->item_offset; + parser->state.UNIX.main = PL_UNIX_FILETYPE; + result = ftp_pl_insert_finfo(data, infop); + } + else + result = CURLE_FTP_BAD_FILE_LIST; + break; + } + return result; +} + +static CURLcode parse_unix_symlink(struct Curl_easy *data, + struct ftp_parselist_data *parser, + struct fileinfo *infop, + const char c) +{ + size_t len = curlx_dyn_len(&infop->buf); + char *mem = curlx_dyn_ptr(&infop->buf); + CURLcode result = CURLE_OK; + + switch(parser->state.UNIX.sub.symlink) { + case PL_UNIX_SYMLINK_PRESPACE: + if(c != ' ' && len) { + parser->item_offset = len - 1; + parser->item_length = 1; + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; + } + break; + case PL_UNIX_SYMLINK_NAME: + parser->item_length++; + if(c == ' ') + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1; + + else if(c == '\r' || c == '\n') + return CURLE_FTP_BAD_FILE_LIST; + + break; + case PL_UNIX_SYMLINK_PRETARGET1: + parser->item_length++; + if(c == '-') + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2; + + else if(c == '\r' || c == '\n') + return CURLE_FTP_BAD_FILE_LIST; + else + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; + break; + case PL_UNIX_SYMLINK_PRETARGET2: + parser->item_length++; + if(c == '>') + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3; + else if(c == '\r' || c == '\n') + return CURLE_FTP_BAD_FILE_LIST; + else + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; + + break; + case PL_UNIX_SYMLINK_PRETARGET3: + parser->item_length++; + if(c == ' ') { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4; + /* now place where is symlink following */ + mem[parser->item_offset + parser->item_length - 4] = 0; + parser->offsets.filename = parser->item_offset; + parser->item_length = 0; + parser->item_offset = 0; + } + else if(c == '\r' || c == '\n') + return CURLE_FTP_BAD_FILE_LIST; + else + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; + break; + case PL_UNIX_SYMLINK_PRETARGET4: + if(c != '\r' && c != '\n' && len) { + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET; + parser->item_offset = len - 1; + parser->item_length = 1; + } + else + return CURLE_FTP_BAD_FILE_LIST; + + break; + case PL_UNIX_SYMLINK_TARGET: + parser->item_length++; + if(c == '\r') + parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL; + + else if(c == '\n') { + mem[parser->item_offset + parser->item_length - 1] = 0; + parser->offsets.symlink_target = parser->item_offset; + result = ftp_pl_insert_finfo(data, infop); + if(result) + break; + + parser->state.UNIX.main = PL_UNIX_FILETYPE; + } + break; + case PL_UNIX_SYMLINK_WINDOWSEOL: + if(c == '\n') { + mem[parser->item_offset + parser->item_length - 1] = 0; + parser->offsets.symlink_target = parser->item_offset; + result = ftp_pl_insert_finfo(data, infop); + if(result) + break; + + parser->state.UNIX.main = PL_UNIX_FILETYPE; + } + else + result = CURLE_FTP_BAD_FILE_LIST; + + break; + } + return result; +} + static CURLcode parse_unix(struct Curl_easy *data, struct ftp_parselist_data *parser, struct fileinfo *infop, const char c) { struct curl_fileinfo *finfo = &infop->info; - size_t len = curlx_dyn_len(&infop->buf); - char *mem = curlx_dyn_ptr(&infop->buf); CURLcode result = CURLE_OK; switch(parser->state.UNIX.main) { case PL_UNIX_TOTALSIZE: - switch(parser->state.UNIX.sub.total_dirsize) { - case PL_UNIX_TOTALSIZE_INIT: - if(c == 't') { - parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING; - parser->item_length++; - } - else { - parser->state.UNIX.main = PL_UNIX_FILETYPE; - /* continue to fall through */ - } + result = parse_unix_totalsize(parser, infop, c); + if(result) break; - case PL_UNIX_TOTALSIZE_READING: - parser->item_length++; - if(c == '\r') { - parser->item_length--; - if(len) - curlx_dyn_setlen(&infop->buf, --len); - } - else if(c == '\n') { - mem[parser->item_length - 1] = 0; - if(!strncmp("total ", mem, 6)) { - const char *endptr = mem + 6; - /* here we can deal with directory size, pass the leading - whitespace and then the digits */ - curlx_str_passblanks(&endptr); - while(ISDIGIT(*endptr)) - endptr++; - if(*endptr) { - return CURLE_FTP_BAD_FILE_LIST; - } - parser->state.UNIX.main = PL_UNIX_FILETYPE; - curlx_dyn_reset(&infop->buf); - } - else - return CURLE_FTP_BAD_FILE_LIST; - - } - break; - } if(parser->state.UNIX.main != PL_UNIX_FILETYPE) break; FALLTHROUGH(); case PL_UNIX_FILETYPE: result = unix_filetype(c, &finfo->filetype); - if(result) - return result; - parser->state.UNIX.main = PL_UNIX_PERMISSION; - parser->item_length = 0; - parser->item_offset = 1; + if(!result) { + parser->state.UNIX.main = PL_UNIX_PERMISSION; + parser->item_length = 0; + parser->item_offset = 1; + } break; case PL_UNIX_PERMISSION: - parser->item_length++; - if((parser->item_length <= 9) && !strchr("rwx-tTsS", c)) - return CURLE_FTP_BAD_FILE_LIST; - - else if(parser->item_length == 10) { - unsigned int perm; - if(c != ' ') - return CURLE_FTP_BAD_FILE_LIST; - - mem[10] = 0; /* terminate permissions */ - perm = ftp_pl_get_permission(mem + parser->item_offset); - if(perm & FTP_LP_MALFORMATED_PERM) - return CURLE_FTP_BAD_FILE_LIST; - - parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM; - parser->file_data->info.perm = perm; - parser->offsets.perm = parser->item_offset; - - parser->item_length = 0; - parser->state.UNIX.main = PL_UNIX_HLINKS; - parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE; - } + result = parse_unix_permission(parser, infop, c); break; case PL_UNIX_HLINKS: - switch(parser->state.UNIX.sub.hlinks) { - case PL_UNIX_HLINKS_PRESPACE: - if(c != ' ') { - if(ISDIGIT(c) && len) { - parser->item_offset = len - 1; - parser->item_length = 1; - parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER; - } - else - return CURLE_FTP_BAD_FILE_LIST; - } - break; - case PL_UNIX_HLINKS_NUMBER: - parser->item_length ++; - if(c == ' ') { - const char *p = &mem[parser->item_offset]; - curl_off_t hlinks; - mem[parser->item_offset + parser->item_length - 1] = 0; - - if(!curlx_str_number(&p, &hlinks, LONG_MAX)) { - parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT; - parser->file_data->info.hardlinks = (long)hlinks; - } - parser->item_length = 0; - parser->item_offset = 0; - parser->state.UNIX.main = PL_UNIX_USER; - parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE; - } - else if(!ISDIGIT(c)) - return CURLE_FTP_BAD_FILE_LIST; - - break; - } + result = parse_unix_hlinks(parser, infop, c); break; case PL_UNIX_USER: - switch(parser->state.UNIX.sub.user) { - case PL_UNIX_USER_PRESPACE: - if(c != ' ' && len) { - parser->item_offset = len - 1; - parser->item_length = 1; - parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING; - } - break; - case PL_UNIX_USER_PARSING: - parser->item_length++; - if(c == ' ') { - mem[parser->item_offset + parser->item_length - 1] = 0; - parser->offsets.user = parser->item_offset; - parser->state.UNIX.main = PL_UNIX_GROUP; - parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE; - parser->item_offset = 0; - parser->item_length = 0; - } - break; - } + result = parse_unix_user(parser, infop, c); break; case PL_UNIX_GROUP: - switch(parser->state.UNIX.sub.group) { - case PL_UNIX_GROUP_PRESPACE: - if(c != ' ' && len) { - parser->item_offset = len - 1; - parser->item_length = 1; - parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME; - } - break; - case PL_UNIX_GROUP_NAME: - parser->item_length++; - if(c == ' ') { - mem[parser->item_offset + parser->item_length - 1] = 0; - parser->offsets.group = parser->item_offset; - parser->state.UNIX.main = PL_UNIX_SIZE; - parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE; - parser->item_offset = 0; - parser->item_length = 0; - } - break; - } + result = parse_unix_group(parser, infop, c); break; case PL_UNIX_SIZE: - switch(parser->state.UNIX.sub.size) { - case PL_UNIX_SIZE_PRESPACE: - if(c != ' ') { - if(ISDIGIT(c) && len) { - parser->item_offset = len - 1; - parser->item_length = 1; - parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER; - } - else - return CURLE_FTP_BAD_FILE_LIST; - } - break; - case PL_UNIX_SIZE_NUMBER: - parser->item_length++; - if(c == ' ') { - const char *p = mem + parser->item_offset; - curl_off_t fsize; - mem[parser->item_offset + parser->item_length - 1] = 0; - if(!curlx_str_numblanks(&p, &fsize)) { - if(p[0] == '\0' && fsize != CURL_OFF_T_MAX) { - parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE; - parser->file_data->info.size = fsize; - } - parser->item_length = 0; - parser->item_offset = 0; - parser->state.UNIX.main = PL_UNIX_TIME; - parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1; - } - } - else if(!ISDIGIT(c)) - return CURLE_FTP_BAD_FILE_LIST; - - break; - } + result = parse_unix_size(parser, infop, c); break; case PL_UNIX_TIME: - switch(parser->state.UNIX.sub.time) { - case PL_UNIX_TIME_PREPART1: - if(c != ' ') { - if(ISALNUM(c) && len) { - parser->item_offset = len -1; - parser->item_length = 1; - parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1; - } - else - return CURLE_FTP_BAD_FILE_LIST; - } - break; - case PL_UNIX_TIME_PART1: - parser->item_length++; - if(c == ' ') - parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2; - - else if(!ISALNUM(c) && c != '.') - return CURLE_FTP_BAD_FILE_LIST; - - break; - case PL_UNIX_TIME_PREPART2: - parser->item_length++; - if(c != ' ') { - if(ISALNUM(c)) - parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2; - else - return CURLE_FTP_BAD_FILE_LIST; - } - break; - case PL_UNIX_TIME_PART2: - parser->item_length++; - if(c == ' ') - parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3; - else if(!ISALNUM(c) && c != '.') - return CURLE_FTP_BAD_FILE_LIST; - break; - case PL_UNIX_TIME_PREPART3: - parser->item_length++; - if(c != ' ') { - if(ISALNUM(c)) - parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3; - else - return CURLE_FTP_BAD_FILE_LIST; - } - break; - case PL_UNIX_TIME_PART3: - parser->item_length++; - if(c == ' ') { - mem[parser->item_offset + parser->item_length -1] = 0; - parser->offsets.time = parser->item_offset; - if(finfo->filetype == CURLFILETYPE_SYMLINK) { - parser->state.UNIX.main = PL_UNIX_SYMLINK; - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE; - } - else { - parser->state.UNIX.main = PL_UNIX_FILENAME; - parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE; - } - } - else if(!ISALNUM(c) && c != '.' && c != ':') - return CURLE_FTP_BAD_FILE_LIST; - break; - } + result = parse_unix_time(parser, infop, c); break; case PL_UNIX_FILENAME: - switch(parser->state.UNIX.sub.filename) { - case PL_UNIX_FILENAME_PRESPACE: - if(c != ' ' && len) { - parser->item_offset = len - 1; - parser->item_length = 1; - parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME; - } - break; - case PL_UNIX_FILENAME_NAME: - parser->item_length++; - if(c == '\r') - parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL; - - else if(c == '\n') { - mem[parser->item_offset + parser->item_length - 1] = 0; - parser->offsets.filename = parser->item_offset; - parser->state.UNIX.main = PL_UNIX_FILETYPE; - result = ftp_pl_insert_finfo(data, infop); - if(result) - return result; - } - break; - case PL_UNIX_FILENAME_WINDOWSEOL: - if(c == '\n') { - mem[parser->item_offset + parser->item_length - 1] = 0; - parser->offsets.filename = parser->item_offset; - parser->state.UNIX.main = PL_UNIX_FILETYPE; - result = ftp_pl_insert_finfo(data, infop); - if(result) - return result; - } - else - return CURLE_FTP_BAD_FILE_LIST; - - break; - } + result = parse_unix_filename(data, parser, infop, c); break; case PL_UNIX_SYMLINK: - switch(parser->state.UNIX.sub.symlink) { - case PL_UNIX_SYMLINK_PRESPACE: - if(c != ' ' && len) { - parser->item_offset = len - 1; - parser->item_length = 1; - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; - } - break; - case PL_UNIX_SYMLINK_NAME: - parser->item_length++; - if(c == ' ') - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1; - - else if(c == '\r' || c == '\n') - return CURLE_FTP_BAD_FILE_LIST; - - break; - case PL_UNIX_SYMLINK_PRETARGET1: - parser->item_length++; - if(c == '-') - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2; - - else if(c == '\r' || c == '\n') - return CURLE_FTP_BAD_FILE_LIST; - else - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; - break; - case PL_UNIX_SYMLINK_PRETARGET2: - parser->item_length++; - if(c == '>') - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3; - else if(c == '\r' || c == '\n') - return CURLE_FTP_BAD_FILE_LIST; - else - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; - - break; - case PL_UNIX_SYMLINK_PRETARGET3: - parser->item_length++; - if(c == ' ') { - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4; - /* now place where is symlink following */ - mem[parser->item_offset + parser->item_length - 4] = 0; - parser->offsets.filename = parser->item_offset; - parser->item_length = 0; - parser->item_offset = 0; - } - else if(c == '\r' || c == '\n') - return CURLE_FTP_BAD_FILE_LIST; - else - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME; - break; - case PL_UNIX_SYMLINK_PRETARGET4: - if(c != '\r' && c != '\n' && len) { - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET; - parser->item_offset = len - 1; - parser->item_length = 1; - } - else - return CURLE_FTP_BAD_FILE_LIST; - - break; - case PL_UNIX_SYMLINK_TARGET: - parser->item_length++; - if(c == '\r') - parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL; - - else if(c == '\n') { - mem[parser->item_offset + parser->item_length - 1] = 0; - parser->offsets.symlink_target = parser->item_offset; - result = ftp_pl_insert_finfo(data, infop); - if(result) - return result; - - parser->state.UNIX.main = PL_UNIX_FILETYPE; - } - break; - case PL_UNIX_SYMLINK_WINDOWSEOL: - if(c == '\n') { - mem[parser->item_offset + parser->item_length - 1] = 0; - parser->offsets.symlink_target = parser->item_offset; - result = ftp_pl_insert_finfo(data, infop); - if(result) - return result; - - parser->state.UNIX.main = PL_UNIX_FILETYPE; - } - else - return CURLE_FTP_BAD_FILE_LIST; - - break; - } + result = parse_unix_symlink(data, parser, infop, c); break; } - return CURLE_OK; + return result; } static CURLcode parse_winnt(struct Curl_easy *data, diff --git a/lib/getinfo.c b/lib/getinfo.c index 388646bf75..7ff78d2d6d 100644 --- a/lib/getinfo.c +++ b/lib/getinfo.c @@ -28,6 +28,7 @@ #include "urldata.h" #include "getinfo.h" +#include "cfilters.h" #include "vtls/vtls.h" #include "connect.h" /* Curl_getconnectinfo() */ #include "progress.h" @@ -579,19 +580,14 @@ static CURLcode getinfo_slist(struct Curl_easy *data, CURLINFO info, struct curl_tlssessioninfo **tsip = (struct curl_tlssessioninfo **) param_slistp; struct curl_tlssessioninfo *tsi = &data->tsi; -#ifdef USE_SSL - struct connectdata *conn = data->conn; -#endif + /* we are exposing a pointer to internal memory with unknown + * lifetime here. */ *tsip = tsi; - tsi->backend = Curl_ssl_backend(); - tsi->internals = NULL; - -#ifdef USE_SSL - if(conn && tsi->backend != CURLSSLBACKEND_NONE) { - tsi->internals = Curl_ssl_get_internals(data, FIRSTSOCKET, info, 0); + if(!Curl_conn_get_ssl_info(data, data->conn, FIRSTSOCKET, tsi)) { + tsi->backend = Curl_ssl_backend(); + tsi->internals = NULL; } -#endif } break; default: diff --git a/lib/hash.c b/lib/hash.c index 8d13aae483..89f47ed71e 100644 --- a/lib/hash.c +++ b/lib/hash.c @@ -353,7 +353,7 @@ size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num) } size_t curlx_str_key_compare(void *k1, size_t key1_len, - void *k2, size_t key2_len) + void *k2, size_t key2_len) { if((key1_len == key2_len) && !memcmp(k1, k2, key1_len)) return 1; diff --git a/lib/hash.h b/lib/hash.h index 314b811e4a..fcd92db664 100644 --- a/lib/hash.h +++ b/lib/hash.h @@ -99,7 +99,7 @@ void Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user, int (*comp)(void *, void *)); size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num); size_t curlx_str_key_compare(void *k1, size_t key1_len, void *k2, - size_t key2_len); + size_t key2_len); void Curl_hash_start_iterate(struct Curl_hash *hash, struct Curl_hash_iterator *iter); struct Curl_hash_element * diff --git a/lib/headers.c b/lib/headers.c index 71592a9c73..426d0e57b7 100644 --- a/lib/headers.c +++ b/lib/headers.c @@ -26,7 +26,6 @@ #include "urldata.h" #include "strdup.h" -#include "strcase.h" #include "sendf.h" #include "headers.h" #include "curlx/strparse.h" @@ -88,7 +87,7 @@ CURLHcode curl_easy_header(CURL *easy, /* we need a first round to count amount of this header */ for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { hs = Curl_node_elem(e); - if(strcasecompare(hs->name, name) && + if(curl_strequal(hs->name, name) && (hs->type & type) && (hs->request == request)) { amount++; @@ -107,7 +106,7 @@ CURLHcode curl_easy_header(CURL *easy, else { for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { hs = Curl_node_elem(e); - if(strcasecompare(hs->name, name) && + if(curl_strequal(hs->name, name) && (hs->type & type) && (hs->request == request) && (match++ == nameindex)) { @@ -173,7 +172,7 @@ struct curl_header *curl_easy_nextheader(CURL *easy, the index for the currently selected entry */ for(e = Curl_llist_head(&data->state.httphdrs); e; e = Curl_node_next(e)) { struct Curl_header_store *check = Curl_node_elem(e); - if(strcasecompare(hs->name, check->name) && + if(curl_strequal(hs->name, check->name) && (check->request == request) && (check->type & type)) amount++; diff --git a/lib/hostip.c b/lib/hostip.c index ca6724ed55..191aaeb8de 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -54,7 +54,7 @@ #include "rand.h" #include "share.h" #include "url.h" -#include "inet_ntop.h" +#include "curlx/inet_ntop.h" #include "curlx/inet_pton.h" #include "multiif.h" #include "doh.h" @@ -145,14 +145,14 @@ void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf, case AF_INET: { const struct sockaddr_in *sa4 = (const void *)ai->ai_addr; const struct in_addr *ipaddr4 = &sa4->sin_addr; - (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize); + (void)curlx_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize); break; } #ifdef USE_IPV6 case AF_INET6: { const struct sockaddr_in6 *sa6 = (const void *)ai->ai_addr; const struct in6_addr *ipaddr6 = &sa6->sin6_addr; - (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, bufsize); + (void)curlx_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, bufsize); break; } #endif @@ -505,6 +505,8 @@ Curl_dnscache_mk_entry(struct Curl_easy *data, return NULL; } } +#else + (void)data; #endif if(!hostlen) hostlen = strlen(hostname); @@ -730,7 +732,7 @@ static bool tailmatch(const char *full, size_t flen, { if(plen > flen) return FALSE; - return strncasecompare(part, &full[flen - plen], plen); + return curl_strnequal(part, &full[flen - plen], plen); } static struct Curl_addrinfo * @@ -872,8 +874,8 @@ CURLcode Curl_resolv(struct Curl_easy *data, goto error; if(!is_ipaddr && - (strcasecompare(hostname, "localhost") || - strcasecompare(hostname, "localhost.") || + (curl_strequal(hostname, "localhost") || + curl_strequal(hostname, "localhost.") || tailmatch(hostname, hostname_len, STRCONST(".localhost")) || tailmatch(hostname, hostname_len, STRCONST(".localhost.")))) { addr = get_localhost(port, hostname); diff --git a/lib/hostip6.c b/lib/hostip6.c index 35cc2d737b..ce7f5050ea 100644 --- a/lib/hostip6.c +++ b/lib/hostip6.c @@ -44,6 +44,7 @@ #endif #include "urldata.h" +#include "cfilters.h" #include "sendf.h" #include "hostip.h" #include "hash.h" @@ -104,7 +105,8 @@ struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data, memset(&hints, 0, sizeof(hints)); hints.ai_family = pf; - hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ? + hints.ai_socktype = + (Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM; #ifndef USE_RESOLVE_ON_IPS diff --git a/lib/hsts.c b/lib/hsts.c index 62a3f89058..91f32276f9 100644 --- a/lib/hsts.c +++ b/lib/hsts.c @@ -33,7 +33,6 @@ #include "llist.h" #include "hsts.h" #include "curl_get_line.h" -#include "strcase.h" #include "sendf.h" #include "parsedate.h" #include "fopen.h" @@ -155,7 +154,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, do { curlx_str_passblanks(&p); - if(strncasecompare("max-age", p, 7)) { + if(curl_strnequal("max-age", p, 7)) { bool quoted = FALSE; int rc; @@ -185,7 +184,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname, } gotma = TRUE; } - else if(strncasecompare("includesubdomains", p, 17)) { + else if(curl_strnequal("includesubdomains", p, 17)) { if(gotinc) return CURLE_BAD_FUNCTION_ARGUMENT; subdomains = TRUE; @@ -272,15 +271,15 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname, if((subdomain && sts->includeSubDomains) && (ntail < hlen)) { size_t offs = hlen - ntail; if((hostname[offs-1] == '.') && - strncasecompare(&hostname[offs], sts->host, ntail) && + curl_strnequal(&hostname[offs], sts->host, ntail) && (ntail > blen)) { /* save the tail match with the longest tail */ bestsub = sts; blen = ntail; } } - /* avoid strcasecompare because the host name is not null-terminated */ - if((hlen == ntail) && strncasecompare(hostname, sts->host, hlen)) + /* avoid curl_strequal because the host name is not null-terminated */ + if((hlen == ntail) && curl_strnequal(hostname, sts->host, hlen)) return sts; } } @@ -581,4 +580,8 @@ void Curl_hsts_loadfiles(struct Curl_easy *data) } } +#if defined(DEBUGBUILD) || defined(UNITTESTS) +#undef time +#endif + #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_HSTS */ diff --git a/lib/http.c b/lib/http.c index 5942d313b8..e5a0696274 100644 --- a/lib/http.c +++ b/lib/http.c @@ -232,7 +232,7 @@ CURLcode Curl_http_setup_conn(struct Curl_easy *data, connkeep(conn, "HTTP default"); if(data->state.http_neg.wanted == CURL_HTTP_V3x) { /* only HTTP/3, needs to work */ - CURLcode result = Curl_conn_may_http3(data, conn); + CURLcode result = Curl_conn_may_http3(data, conn, conn->transport_wanted); if(result) return result; } @@ -259,7 +259,7 @@ char *Curl_checkProxyheaders(struct Curl_easy *data, for(head = (conn->bits.proxy && data->set.sep_headers) ? data->set.proxyheaders : data->set.headers; head; head = head->next) { - if(strncasecompare(head->data, thisheader, thislen) && + if(curl_strnequal(head->data, thisheader, thislen) && Curl_headersep(head->data[thislen])) return head->data; } @@ -862,7 +862,7 @@ static bool authcmp(const char *auth, const char *line) { /* the auth string must not have an alnum following */ size_t n = strlen(auth); - return strncasecompare(auth, line, n) && !ISALNUM(line[n]); + return curl_strnequal(auth, line, n) && !ISALNUM(line[n]); } #endif @@ -1482,7 +1482,7 @@ Curl_compareheader(const char *headerline, /* line to check */ DEBUGASSERT(header); DEBUGASSERT(content); - if(!strncasecompare(headerline, header, hlen)) + if(!curl_strnequal(headerline, header, hlen)) return FALSE; /* does not start with header */ /* pass the header */ @@ -1497,7 +1497,7 @@ Curl_compareheader(const char *headerline, /* line to check */ size_t len; p = curlx_str(&val); for(len = curlx_strlen(&val); len >= curlx_strlen(&val); len--, p++) { - if(strncasecompare(p, content, clen)) + if(curl_strnequal(p, content, clen)) return TRUE; /* match! */ } } @@ -1893,7 +1893,7 @@ static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn) ptr = Curl_checkheaders(data, STRCONST("Host")); if(ptr && (!data->state.this_is_a_follow || - strcasecompare(data->state.first_host, conn->host.name))) { + curl_strequal(data->state.first_host, conn->host.name))) { #if !defined(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 @@ -1929,7 +1929,7 @@ static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn) } #endif - if(!strcasecompare("Host:", ptr)) { + if(!curl_strequal("Host:", ptr)) { aptr->host = aprintf("Host:%s\r\n", &ptr[5]); if(!aptr->host) return CURLE_OUT_OF_MEMORY; @@ -2005,7 +2005,7 @@ static CURLcode http_target(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } - if(strcasecompare("http", data->state.up.scheme)) { + if(curl_strequal("http", data->state.up.scheme)) { /* when getting HTTP, we do not want the userinfo the URL */ uc = curl_url_set(h, CURLUPART_USER, NULL, 0); if(uc) { @@ -2034,7 +2034,7 @@ static CURLcode http_target(struct Curl_easy *data, if(result) return result; - if(strcasecompare("ftp", data->state.up.scheme)) { + if(curl_strequal("ftp", data->state.up.scheme)) { if(data->set.proxy_transfer_mode) { /* when doing ftp, append ;type= if not present */ char *type = strstr(path, ";type="); @@ -2443,7 +2443,7 @@ static CURLcode http_cookies(struct Curl_easy *data, data->state.aptr.cookiehost : conn->host.name; const bool secure_context = conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) || - strcasecompare("localhost", host) || + curl_strequal("localhost", host) || !strcmp(host, "127.0.0.1") || !strcmp(host, "::1"); Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); @@ -3000,377 +3000,489 @@ checkprotoprefix(struct Curl_easy *data, struct connectdata *conn, ((hdlen) > ((sizeof(n)-1) + (sizeof(v)-1))) && \ Curl_compareheader(hd, STRCONST(n), STRCONST(v))) +/* + * http_header_a() parses a single response header starting with A. + */ +static CURLcode http_header_a(struct Curl_easy *data, + const char *hd, size_t hdlen) +{ +#ifndef CURL_DISABLE_ALTSVC + const char *v; + struct connectdata *conn = data->conn; + v = (data->asi && + (Curl_conn_is_ssl(data->conn, FIRSTSOCKET) || +#ifdef DEBUGBUILD + /* allow debug builds to circumvent the HTTPS restriction */ + getenv("CURL_ALTSVC_HTTP") +#else + 0 +#endif + )) ? HD_VAL(hd, hdlen, "Alt-Svc:") : NULL; + if(v) { + /* the ALPN of the current request */ + 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)); + } +#else + (void)data; + (void)hd; + (void)hdlen; +#endif + return CURLE_OK; +} + +/* + * http_header_c() parses a single response header starting with C. + */ +static CURLcode http_header_c(struct Curl_easy *data, + const char *hd, size_t hdlen) +{ + struct connectdata *conn = data->conn; + struct SingleRequest *k = &data->req; + const char *v; + + /* Check for Content-Length: header lines to get size */ + v = (!k->http_bodyless && !data->set.ignorecl) ? + HD_VAL(hd, hdlen, "Content-Length:") : NULL; + if(v) { + curl_off_t contentlength; + int offt = curlx_str_numblanks(&v, &contentlength); + + if(offt == STRE_OK) { + k->size = contentlength; + k->maxdownload = k->size; + } + else if(offt == STRE_OVERFLOW) { + /* out of range */ + if(data->set.max_filesize) { + failf(data, "Maximum file size exceeded"); + return CURLE_FILESIZE_EXCEEDED; + } + streamclose(conn, "overflow content-length"); + infof(data, "Overflow Content-Length: value"); + } + else { + /* negative or just rubbish - bad HTTP */ + failf(data, "Invalid Content-Length: value"); + return CURLE_WEIRD_SERVER_REPLY; + } + return CURLE_OK; + } + v = (!k->http_bodyless && data->set.str[STRING_ENCODING]) ? + HD_VAL(hd, hdlen, "Content-Encoding:") : NULL; + if(v) { + /* + * Process Content-Encoding. Look for the values: identity, + * gzip, deflate, compress, x-gzip and x-compress. x-gzip and + * x-compress are the same as gzip and compress. (Sec 3.5 RFC + * 2616). zlib cannot handle compress. However, errors are + * handled further down when the response body is processed + */ + return Curl_build_unencoding_stack(data, v, FALSE); + } + /* check for Content-Type: header lines to get the MIME-type */ + v = HD_VAL(hd, hdlen, "Content-Type:"); + if(v) { + char *contenttype = Curl_copy_header_value(hd); + if(!contenttype) + return CURLE_OUT_OF_MEMORY; + if(!*contenttype) + /* ignore empty data */ + free(contenttype); + else { + free(data->info.contenttype); + data->info.contenttype = contenttype; + } + return CURLE_OK; + } + if(HD_IS_AND_SAYS(hd, hdlen, "Connection:", "close")) { + /* + * [RFC 2616, section 8.1.2.1] + * "Connection: close" is HTTP/1.1 language and means that + * the connection will close when this request has been + * served. + */ + streamclose(conn, "Connection: close used"); + return CURLE_OK; + } + if((k->httpversion == 10) && + HD_IS_AND_SAYS(hd, hdlen, "Connection:", "keep-alive")) { + /* + * An HTTP/1.0 reply with the 'Connection: keep-alive' line + * tells us the connection will be kept alive for our + * pleasure. Default action for 1.0 is to close. + * + * [RFC2068, section 19.7.1] */ + connkeep(conn, "Connection keep-alive"); + infof(data, "HTTP/1.0 connection set to keep alive"); + return CURLE_OK; + } + v = !k->http_bodyless ? HD_VAL(hd, hdlen, "Content-Range:") : NULL; + if(v) { + /* Content-Range: bytes [num]- + Content-Range: bytes: [num]- + Content-Range: [num]- + Content-Range: [asterisk]/[total] + + The second format was added since Sun's webserver + JavaWebServer/1.1.1 obviously sends the header this way! + The third added since some servers use that! + The fourth means the requested range was unsatisfied. + */ + + const char *ptr = v; + + /* Move forward until first digit or asterisk */ + while(*ptr && !ISDIGIT(*ptr) && *ptr != '*') + ptr++; + + /* if it truly stopped on a digit */ + if(ISDIGIT(*ptr)) { + if(!curlx_str_number(&ptr, &k->offset, CURL_OFF_T_MAX) && + (data->state.resume_from == k->offset)) + /* we asked for a resume and we got it */ + k->content_range = TRUE; + } + else if(k->httpcode < 300) + data->state.resume_from = 0; /* get everything */ + } + return CURLE_OK; +} + +/* + * http_header_l() parses a single response header starting with L. + */ +static CURLcode http_header_l(struct Curl_easy *data, + const char *hd, size_t hdlen) +{ + struct connectdata *conn = data->conn; + struct SingleRequest *k = &data->req; + const char *v = (!k->http_bodyless && + (data->set.timecondition || data->set.get_filetime)) ? + HD_VAL(hd, hdlen, "Last-Modified:") : NULL; + if(v) { + k->timeofdoc = Curl_getdate_capped(v); + if(data->set.get_filetime) + data->info.filetime = k->timeofdoc; + return CURLE_OK; + } + if((k->httpcode >= 300 && k->httpcode < 400) && + HD_IS(hd, hdlen, "Location:") && + !data->req.location) { + /* this is the URL that the server advises us to use instead */ + char *location = Curl_copy_header_value(hd); + if(!location) + return CURLE_OUT_OF_MEMORY; + if(!*location) + /* ignore empty data */ + free(location); + else { + data->req.location = location; + + if(data->set.http_follow_mode) { + CURLcode result; + DEBUGASSERT(!data->req.newurl); + data->req.newurl = strdup(data->req.location); /* clone */ + if(!data->req.newurl) + return CURLE_OUT_OF_MEMORY; + + /* some cases of POST and PUT etc needs to rewind the data + stream at this point */ + result = http_perhapsrewind(data, conn); + if(result) + return result; + + /* mark the next request as a followed location: */ + data->state.this_is_a_follow = TRUE; + } + } + } + return CURLE_OK; +} + +/* + * http_header_p() parses a single response header starting with P. + */ +static CURLcode http_header_p(struct Curl_easy *data, + const char *hd, size_t hdlen) +{ + struct SingleRequest *k = &data->req; + +#ifndef CURL_DISABLE_PROXY + const char *v = HD_VAL(hd, hdlen, "Proxy-Connection:"); + if(v) { + struct connectdata *conn = data->conn; + if((k->httpversion == 10) && conn->bits.httpproxy && + HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) { + /* + * When an HTTP/1.0 reply comes when using a proxy, the + * 'Proxy-Connection: keep-alive' line tells us the + * connection will be kept alive for our pleasure. + * Default action for 1.0 is to close. + */ + 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 && + HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) { + /* + * We get an HTTP/1.1 response from a proxy and it says it will + * close down after this transfer. + */ + connclose(conn, "Proxy-Connection: asked to close after done"); + infof(data, "HTTP/1.1 proxy connection set close"); + } + return CURLE_OK; + } +#endif + if((407 == k->httpcode) && HD_IS(hd, hdlen, "Proxy-authenticate:")) { + char *auth = Curl_copy_header_value(hd); + CURLcode result; + if(!auth) + return CURLE_OUT_OF_MEMORY; + result = Curl_http_input_auth(data, TRUE, auth); + free(auth); + return result; + } +#ifdef USE_SPNEGO + if(HD_IS(hd, hdlen, "Persistent-Auth:")) { + struct connectdata *conn = data->conn; + struct negotiatedata *negdata = Curl_auth_nego_get(conn, FALSE); + struct auth *authp = &data->state.authhost; + if(!negdata) + return CURLE_OUT_OF_MEMORY; + if(authp->picked == CURLAUTH_NEGOTIATE) { + char *persistentauth = Curl_copy_header_value(hd); + if(!persistentauth) + return CURLE_OUT_OF_MEMORY; + negdata->noauthpersist = !!checkprefix("false", persistentauth); + negdata->havenoauthpersist = TRUE; + infof(data, "Negotiate: noauthpersist -> %d, header part: %s", + negdata->noauthpersist, persistentauth); + free(persistentauth); + } + } +#endif + return CURLE_OK; +} + +/* + * http_header_r() parses a single response header starting with R. + */ +static CURLcode http_header_r(struct Curl_easy *data, + const char *hd, size_t hdlen) +{ + const char *v = HD_VAL(hd, hdlen, "Retry-After:"); + if(v) { + /* Retry-After = HTTP-date / delay-seconds */ + curl_off_t retry_after = 0; /* zero for unknown or "now" */ + time_t date; + curlx_str_passblanks(&v); + + /* try it as a date first, because a date can otherwise start with and + get treated as a number */ + date = Curl_getdate_capped(v); + + if((time_t)-1 != date) { + time_t current = time(NULL); + if(date >= current) + /* convert date to number of seconds into the future */ + retry_after = date - current; + } + else + /* Try it as a decimal number, ignore errors */ + (void)curlx_str_number(&v, &retry_after, CURL_OFF_T_MAX); + /* limit to 6 hours max. this is not documented so that it can be changed + in the future if necessary. */ + if(retry_after > 21600) + retry_after = 21600; + data->info.retry_after = retry_after; + } + return CURLE_OK; +} + +/* + * http_header_s() parses a single response header starting with S. + */ +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; + (void)hd; + (void)hdlen; +#endif + +#if !defined(CURL_DISABLE_COOKIES) + v = (data->cookies && data->state.cookie_engine) ? + HD_VAL(hd, hdlen, "Set-Cookie:") : NULL; + if(v) { + /* If there is a custom-set Host: name, use it here, or else use + * real peer hostname. */ + const char *host = data->state.aptr.cookiehost ? + data->state.aptr.cookiehost : conn->host.name; + const bool secure_context = + conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) || + curl_strequal("localhost", host) || + !strcmp(host, "127.0.0.1") || + !strcmp(host, "::1"); + + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, + CURL_LOCK_ACCESS_SINGLE); + Curl_cookie_add(data, data->cookies, TRUE, FALSE, v, host, + data->state.up.path, secure_context); + Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); + return CURLE_OK; + } +#endif +#ifndef CURL_DISABLE_HSTS + /* If enabled, the header is incoming and this is over HTTPS */ + v = (data->hsts && + (Curl_conn_is_ssl(conn, FIRSTSOCKET) || +#ifdef DEBUGBUILD + /* allow debug builds to circumvent the HTTPS restriction */ + getenv("CURL_HSTS_HTTP") +#else + 0 +#endif + ) + ) ? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL; + if(v) { + CURLcode check = + Curl_hsts_parse(data->hsts, conn->host.name, v); + if(check) + infof(data, "Illegal STS header skipped"); +#ifdef DEBUGBUILD + else + infof(data, "Parsed STS header fine (%zu entries)", + Curl_llist_count(&data->hsts->list)); +#endif + } +#endif + + return CURLE_OK; +} + +/* + * http_header_t() parses a single response header starting with T. + */ +static CURLcode http_header_t(struct Curl_easy *data, + const char *hd, size_t hdlen) +{ + struct connectdata *conn = data->conn; + struct SingleRequest *k = &data->req; + + /* RFC 9112, ch. 6.1 + * "Transfer-Encoding MAY be sent in a response to a HEAD request or + * in a 304 (Not Modified) response (Section 15.4.5 of [HTTP]) to a + * GET request, neither of which includes a message body, to indicate + * that the origin server would have applied a transfer coding to the + * message body if the request had been an unconditional GET." + * + * Read: in these cases the 'Transfer-Encoding' does not apply + * to any data following the response headers. Do not add any decoders. + */ + const char *v = (!k->http_bodyless && + (data->state.httpreq != HTTPREQ_HEAD) && + (k->httpcode != 304)) ? + HD_VAL(hd, hdlen, "Transfer-Encoding:") : NULL; + if(v) { + /* One or more encodings. We check for chunked and/or a compression + algorithm. */ + CURLcode result = Curl_build_unencoding_stack(data, v, TRUE); + if(result) + return result; + if(!k->chunk && data->set.http_transfer_encoding) { + /* if this is not chunked, only close can signal the end of this + * transfer as Content-Length is said not to be trusted for + * transfer-encoding! */ + connclose(conn, "HTTP/1.1 transfer-encoding without chunks"); + k->ignore_cl = TRUE; + } + return CURLE_OK; + } + v = HD_VAL(hd, hdlen, "Trailer:"); + if(v) { + data->req.resp_trailer = TRUE; + return CURLE_OK; + } + return CURLE_OK; +} + +/* + * http_header_w() parses a single response header starting with W. + */ +static CURLcode http_header_w(struct Curl_easy *data, + const char *hd, size_t hdlen) +{ + struct SingleRequest *k = &data->req; + CURLcode result = CURLE_OK; + + if((401 == k->httpcode) && HD_IS(hd, hdlen, "WWW-Authenticate:")) { + char *auth = Curl_copy_header_value(hd); + if(!auth) + return CURLE_OUT_OF_MEMORY; + result = Curl_http_input_auth(data, FALSE, auth); + free(auth); + } + return result; +} + /* * http_header() parses a single response header. */ static CURLcode http_header(struct Curl_easy *data, const char *hd, size_t hdlen) { - struct connectdata *conn = data->conn; - CURLcode result; - struct SingleRequest *k = &data->req; - const char *v; + CURLcode result = CURLE_OK; switch(hd[0]) { case 'a': case 'A': -#ifndef CURL_DISABLE_ALTSVC - v = (data->asi && - (Curl_conn_is_ssl(data->conn, FIRSTSOCKET) || -#ifdef DEBUGBUILD - /* allow debug builds to circumvent the HTTPS restriction */ - getenv("CURL_ALTSVC_HTTP") -#else - 0 -#endif - )) ? HD_VAL(hd, hdlen, "Alt-Svc:") : NULL; - if(v) { - /* the ALPN of the current request */ - 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)); - } -#endif + result = http_header_a(data, hd, hdlen); break; case 'c': case 'C': - /* Check for Content-Length: header lines to get size */ - v = (!k->http_bodyless && !data->set.ignorecl) ? - HD_VAL(hd, hdlen, "Content-Length:") : NULL; - if(v) { - curl_off_t contentlength; - int offt = curlx_str_numblanks(&v, &contentlength); - - if(offt == STRE_OK) { - k->size = contentlength; - k->maxdownload = k->size; - } - else if(offt == STRE_OVERFLOW) { - /* out of range */ - if(data->set.max_filesize) { - failf(data, "Maximum file size exceeded"); - return CURLE_FILESIZE_EXCEEDED; - } - streamclose(conn, "overflow content-length"); - infof(data, "Overflow Content-Length: value"); - } - else { - /* negative or just rubbish - bad HTTP */ - failf(data, "Invalid Content-Length: value"); - return CURLE_WEIRD_SERVER_REPLY; - } - return CURLE_OK; - } - v = (!k->http_bodyless && data->set.str[STRING_ENCODING]) ? - HD_VAL(hd, hdlen, "Content-Encoding:") : NULL; - if(v) { - /* - * Process Content-Encoding. Look for the values: identity, - * gzip, deflate, compress, x-gzip and x-compress. x-gzip and - * x-compress are the same as gzip and compress. (Sec 3.5 RFC - * 2616). zlib cannot handle compress. However, errors are - * handled further down when the response body is processed - */ - return Curl_build_unencoding_stack(data, v, FALSE); - } - /* check for Content-Type: header lines to get the MIME-type */ - v = HD_VAL(hd, hdlen, "Content-Type:"); - if(v) { - char *contenttype = Curl_copy_header_value(hd); - if(!contenttype) - return CURLE_OUT_OF_MEMORY; - if(!*contenttype) - /* ignore empty data */ - free(contenttype); - else { - free(data->info.contenttype); - data->info.contenttype = contenttype; - } - return CURLE_OK; - } - if(HD_IS_AND_SAYS(hd, hdlen, "Connection:", "close")) { - /* - * [RFC 2616, section 8.1.2.1] - * "Connection: close" is HTTP/1.1 language and means that - * the connection will close when this request has been - * served. - */ - streamclose(conn, "Connection: close used"); - return CURLE_OK; - } - if((k->httpversion == 10) && - HD_IS_AND_SAYS(hd, hdlen, "Connection:", "keep-alive")) { - /* - * An HTTP/1.0 reply with the 'Connection: keep-alive' line - * tells us the connection will be kept alive for our - * pleasure. Default action for 1.0 is to close. - * - * [RFC2068, section 19.7.1] */ - connkeep(conn, "Connection keep-alive"); - infof(data, "HTTP/1.0 connection set to keep alive"); - return CURLE_OK; - } - v = !k->http_bodyless ? HD_VAL(hd, hdlen, "Content-Range:") : NULL; - if(v) { - /* Content-Range: bytes [num]- - Content-Range: bytes: [num]- - Content-Range: [num]- - Content-Range: [asterisk]/[total] - - The second format was added since Sun's webserver - JavaWebServer/1.1.1 obviously sends the header this way! - The third added since some servers use that! - The fourth means the requested range was unsatisfied. - */ - - const char *ptr = v; - - /* Move forward until first digit or asterisk */ - while(*ptr && !ISDIGIT(*ptr) && *ptr != '*') - ptr++; - - /* if it truly stopped on a digit */ - if(ISDIGIT(*ptr)) { - if(!curlx_str_number(&ptr, &k->offset, CURL_OFF_T_MAX) && - (data->state.resume_from == k->offset)) - /* we asked for a resume and we got it */ - k->content_range = TRUE; - } - else if(k->httpcode < 300) - data->state.resume_from = 0; /* get everything */ - } + result = http_header_c(data, hd, hdlen); break; case 'l': case 'L': - v = (!k->http_bodyless && - (data->set.timecondition || data->set.get_filetime)) ? - HD_VAL(hd, hdlen, "Last-Modified:") : NULL; - if(v) { - k->timeofdoc = Curl_getdate_capped(v); - if(data->set.get_filetime) - data->info.filetime = k->timeofdoc; - return CURLE_OK; - } - if((k->httpcode >= 300 && k->httpcode < 400) && - HD_IS(hd, hdlen, "Location:") && - !data->req.location) { - /* this is the URL that the server advises us to use instead */ - char *location = Curl_copy_header_value(hd); - if(!location) - return CURLE_OUT_OF_MEMORY; - if(!*location) - /* ignore empty data */ - free(location); - else { - data->req.location = location; - - if(data->set.http_follow_mode) { - DEBUGASSERT(!data->req.newurl); - data->req.newurl = strdup(data->req.location); /* clone */ - if(!data->req.newurl) - return CURLE_OUT_OF_MEMORY; - - /* some cases of POST and PUT etc needs to rewind the data - stream at this point */ - result = http_perhapsrewind(data, conn); - if(result) - return result; - - /* mark the next request as a followed location: */ - data->state.this_is_a_follow = TRUE; - } - } - } + result = http_header_l(data, hd, hdlen); break; case 'p': case 'P': -#ifndef CURL_DISABLE_PROXY - v = HD_VAL(hd, hdlen, "Proxy-Connection:"); - if(v) { - if((k->httpversion == 10) && conn->bits.httpproxy && - HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) { - /* - * When an HTTP/1.0 reply comes when using a proxy, the - * 'Proxy-Connection: keep-alive' line tells us the - * connection will be kept alive for our pleasure. - * Default action for 1.0 is to close. - */ - 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 && - HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) { - /* - * We get an HTTP/1.1 response from a proxy and it says it will - * close down after this transfer. - */ - connclose(conn, "Proxy-Connection: asked to close after done"); - infof(data, "HTTP/1.1 proxy connection set close"); - } - return CURLE_OK; - } -#endif - if((407 == k->httpcode) && HD_IS(hd, hdlen, "Proxy-authenticate:")) { - char *auth = Curl_copy_header_value(hd); - if(!auth) - return CURLE_OUT_OF_MEMORY; - result = Curl_http_input_auth(data, TRUE, auth); - free(auth); - return result; - } -#ifdef USE_SPNEGO - if(HD_IS(hd, hdlen, "Persistent-Auth:")) { - struct negotiatedata *negdata = &conn->negotiate; - struct auth *authp = &data->state.authhost; - if(authp->picked == CURLAUTH_NEGOTIATE) { - char *persistentauth = Curl_copy_header_value(hd); - if(!persistentauth) - return CURLE_OUT_OF_MEMORY; - negdata->noauthpersist = !!checkprefix("false", persistentauth); - negdata->havenoauthpersist = TRUE; - infof(data, "Negotiate: noauthpersist -> %d, header part: %s", - negdata->noauthpersist, persistentauth); - free(persistentauth); - } - } -#endif + result = http_header_p(data, hd, hdlen); break; case 'r': case 'R': - v = HD_VAL(hd, hdlen, "Retry-After:"); - if(v) { - /* Retry-After = HTTP-date / delay-seconds */ - curl_off_t retry_after = 0; /* zero for unknown or "now" */ - time_t date; - curlx_str_passblanks(&v); - - /* try it as a date first, because a date can otherwise start with and - get treated as a number */ - date = Curl_getdate_capped(v); - - if((time_t)-1 != date) { - time_t current = time(NULL); - if(date >= current) - /* convert date to number of seconds into the future */ - retry_after = date - current; - } - else - /* Try it as a decimal number */ - curlx_str_number(&v, &retry_after, CURL_OFF_T_MAX); - /* limit to 6 hours max. this is not documented so that it can be changed - in the future if necessary. */ - if(retry_after > 21600) - retry_after = 21600; - data->info.retry_after = retry_after; - return CURLE_OK; - } + result = http_header_r(data, hd, hdlen); break; case 's': case 'S': -#if !defined(CURL_DISABLE_COOKIES) - v = (data->cookies && data->state.cookie_engine) ? - HD_VAL(hd, hdlen, "Set-Cookie:") : NULL; - if(v) { - /* If there is a custom-set Host: name, use it here, or else use - * real peer hostname. */ - const char *host = data->state.aptr.cookiehost ? - data->state.aptr.cookiehost : conn->host.name; - const bool secure_context = - conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) || - strcasecompare("localhost", host) || - !strcmp(host, "127.0.0.1") || - !strcmp(host, "::1"); - - Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, - CURL_LOCK_ACCESS_SINGLE); - Curl_cookie_add(data, data->cookies, TRUE, FALSE, v, host, - data->state.up.path, secure_context); - Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); - return CURLE_OK; - } -#endif -#ifndef CURL_DISABLE_HSTS - /* If enabled, the header is incoming and this is over HTTPS */ - v = (data->hsts && - (Curl_conn_is_ssl(conn, FIRSTSOCKET) || -#ifdef DEBUGBUILD - /* allow debug builds to circumvent the HTTPS restriction */ - getenv("CURL_HSTS_HTTP") -#else - 0 -#endif - ) - ) ? HD_VAL(hd, hdlen, "Strict-Transport-Security:") : NULL; - if(v) { - CURLcode check = - Curl_hsts_parse(data->hsts, conn->host.name, v); - if(check) - infof(data, "Illegal STS header skipped"); -#ifdef DEBUGBUILD - else - infof(data, "Parsed STS header fine (%zu entries)", - Curl_llist_count(&data->hsts->list)); -#endif - } -#endif + result = http_header_s(data, hd, hdlen); break; case 't': case 'T': - /* RFC 9112, ch. 6.1 - * "Transfer-Encoding MAY be sent in a response to a HEAD request or - * in a 304 (Not Modified) response (Section 15.4.5 of [HTTP]) to a - * GET request, neither of which includes a message body, to indicate - * that the origin server would have applied a transfer coding to the - * message body if the request had been an unconditional GET." - * - * Read: in these cases the 'Transfer-Encoding' does not apply - * to any data following the response headers. Do not add any decoders. - */ - v = (!k->http_bodyless && - (data->state.httpreq != HTTPREQ_HEAD) && - (k->httpcode != 304)) ? - HD_VAL(hd, hdlen, "Transfer-Encoding:") : NULL; - if(v) { - /* One or more encodings. We check for chunked and/or a compression - algorithm. */ - result = Curl_build_unencoding_stack(data, v, TRUE); - if(result) - return result; - if(!k->chunk && data->set.http_transfer_encoding) { - /* if this is not chunked, only close can signal the end of this - * transfer as Content-Length is said not to be trusted for - * transfer-encoding! */ - connclose(conn, "HTTP/1.1 transfer-encoding without chunks"); - k->ignore_cl = TRUE; - } - return CURLE_OK; - } - v = HD_VAL(hd, hdlen, "Trailer:"); - if(v) { - data->req.resp_trailer = TRUE; - return CURLE_OK; - } + result = http_header_t(data, hd, hdlen); break; case 'w': case 'W': - if((401 == k->httpcode) && HD_IS(hd, hdlen, "WWW-Authenticate:")) { - char *auth = Curl_copy_header_value(hd); - if(!auth) - return CURLE_OUT_OF_MEMORY; - result = Curl_http_input_auth(data, FALSE, auth); - free(auth); - return result; - } + result = http_header_w(data, hd, hdlen); break; } - if(conn->handler->protocol & CURLPROTO_RTSP) { - result = Curl_rtsp_parseheader(data, hd); - if(result) - return result; + if(!result) { + struct connectdata *conn = data->conn; + if(conn->handler->protocol & CURLPROTO_RTSP) + result = Curl_rtsp_parseheader(data, hd); } - return CURLE_OK; + return result; } /* @@ -3850,9 +3962,8 @@ static CURLcode http_on_response(struct Curl_easy *data, out: if(last_hd) { /* if not written yet, write it now */ - CURLcode r2 = http_write_header(data, last_hd, last_hd_len); - if(!result) - result = r2; + result = Curl_1st_err( + result, http_write_header(data, last_hd, last_hd_len)); } return result; } @@ -4474,7 +4585,7 @@ static bool h2_permissible_field(struct dynhds_entry *e) if(e->namelen < H2_NON_FIELD[i].namelen) return TRUE; if(e->namelen == H2_NON_FIELD[i].namelen && - strcasecompare(H2_NON_FIELD[i].name, e->name)) + curl_strequal(H2_NON_FIELD[i].name, e->name)) return FALSE; } return TRUE; @@ -4565,7 +4676,7 @@ CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers, e = Curl_dynhds_getn(&req->headers, i); /* "TE" is special in that it is only permissible when it * has only value "trailers". RFC 9113 ch. 8.2.2 */ - if(e->namelen == 2 && strcasecompare("TE", e->name)) { + if(e->namelen == 2 && curl_strequal("TE", e->name)) { if(http_TE_has_token(e->value, "trailers")) result = Curl_dynhds_add(h2_headers, e->name, e->namelen, "trailers", sizeof("trailers") - 1); diff --git a/lib/http2.c b/lib/http2.c index dd82aa963e..36dca426de 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -36,7 +36,6 @@ #include "sendf.h" #include "select.h" #include "curlx/base64.h" -#include "strcase.h" #include "multiif.h" #include "url.h" #include "urlapi-int.h" @@ -205,6 +204,9 @@ static void cf_h2_ctx_close(struct cf_h2_ctx *ctx) } } +static CURLcode nw_out_flush(struct Curl_cfilter *cf, + struct Curl_easy *data); + static CURLcode h2_progress_egress(struct Curl_cfilter *cf, struct Curl_easy *data); @@ -374,27 +376,6 @@ static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf, } #endif /* !NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE */ -/* - * Mark this transfer to get "drained". - */ -static void drain_stream(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h2_stream_ctx *stream) -{ - unsigned char bits; - - (void)cf; - bits = CURL_CSELECT_IN; - if(!stream->closed && - (!stream->body_eos || !Curl_bufq_is_empty(&stream->sendbuf))) - bits |= CURL_CSELECT_OUT; - if(stream->closed || (data->state.select_bits != bits)) { - CURL_TRC_CF(data, cf, "[%d] DRAIN select_bits=%x", - stream->id, bits); - data->state.select_bits = bits; - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } -} static CURLcode http2_data_setup(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -449,8 +430,10 @@ static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) flush_egress = TRUE; } - if(flush_egress) - nghttp2_session_send(ctx->h2); + if(flush_egress) { + (void)nghttp2_session_send(ctx->h2); + (void)nw_out_flush(cf, data); + } } Curl_uint_hash_remove(&ctx->streams, data->mid); @@ -480,33 +463,6 @@ static int h2_client_new(struct Curl_cfilter *cf, return rc; } -static ssize_t nw_in_reader(void *reader_ctx, - unsigned char *buf, size_t buflen, - CURLcode *err) -{ - struct Curl_cfilter *cf = reader_ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - - return Curl_conn_cf_recv(cf->next, data, (char *)buf, buflen, err); -} - -static ssize_t nw_out_writer(void *writer_ctx, - const unsigned char *buf, size_t buflen, - CURLcode *err) -{ - struct Curl_cfilter *cf = writer_ctx; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - - if(data) { - ssize_t nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, - buflen, FALSE, err); - if(nwritten > 0) - CURL_TRC_CF(data, cf, "[0] egress: wrote %zd bytes", nwritten); - return nwritten; - } - return 0; -} - static ssize_t send_callback(nghttp2_session *h2, const uint8_t *mem, size_t length, int flags, void *userp); @@ -727,12 +683,12 @@ static bool http2_connisalive(struct Curl_cfilter *cf, struct Curl_easy *data, not in use by any other transfer, there should not be any data here, only "protocol frames" */ CURLcode result; - ssize_t nread = -1; + size_t nread; *input_pending = FALSE; - nread = Curl_bufq_slurp(&ctx->inbufq, nw_in_reader, cf, &result); - if(nread != -1) { - CURL_TRC_CF(data, cf, "%zd bytes stray data read before trying " + result = Curl_cf_recv_bufq(cf->next, data, &ctx->inbufq, 0, &nread); + if(!result) { + CURL_TRC_CF(data, cf, "%zu bytes stray data read before trying " "h2 connection", nread); if(h2_process_pending_input(cf, data, &result) < 0) /* immediate error, considered dead */ @@ -785,15 +741,16 @@ static CURLcode nw_out_flush(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_h2_ctx *ctx = cf->ctx; - ssize_t nwritten; + size_t nwritten; CURLcode result; (void)data; if(Curl_bufq_is_empty(&ctx->outbufq)) return CURLE_OK; - nwritten = Curl_bufq_pass(&ctx->outbufq, nw_out_writer, cf, &result); - if(nwritten < 0) { + result = Curl_cf_send_bufq(cf->next, data, &ctx->outbufq, NULL, 0, + &nwritten); + if(result) { if(result == CURLE_AGAIN) { CURL_TRC_CF(data, cf, "flush nw send buffer(%zu) -> EAGAIN", Curl_bufq_len(&ctx->outbufq)); @@ -816,7 +773,7 @@ static ssize_t send_callback(nghttp2_session *h2, struct Curl_cfilter *cf = userp; struct cf_h2_ctx *ctx = cf->ctx; struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nwritten; + size_t nwritten; CURLcode result = CURLE_OK; (void)h2; @@ -824,11 +781,12 @@ static ssize_t send_callback(nghttp2_session *h2, DEBUGASSERT(data); if(!cf->connected) - nwritten = Curl_bufq_write(&ctx->outbufq, buf, blen, &result); + result = Curl_bufq_write(&ctx->outbufq, buf, blen, &nwritten); else - nwritten = Curl_bufq_write_pass(&ctx->outbufq, buf, blen, - nw_out_writer, cf, &result); - if(nwritten < 0) { + result = Curl_cf_send_bufq(cf->next, data, &ctx->outbufq, buf, blen, + &nwritten); + + if(result) { if(result == CURLE_AGAIN) { ctx->nw_out_blocked = 1; return NGHTTP2_ERR_WOULDBLOCK; @@ -841,7 +799,8 @@ static ssize_t send_callback(nghttp2_session *h2, ctx->nw_out_blocked = 1; return NGHTTP2_ERR_WOULDBLOCK; } - return nwritten; + return (nwritten > SSIZE_T_MAX) ? + NGHTTP2_ERR_CALLBACK_FAILURE : (ssize_t)nwritten; } @@ -1191,7 +1150,7 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, if(stream->status_code / 100 != 1) { stream->resp_hds_complete = TRUE; } - drain_stream(cf, data, stream); + Curl_multi_mark_dirty(data); break; case NGHTTP2_PUSH_PROMISE: rv = push_promise(cf, data, &frame->push_promise); @@ -1214,12 +1173,12 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, if(frame->rst_stream.error_code) { stream->reset = TRUE; } - drain_stream(cf, data, stream); + Curl_multi_mark_dirty(data); break; case NGHTTP2_WINDOW_UPDATE: if(CURL_WANT_SEND(data) && Curl_bufq_is_empty(&stream->sendbuf)) { /* need more data, force processing of transfer */ - drain_stream(cf, data, stream); + Curl_multi_mark_dirty(data); } else if(!Curl_bufq_is_empty(&stream->sendbuf)) { /* resume the potentially suspended stream */ @@ -1245,7 +1204,7 @@ static CURLcode on_stream_frame(struct Curl_cfilter *cf, stream->id, NGHTTP2_STREAM_CLOSED); stream->closed = TRUE; } - drain_stream(cf, data, stream); + Curl_multi_mark_dirty(data); } return CURLE_OK; } @@ -1394,11 +1353,8 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame, * window and *assume* that we treat this like a WINDOW_UPDATE. Some * servers send an explicit WINDOW_UPDATE, but not all seem to do that. * To be safe, we UNHOLD a stream in order not to stall. */ - if(CURL_WANT_SEND(data)) { - struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); - if(stream) - drain_stream(cf, data, stream); - } + if(CURL_WANT_SEND(data)) + Curl_multi_mark_dirty(data); } break; } @@ -1543,7 +1499,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id, stream_id, nghttp2_http2_strerror(error_code), error_code); else CURL_TRC_CF(data_s, cf, "[%d] CLOSED", stream_id); - drain_stream(cf, data_s, stream); + Curl_multi_mark_dirty(data_s); /* remove `data_s` from the nghttp2 stream */ rv = nghttp2_session_set_stream_user_data(session, stream_id, 0); @@ -1641,9 +1597,9 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, if(!check) /* no memory */ return NGHTTP2_ERR_CALLBACK_FAILURE; - if(!strcasecompare(check, (const char *)value) && + if(!curl_strequal(check, (const char *)value) && ((cf->conn->remote_port != cf->conn->given->defport) || - !strcasecompare(cf->conn->host.name, (const char *)value))) { + !curl_strequal(cf->conn->host.name, (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 @@ -1737,7 +1693,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, } /* if we receive data for another handle, wake that up */ if(CF_DATA_CURRENT(cf) != data_s) - Curl_expire(data_s, 0, EXPIRE_RUN_NOW); + Curl_multi_mark_dirty(data_s); CURL_TRC_CF(data_s, cf, "[%d] status: HTTP/2 %03d", stream->id, stream->status_code); @@ -1764,7 +1720,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame, } /* if we receive data for another handle, wake that up */ if(CF_DATA_CURRENT(cf) != data_s) - Curl_expire(data_s, 0, EXPIRE_RUN_NOW); + Curl_multi_mark_dirty(data_s); CURL_TRC_CF(data_s, cf, "[%d] header: %.*s: %.*s", stream->id, (int)namelen, name, (int)valuelen, value); @@ -1785,6 +1741,7 @@ static ssize_t req_body_read_callback(nghttp2_session *session, struct h2_stream_ctx *stream = NULL; CURLcode result; ssize_t nread; + size_t n; (void)source; (void)cf; @@ -1803,12 +1760,14 @@ static ssize_t req_body_read_callback(nghttp2_session *session, if(!stream) return NGHTTP2_ERR_CALLBACK_FAILURE; - nread = Curl_bufq_read(&stream->sendbuf, buf, length, &result); - if(nread < 0) { + result = Curl_bufq_read(&stream->sendbuf, buf, length, &n); + if(result) { if(result != CURLE_AGAIN) return NGHTTP2_ERR_CALLBACK_FAILURE; nread = 0; } + else + 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); @@ -1874,20 +1833,20 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req, return result; } -static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h2_stream_ctx *stream, - CURLcode *err) +static CURLcode http2_handle_stream_close(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h2_stream_ctx *stream, + size_t *pnlen) { - ssize_t rv = 0; + CURLcode result; + *pnlen = 0; if(stream->error == NGHTTP2_REFUSED_STREAM) { CURL_TRC_CF(data, cf, "[%d] REFUSED_STREAM, try again on a new " "connection", stream->id); connclose(cf->conn, "REFUSED_STREAM"); /* do not use this anymore */ data->state.refused_stream = TRUE; - *err = CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ - return -1; + return CURLE_RECV_ERROR; /* trigger Curl_retry_request() later */ } else if(stream->error != NGHTTP2_NO_ERROR) { if(stream->resp_hds_complete && data->req.no_body) { @@ -1896,27 +1855,23 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, stream->id, nghttp2_http2_strerror(stream->error), stream->error); stream->close_handled = TRUE; - *err = CURLE_OK; - goto out; + return CURLE_OK; } failf(data, "HTTP/2 stream %u was not closed cleanly: %s (err %u)", stream->id, nghttp2_http2_strerror(stream->error), stream->error); - *err = CURLE_HTTP2_STREAM; - return -1; + return CURLE_HTTP2_STREAM; } else if(stream->reset) { failf(data, "HTTP/2 stream %u was reset", stream->id); - *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2; - return -1; + return data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2; } if(!stream->bodystarted) { failf(data, "HTTP/2 stream %u was closed cleanly, but before getting " " all response header fields, treated as error", stream->id); - *err = CURLE_HTTP2_STREAM; - return -1; + return CURLE_HTTP2_STREAM; } if(Curl_dynhds_count(&stream->resp_trailers)) { @@ -1924,37 +1879,36 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf, struct dynbuf dbuf; size_t i; - *err = CURLE_OK; + result = CURLE_OK; curlx_dyn_init(&dbuf, DYN_TRAILERS); for(i = 0; i < Curl_dynhds_count(&stream->resp_trailers); ++i) { e = Curl_dynhds_getn(&stream->resp_trailers, i); if(!e) break; curlx_dyn_reset(&dbuf); - *err = curlx_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a", + result = curlx_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a", (int)e->namelen, e->name, (int)e->valuelen, e->value); - if(*err) + if(result) break; Curl_debug(data, CURLINFO_HEADER_IN, curlx_dyn_ptr(&dbuf), curlx_dyn_len(&dbuf)); - *err = Curl_client_write(data, CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER, - curlx_dyn_ptr(&dbuf), curlx_dyn_len(&dbuf)); - if(*err) + result = Curl_client_write(data, CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER, + curlx_dyn_ptr(&dbuf), curlx_dyn_len(&dbuf)); + if(result) break; } curlx_dyn_free(&dbuf); - if(*err) + if(result) goto out; } stream->close_handled = TRUE; - *err = CURLE_OK; - rv = 0; + result = CURLE_OK; out: - CURL_TRC_CF(data, cf, "handle_stream_close -> %zd, %d", rv, *err); - return rv; + CURL_TRC_CF(data, cf, "handle_stream_close -> %d, %zu", result, *pnlen); + return result; } static int sweight_wanted(const struct Curl_easy *data) @@ -1997,7 +1951,7 @@ static void h2_pri_spec(struct cf_h2_ctx *ctx, * Flush any out data pending in the network buffer. */ static CURLcode h2_progress_egress(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data) { struct cf_h2_ctx *ctx = cf->ctx; struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); @@ -2037,36 +1991,36 @@ out: return nw_out_flush(cf, data); } -static ssize_t stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - struct h2_stream_ctx *stream, - char *buf, size_t len, CURLcode *err) +static CURLcode stream_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + struct h2_stream_ctx *stream, + char *buf, size_t len, size_t *pnread) { struct cf_h2_ctx *ctx = cf->ctx; - ssize_t nread = -1; + CURLcode result = CURLE_AGAIN; (void)buf; - *err = CURLE_AGAIN; + (void)len; + *pnread = 0; + if(stream->xfer_result) { CURL_TRC_CF(data, cf, "[%d] xfer write failed", stream->id); - *err = stream->xfer_result; - nread = -1; + result = stream->xfer_result; } else if(stream->closed) { CURL_TRC_CF(data, cf, "[%d] returning CLOSE", stream->id); - nread = http2_handle_stream_close(cf, data, stream, err); + result = http2_handle_stream_close(cf, data, stream, pnread); } else if(stream->reset || (ctx->conn_closed && Curl_bufq_is_empty(&ctx->inbufq)) || (ctx->rcvd_goaway && ctx->remote_max_sid < stream->id)) { CURL_TRC_CF(data, cf, "[%d] returning ERR", stream->id); - *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2; - nread = -1; + result = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP2; } - if(nread < 0 && *err != CURLE_AGAIN) - CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %zd, %d", - stream->id, len, nread, *err); - return nread; + if(result && (result != CURLE_AGAIN)) + CURL_TRC_CF(data, cf, "[%d] stream_recv(len=%zu) -> %d, %zu", + stream->id, len, result, *pnread); + return result; } static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, @@ -2076,7 +2030,7 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, struct cf_h2_ctx *ctx = cf->ctx; struct h2_stream_ctx *stream; CURLcode result = CURLE_OK; - ssize_t nread; + size_t nread; if(should_close_session(ctx)) { CURL_TRC_CF(data, cf, "progress ingress, session is closed"); @@ -2102,12 +2056,12 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, * this may leave data in underlying buffers that will not * be consumed. */ if(!cf->next || !cf->next->cft->has_data_pending(cf->next, data)) - drain_stream(cf, data, stream); + Curl_multi_mark_dirty(data); break; } - nread = Curl_bufq_sipn(&ctx->inbufq, 0, nw_in_reader, cf, &result); - if(nread < 0) { + 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, curl_easy_strerror(result)); @@ -2121,9 +2075,8 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, break; } else { - CURL_TRC_CF(data, cf, "[0] ingress: read %zd bytes", nread); - data_max_bytes = (data_max_bytes > (size_t)nread) ? - (data_max_bytes - (size_t)nread) : 0; + CURL_TRC_CF(data, cf, "[0] ingress: read %zu bytes", nread); + data_max_bytes = (data_max_bytes > nread) ? (data_max_bytes - nread) : 0; } if(h2_process_pending_input(cf, data, &result)) @@ -2140,15 +2093,15 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf, return CURLE_OK; } -static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) +static CURLcode cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, size_t *pnread) { struct cf_h2_ctx *ctx = cf->ctx; struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); - ssize_t nread = -1; - CURLcode result; + CURLcode result, r2; struct cf_call_data save; + *pnread = 0; if(!stream) { /* Abnormal call sequence: either this transfer has never opened a stream * (unlikely) or the transfer has been done, cleaned up its resources, but @@ -2156,51 +2109,49 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, * is for such a case. */ failf(data, "http/2 recv on a transfer never opened " "or already cleared, mid=%u", data->mid); - *err = CURLE_HTTP2; - return -1; + return CURLE_HTTP2; } CF_DATA_SAVE(save, cf, data); - nread = stream_recv(cf, data, stream, buf, len, err); - if(nread < 0 && *err != CURLE_AGAIN) + result = stream_recv(cf, data, stream, buf, len, pnread); + if(result && (result != CURLE_AGAIN)) goto out; - if(nread < 0) { - *err = h2_progress_ingress(cf, data, len); - if(*err) + if(result) { + result = h2_progress_ingress(cf, data, len); + if(result) goto out; - nread = stream_recv(cf, data, stream, buf, len, err); + result = stream_recv(cf, data, stream, buf, len, pnread); } - if(nread > 0) { + if(*pnread > 0) { /* Now that we transferred this to the upper layer, we report * the actual amount of DATA consumed to the H2 session, so * that it adjusts stream flow control */ - nghttp2_session_consume(ctx->h2, stream->id, (size_t)nread); + nghttp2_session_consume(ctx->h2, stream->id, *pnread); if(stream->closed) { CURL_TRC_CF(data, cf, "[%d] DRAIN closed stream", stream->id); - drain_stream(cf, data, stream); + Curl_multi_mark_dirty(data); } } out: - result = h2_progress_egress(cf, data); - if(result == CURLE_AGAIN) { + r2 = h2_progress_egress(cf, data); + if(r2 == CURLE_AGAIN) { /* pending data to send, need to be called again. Ideally, we * monitor the socket for POLLOUT, but when not SENDING * any more, we force processing of the transfer. */ if(!CURL_WANT_SEND(data)) - drain_stream(cf, data, stream); + Curl_multi_mark_dirty(data); } - else if(result) { - *err = result; - nread = -1; + else if(r2) { + result = r2; } - CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %zd %d, " + CURL_TRC_CF(data, cf, "[%d] cf_recv(len=%zu) -> %d, %zu, " "window=%d/%d, connection %d/%d", - stream->id, len, nread, *err, + stream->id, len, result, *pnread, nghttp2_session_get_stream_effective_recv_data_length( ctx->h2, stream->id), nghttp2_session_get_stream_effective_local_window_size( @@ -2209,7 +2160,7 @@ out: HTTP2_HUGE_WINDOW_SIZE); CF_DATA_RESTORE(cf, save); - return nread; + return result; } static ssize_t cf_h2_body_send(struct Curl_cfilter *cf, @@ -2219,7 +2170,7 @@ static ssize_t cf_h2_body_send(struct Curl_cfilter *cf, CURLcode *err) { struct cf_h2_ctx *ctx = cf->ctx; - ssize_t nwritten; + size_t nwritten; if(stream->closed) { if(stream->resp_hds_complete) { @@ -2241,11 +2192,11 @@ static ssize_t cf_h2_body_send(struct Curl_cfilter *cf, return -1; } - nwritten = Curl_bufq_write(&stream->sendbuf, buf, blen, err); - if(nwritten < 0) + *err = Curl_bufq_write(&stream->sendbuf, buf, blen, &nwritten); + if(*err) return -1; - if(eos && (blen == (size_t)nwritten)) + if(eos && (blen == nwritten)) stream->body_eos = TRUE; if(eos || !Curl_bufq_is_empty(&stream->sendbuf)) { @@ -2256,13 +2207,13 @@ static ssize_t cf_h2_body_send(struct Curl_cfilter *cf, return -1; } } - return nwritten; + return (ssize_t)nwritten; } -static ssize_t h2_submit(struct h2_stream_ctx **pstream, - struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, - bool eos, CURLcode *err) +static CURLcode h2_submit(struct h2_stream_ctx **pstream, + struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, + bool eos, size_t *pnwritten) { struct cf_h2_ctx *ctx = cf->ctx; struct h2_stream_ctx *stream = NULL; @@ -2274,36 +2225,34 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream, int32_t stream_id; nghttp2_priority_spec pri_spec; ssize_t nwritten; + CURLcode result = CURLE_OK; + *pnwritten = 0; Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); - *err = http2_data_setup(cf, data, &stream); - if(*err) { - nwritten = -1; + result = http2_data_setup(cf, data, &stream); + if(result) goto out; - } - nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); + nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, &result); if(nwritten < 0) goto out; + *pnwritten = (size_t)nwritten; if(!stream->h1.done) { /* need more data */ goto out; } DEBUGASSERT(stream->h1.req); - *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); - if(*err) { - nwritten = -1; + result = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); + if(result) goto out; - } /* no longer needed */ Curl_h1_req_parse_free(&stream->h1); nva = Curl_dynhds_to_nva(&h2_headers, &nheader); if(!nva) { - *err = CURLE_OUT_OF_MEMORY; - nwritten = -1; + result = CURLE_OUT_OF_MEMORY; goto out; } @@ -2329,8 +2278,7 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream, if(stream_id < 0) { CURL_TRC_CF(data, cf, "send: nghttp2_submit_request error (%s)%u", nghttp2_strerror(stream_id), stream_id); - *err = CURLE_SEND_ERROR; - nwritten = -1; + result = CURLE_SEND_ERROR; goto out; } @@ -2357,48 +2305,46 @@ static ssize_t h2_submit(struct h2_stream_ctx **pstream, stream->id = stream_id; - body = (const char *)buf + nwritten; - bodylen = len - nwritten; + body = (const char *)buf + *pnwritten; + bodylen = len - *pnwritten; if(bodylen || eos) { - ssize_t n = cf_h2_body_send(cf, data, stream, body, bodylen, eos, err); + ssize_t n = cf_h2_body_send(cf, data, stream, body, bodylen, eos, &result); if(n >= 0) - nwritten += n; - else if(*err == CURLE_AGAIN) - *err = CURLE_OK; - else if(*err != CURLE_AGAIN) { - *err = CURLE_SEND_ERROR; - nwritten = -1; - goto out; + *pnwritten += n; + else if(result == CURLE_AGAIN) + result = CURLE_OK; + else { + result = CURLE_SEND_ERROR; } } out: - CURL_TRC_CF(data, cf, "[%d] submit -> %zd, %d", - stream ? stream->id : -1, nwritten, *err); + CURL_TRC_CF(data, cf, "[%d] submit -> %d, %zu", + stream ? stream->id : -1, result, *pnwritten); Curl_safefree(nva); *pstream = stream; Curl_dynhds_free(&h2_headers); - return nwritten; + return result; } -static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, - CURLcode *err) +static CURLcode cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, bool eos, + size_t *pnwritten) { struct cf_h2_ctx *ctx = cf->ctx; struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data); struct cf_call_data save; ssize_t nwritten; - CURLcode result; + CURLcode result = CURLE_OK, r2; CF_DATA_SAVE(save, cf, data); + *pnwritten = 0; if(!stream || stream->id == -1) { - nwritten = h2_submit(&stream, cf, data, buf, len, eos, err); - if(nwritten < 0) { + result = h2_submit(&stream, cf, data, buf, len, eos, pnwritten); + if(result) goto out; - } DEBUGASSERT(stream); } else if(stream->body_eos) { @@ -2407,35 +2353,35 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, * to trigger flushing again. * If this works, we report to have written `len` bytes. */ DEBUGASSERT(eos); - nwritten = cf_h2_body_send(cf, data, stream, buf, 0, eos, err); + nwritten = cf_h2_body_send(cf, data, stream, buf, 0, eos, &result); CURL_TRC_CF(data, cf, "[%d] cf_body_send last CHUNK -> %zd, %d, eos=%d", - stream->id, nwritten, *err, eos); + stream->id, nwritten, result, eos); if(nwritten < 0) { goto out; } - nwritten = len; + *pnwritten = len; } else { - nwritten = cf_h2_body_send(cf, data, stream, buf, len, eos, err); + nwritten = cf_h2_body_send(cf, data, stream, buf, len, eos, &result); CURL_TRC_CF(data, cf, "[%d] cf_body_send(len=%zu) -> %zd, %d, eos=%d", - stream->id, len, nwritten, *err, eos); + stream->id, len, nwritten, result, eos); + if(nwritten >= 0) + *pnwritten = (size_t)nwritten; } /* Call the nghttp2 send loop and flush to write ALL buffered data, * headers and/or request body completely out to the network */ - result = h2_progress_egress(cf, data); + r2 = h2_progress_egress(cf, data); /* if the stream has been closed in egress handling (nghttp2 does that * when it does not like the headers, for example */ if(stream && stream->closed) { infof(data, "stream %u closed", stream->id); - *err = CURLE_SEND_ERROR; - nwritten = -1; + result = CURLE_SEND_ERROR; goto out; } - else if(result && (result != CURLE_AGAIN)) { - *err = result; - nwritten = -1; + else if(r2 && (r2 != CURLE_AGAIN)) { + result = r2; goto out; } @@ -2443,21 +2389,20 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* nghttp2 thinks this session is done. If the stream has not been * closed, this is an error state for out transfer */ if(stream && stream->closed) { - nwritten = http2_handle_stream_close(cf, data, stream, err); + result = http2_handle_stream_close(cf, data, stream, pnwritten); } else { CURL_TRC_CF(data, cf, "send: nothing to do in this session"); - *err = CURLE_HTTP2; - nwritten = -1; + result = CURLE_HTTP2; } } out: if(stream) { - CURL_TRC_CF(data, cf, "[%d] cf_send(len=%zu) -> %zd, %d, " + 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, nwritten, *err, + stream->id, len, result, *pnwritten, stream->body_eos, nghttp2_session_get_stream_remote_window_size( ctx->h2, stream->id), @@ -2466,14 +2411,14 @@ out: Curl_bufq_len(&ctx->outbufq)); } else { - CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %zd, %d, " + CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %d, %zu, " "connection-window=%d, nw_send_buffer(%zu)", - len, nwritten, *err, + len, result, *pnwritten, nghttp2_session_get_remote_window_size(ctx->h2), Curl_bufq_len(&ctx->outbufq)); } CF_DATA_RESTORE(cf, save); - return nwritten; + return result; } static CURLcode cf_h2_flush(struct Curl_cfilter *cf, @@ -2716,8 +2661,7 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf, * not. We may have already buffered and exhausted the new window * by operating on things in flight during the handling of other * transfers. */ - drain_stream(cf, data, stream); - Curl_expire(data, 0, EXPIRE_RUN_NOW); + Curl_multi_mark_dirty(data); } CURL_TRC_CF(data, cf, "[%d] stream now %spaused", stream->id, pause ? "" : "un"); @@ -2769,15 +2713,16 @@ static bool cf_h2_is_alive(struct Curl_cfilter *cf, bool *input_pending) { struct cf_h2_ctx *ctx = cf->ctx; - CURLcode result; + bool alive; struct cf_call_data save; + *input_pending = FALSE; CF_DATA_SAVE(save, cf, data); - result = (ctx && ctx->h2 && http2_connisalive(cf, data, input_pending)); + alive = (ctx && ctx->h2 && http2_connisalive(cf, data, input_pending)); CURL_TRC_CF(data, cf, "conn alive -> %d, input_pending=%d", - result, *input_pending); + alive, *input_pending); CF_DATA_RESTORE(cf, save); - return result; + return alive; } static CURLcode cf_h2_keep_alive(struct Curl_cfilter *cf, @@ -2805,7 +2750,7 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf, DEBUGASSERT(pres1); CF_DATA_SAVE(save, cf, data); - if(nghttp2_session_check_request_allowed(ctx->h2) == 0) { + if(!ctx->h2 || !nghttp2_session_check_request_allowed(ctx->h2)) { /* the limit is what we have in use right now */ effective_max = CONN_ATTACHED(cf->conn); } @@ -2848,7 +2793,6 @@ struct Curl_cftype Curl_cft_nghttp2 = { cf_h2_connect, cf_h2_close, cf_h2_shutdown, - Curl_cf_def_get_host, cf_h2_adjust_pollset, cf_h2_data_pending, cf_h2_send, @@ -3001,17 +2945,17 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data, /* Remaining data from the protocol switch reply is already using * the switched protocol, ie. HTTP/2. We add that to the network * inbufq. */ - ssize_t copied; + size_t copied; - copied = Curl_bufq_write(&ctx->inbufq, - (const unsigned char *)mem, nread, &result); - if(copied < 0) { + result = Curl_bufq_write(&ctx->inbufq, + (const unsigned char *)mem, nread, &copied); + if(result) { failf(data, "error on copying HTTP Upgrade response: %d", result); return CURLE_RECV_ERROR; } - if((size_t)copied < nread) { + if(copied < nread) { failf(data, "connection buffer size could not take all data " - "from HTTP Upgrade response header: copied=%zd, datalen=%zu", + "from HTTP Upgrade response header: copied=%zu, datalen=%zu", copied, nread); return CURLE_HTTP2; } diff --git a/lib/http_aws_sigv4.c b/lib/http_aws_sigv4.c index 7425382359..92c0c1ff4c 100644 --- a/lib/http_aws_sigv4.c +++ b/lib/http_aws_sigv4.c @@ -537,8 +537,8 @@ static int compare_func(const void *a, const void *b) } UNITTEST CURLcode canon_path(const char *q, size_t len, - struct dynbuf *new_path, - bool do_uri_encode) + struct dynbuf *new_path, + bool do_uri_encode) { CURLcode result = CURLE_OK; diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index 5bec3b39ff..2c0b7e16d7 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -39,6 +39,20 @@ #include "curl_memory.h" #include "memdebug.h" + +static void http_auth_nego_reset(struct connectdata *conn, + struct negotiatedata *neg_ctx, + bool proxy) +{ + if(proxy) + conn->proxy_negotiate_state = GSS_AUTHNONE; + else + conn->http_negotiate_state = GSS_AUTHNONE; + if(neg_ctx) + Curl_auth_cleanup_spnego(neg_ctx); +} + + CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, bool proxy, const char *header) { @@ -62,7 +76,6 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, service = data->set.str[STRING_PROXY_SERVICE_NAME] ? data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; host = conn->http_proxy.host.name; - neg_ctx = &conn->proxyneg; state = conn->proxy_negotiate_state; #else return CURLE_NOT_BUILT_IN; @@ -74,10 +87,13 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, service = data->set.str[STRING_SERVICE_NAME] ? data->set.str[STRING_SERVICE_NAME] : "HTTP"; host = conn->host.name; - neg_ctx = &conn->negotiate; state = conn->http_negotiate_state; } + neg_ctx = Curl_auth_nego_get(conn, proxy); + if(!neg_ctx) + return CURLE_OUT_OF_MEMORY; + /* Not set means empty */ if(!userp) userp = ""; @@ -94,12 +110,12 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, if(!len) { if(state == GSS_AUTHSUCC) { infof(data, "Negotiate auth restarted"); - Curl_http_auth_cleanup_negotiate(conn); + http_auth_nego_reset(conn, neg_ctx, proxy); } else if(state != GSS_AUTHNONE) { /* The server rejected our authentication and has not supplied any more negotiation mechanisms */ - Curl_http_auth_cleanup_negotiate(conn); + http_auth_nego_reset(conn, neg_ctx, proxy); return CURLE_LOGIN_DENIED; } } @@ -116,7 +132,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, result = Curl_ssl_get_channel_binding( data, FIRSTSOCKET, &neg_ctx->channel_binding_data); if(result) { - Curl_http_auth_cleanup_negotiate(conn); + http_auth_nego_reset(conn, neg_ctx, proxy); return result; } } @@ -134,7 +150,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, #endif if(result) - Curl_http_auth_cleanup_negotiate(conn); + http_auth_nego_reset(conn, neg_ctx, proxy); return result; } @@ -152,7 +168,6 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data, if(proxy) { #ifndef CURL_DISABLE_PROXY - neg_ctx = &conn->proxyneg; authp = &data->state.authproxy; state = &conn->proxy_negotiate_state; #else @@ -160,10 +175,12 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data, #endif } else { - neg_ctx = &conn->negotiate; authp = &data->state.authhost; state = &conn->http_negotiate_state; } + neg_ctx = Curl_auth_nego_get(conn, proxy); + if(!neg_ctx) + return CURLE_OUT_OF_MEMORY; authp->done = FALSE; @@ -184,7 +201,7 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data, if(neg_ctx->noauthpersist && *state == GSS_AUTHSUCC) { infof(data, "Curl_output_negotiate, " "no persistent authentication: cleanup existing context"); - Curl_http_auth_cleanup_negotiate(conn); + http_auth_nego_reset(conn, neg_ctx, proxy); } if(!neg_ctx->context) { result = Curl_input_negotiate(data, conn, proxy, "Negotiate"); @@ -249,13 +266,4 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data, return CURLE_OK; } -void Curl_http_auth_cleanup_negotiate(struct connectdata *conn) -{ - conn->http_negotiate_state = GSS_AUTHNONE; - conn->proxy_negotiate_state = GSS_AUTHNONE; - - Curl_auth_cleanup_spnego(&conn->negotiate); - Curl_auth_cleanup_spnego(&conn->proxyneg); -} - #endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */ diff --git a/lib/http_negotiate.h b/lib/http_negotiate.h index 76d8356132..ad7c43d833 100644 --- a/lib/http_negotiate.h +++ b/lib/http_negotiate.h @@ -34,10 +34,6 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, CURLcode Curl_output_negotiate(struct Curl_easy *data, struct connectdata *conn, bool proxy); -void Curl_http_auth_cleanup_negotiate(struct connectdata *conn); - -#else /* !CURL_DISABLE_HTTP && USE_SPNEGO */ -#define Curl_http_auth_cleanup_negotiate(x) #endif #endif /* HEADER_CURL_HTTP_NEGOTIATE_H */ diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index e6a3b175a7..0dff8f3a00 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -60,17 +60,18 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data, header */ { /* point to the correct struct with this */ - struct ntlmdata *ntlm; curlntlm *state; CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; - ntlm = proxy ? &conn->proxyntlm : &conn->ntlm; state = proxy ? &conn->proxy_ntlm_state : &conn->http_ntlm_state; if(checkprefix("NTLM", header)) { - header += strlen("NTLM"); + struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, proxy); + if(!ntlm) + return CURLE_FAILED_INIT; + header += strlen("NTLM"); curlx_str_passblanks(&header); if(*header) { unsigned char *hdr; @@ -93,11 +94,11 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data, else { if(*state == NTLMSTATE_LAST) { infof(data, "NTLM auth restarted"); - Curl_http_auth_cleanup_ntlm(conn); + Curl_auth_ntlm_remove(conn, proxy); } else if(*state == NTLMSTATE_TYPE3) { infof(data, "NTLM handshake rejected"); - Curl_http_auth_cleanup_ntlm(conn); + Curl_auth_ntlm_remove(conn, proxy); *state = NTLMSTATE_NONE; return CURLE_REMOTE_ACCESS_DENIED; } @@ -150,7 +151,6 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) service = data->set.str[STRING_PROXY_SERVICE_NAME] ? data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; hostname = conn->http_proxy.host.name; - ntlm = &conn->proxyntlm; state = &conn->proxy_ntlm_state; authp = &data->state.authproxy; #else @@ -164,10 +164,12 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) service = data->set.str[STRING_SERVICE_NAME] ? data->set.str[STRING_SERVICE_NAME] : "HTTP"; hostname = conn->host.name; - ntlm = &conn->ntlm; state = &conn->http_ntlm_state; authp = &data->state.authhost; } + ntlm = Curl_auth_ntlm_get(conn, proxy); + if(!ntlm) + return CURLE_OUT_OF_MEMORY; authp->done = FALSE; /* not set means empty */ @@ -178,10 +180,10 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) passwdp = ""; #ifdef USE_WINDOWS_SSPI - if(!Curl_hSecDll) { + if(!Curl_pSecFn) { /* not thread safe and leaks - use curl_global_init() to avoid */ CURLcode err = Curl_sspi_global_init(); - if(!Curl_hSecDll) + if(!Curl_pSecFn) return err; } #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS @@ -200,9 +202,8 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) case NTLMSTATE_TYPE1: default: /* for the weird cases we (re)start here */ /* Create a type-1 message */ - result = Curl_auth_create_ntlm_type1_message(data, userp, passwdp, - service, hostname, - ntlm, &ntlmmsg); + result = Curl_auth_create_ntlm_type1_message(data, userp, passwdp, service, + hostname, ntlm, &ntlmmsg); if(!result) { DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0); result = curlx_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg), @@ -258,10 +259,4 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy) return result; } -void Curl_http_auth_cleanup_ntlm(struct connectdata *conn) -{ - Curl_auth_cleanup_ntlm(&conn->ntlm); - Curl_auth_cleanup_ntlm(&conn->proxyntlm); -} - #endif /* !CURL_DISABLE_HTTP && USE_NTLM */ diff --git a/lib/http_ntlm.h b/lib/http_ntlm.h index c1cf05701f..b38ff82dba 100644 --- a/lib/http_ntlm.h +++ b/lib/http_ntlm.h @@ -35,10 +35,6 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data, bool proxy, /* this is for creating NTLM header output */ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy); -void Curl_http_auth_cleanup_ntlm(struct connectdata *conn); - -#else /* !CURL_DISABLE_HTTP && USE_NTLM */ -#define Curl_http_auth_cleanup_ntlm(x) #endif #endif /* HEADER_CURL_HTTP_NTLM_H */ diff --git a/lib/http_proxy.c b/lib/http_proxy.c index 3df6329c06..6f435a8674 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -38,7 +38,6 @@ #include "cf-h1-proxy.h" #include "cf-h2-proxy.h" #include "connect.h" -#include "strcase.h" #include "vtls/vtls.h" #include "transfer.h" #include "multiif.h" @@ -53,7 +52,7 @@ static bool hd_name_eq(const char *n1, size_t n1len, const char *n2, size_t n2len) { - return (n1len == n2len) ? strncasecompare(n1, n2, n1len) : FALSE; + return (n1len == n2len) ? curl_strnequal(n1, n2, n1len) : FALSE; } static CURLcode dynhds_add_custom(struct Curl_easy *data, @@ -383,21 +382,21 @@ out: return result; } -void Curl_cf_http_proxy_get_host(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char **phost, - const char **pdisplay_host, - int *pport) +CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) { - (void)data; - if(!cf->connected) { - *phost = cf->conn->http_proxy.host.name; - *pdisplay_host = cf->conn->http_proxy.host.dispname; - *pport = (int)cf->conn->http_proxy.port; - } - else { - cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport); + switch(query) { + case CF_QUERY_HOST_PORT: + *pres1 = (int)cf->conn->http_proxy.port; + *((const char **)pres2) = cf->conn->http_proxy.host.name; + return CURLE_OK; + default: + break; } + return cf->next ? + cf->next->cft->query(cf->next, data, query, pres1, pres2) : + CURLE_UNKNOWN_OPTION; } static void http_proxy_cf_destroy(struct Curl_cfilter *cf, @@ -443,7 +442,6 @@ struct Curl_cftype Curl_cft_http_proxy = { http_proxy_cf_connect, http_proxy_cf_close, Curl_cf_def_shutdown, - Curl_cf_http_proxy_get_host, Curl_cf_def_adjust_pollset, Curl_cf_def_data_pending, Curl_cf_def_send, @@ -451,7 +449,7 @@ struct Curl_cftype Curl_cft_http_proxy = { Curl_cf_def_cntrl, Curl_cf_def_conn_is_alive, Curl_cf_def_conn_keep_alive, - Curl_cf_def_query, + Curl_cf_http_proxy_query, }; CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, diff --git a/lib/http_proxy.h b/lib/http_proxy.h index 2e91ff2037..e6ab2bacf3 100644 --- a/lib/http_proxy.h +++ b/lib/http_proxy.h @@ -48,18 +48,16 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq, /* Default proxy timeout in milliseconds */ #define PROXY_TIMEOUT (3600*1000) -void Curl_cf_http_proxy_get_host(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char **phost, - const char **pdisplay_host, - int *pport); +CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2); CURLcode Curl_cf_http_proxy_insert_after(struct Curl_cfilter *cf_at, struct Curl_easy *data); extern struct Curl_cftype Curl_cft_http_proxy; -#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ +#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */ #define IS_HTTPS_PROXY(t) (((t) == CURLPROXY_HTTPS) || \ ((t) == CURLPROXY_HTTPS2)) diff --git a/lib/if2ip.c b/lib/if2ip.c index 6da68efd50..d685290889 100644 --- a/lib/if2ip.c +++ b/lib/if2ip.c @@ -52,8 +52,7 @@ # include #endif -#include "inet_ntop.h" -#include "strcase.h" +#include "curlx/inet_ntop.h" #include "if2ip.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -117,7 +116,7 @@ if2ip_result_t Curl_if2ip(int af, for(iface = head; iface != NULL; iface = iface->ifa_next) { if(iface->ifa_addr) { if(iface->ifa_addr->sa_family == af) { - if(strcasecompare(iface->ifa_name, interf)) { + if(curl_strequal(iface->ifa_name, interf)) { void *addr; const char *ip; char scope[12] = ""; @@ -162,13 +161,13 @@ if2ip_result_t Curl_if2ip(int af, addr = &((struct sockaddr_in *)(void *)iface->ifa_addr)->sin_addr; res = IF2IP_FOUND; - ip = Curl_inet_ntop(af, addr, ipstr, sizeof(ipstr)); + ip = curlx_inet_ntop(af, addr, ipstr, sizeof(ipstr)); msnprintf(buf, buf_size, "%s%s", ip, scope); break; } } else if((res == IF2IP_NOT_FOUND) && - strcasecompare(iface->ifa_name, interf)) { + curl_strequal(iface->ifa_name, interf)) { res = IF2IP_AF_NOT_SUPPORTED; } } @@ -235,7 +234,7 @@ if2ip_result_t Curl_if2ip(int af, s = (struct sockaddr_in *)(void *)&req.ifr_addr; memcpy(&in, &s->sin_addr, sizeof(in)); - r = Curl_inet_ntop(s->sin_family, &in, buf, buf_size); + r = curlx_inet_ntop(s->sin_family, &in, buf, buf_size); sclose(dummy); if(!r) diff --git a/lib/imap.c b/lib/imap.c index fc38a5b61e..f25176bb9e 100644 --- a/lib/imap.c +++ b/lib/imap.c @@ -302,7 +302,7 @@ static bool imap_matchresp(const char *line, size_t len, const char *cmd) /* Does the command name match and is it followed by a space character or at the end of line? */ - if(line + cmd_len <= end && strncasecompare(line, cmd, cmd_len) && + if(line + cmd_len <= end && curl_strnequal(line, cmd, cmd_len) && (line[cmd_len] == ' ' || line + cmd_len + 2 == end)) return TRUE; @@ -358,16 +358,16 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn, case IMAP_LIST: if((!imap->custom && !imap_matchresp(line, len, "LIST")) || (imap->custom && !imap_matchresp(line, len, imap->custom) && - (!strcasecompare(imap->custom, "STORE") || + (!curl_strequal(imap->custom, "STORE") || !imap_matchresp(line, len, "FETCH")) && - !strcasecompare(imap->custom, "SELECT") && - !strcasecompare(imap->custom, "EXAMINE") && - !strcasecompare(imap->custom, "SEARCH") && - !strcasecompare(imap->custom, "EXPUNGE") && - !strcasecompare(imap->custom, "LSUB") && - !strcasecompare(imap->custom, "UID") && - !strcasecompare(imap->custom, "GETQUOTAROOT") && - !strcasecompare(imap->custom, "NOOP"))) + !curl_strequal(imap->custom, "SELECT") && + !curl_strequal(imap->custom, "EXAMINE") && + !curl_strequal(imap->custom, "SEARCH") && + !curl_strequal(imap->custom, "EXPUNGE") && + !curl_strequal(imap->custom, "LSUB") && + !curl_strequal(imap->custom, "UID") && + !curl_strequal(imap->custom, "GETQUOTAROOT") && + !curl_strequal(imap->custom, "NOOP"))) return FALSE; break; @@ -1239,7 +1239,7 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data, else if(imapcode == IMAP_RESP_OK) { /* Check if the UIDVALIDITY has been specified and matches */ if(imap->uidvalidity && imapc->mailbox_uidvalidity && - !strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity)) { + !curl_strequal(imap->uidvalidity, imapc->mailbox_uidvalidity)) { failf(data, "Mailbox UIDVALIDITY has changed"); result = CURLE_REMOTE_FILE_NOT_FOUND; } @@ -1347,9 +1347,6 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data, else { /* IMAP download */ data->req.maxdownload = size; - /* force a recv/send check of this connection, as the data might've been - read off the socket already */ - data->state.select_bits = CURL_CSELECT_IN; Curl_xfer_setup1(data, CURL_XFER_RECV, size, FALSE); } } @@ -1693,9 +1690,9 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected, /* Determine if the requested mailbox (with the same UIDVALIDITY if set) has already been selected on this connection */ if(imap->mailbox && imapc->mailbox && - strcasecompare(imap->mailbox, imapc->mailbox) && + curl_strequal(imap->mailbox, imapc->mailbox) && (!imap->uidvalidity || !imapc->mailbox_uidvalidity || - strcasecompare(imap->uidvalidity, imapc->mailbox_uidvalidity))) + curl_strequal(imap->uidvalidity, imapc->mailbox_uidvalidity))) selected = TRUE; /* Start the first command in the DO phase */ @@ -1780,18 +1777,14 @@ static CURLcode imap_disconnect(struct Curl_easy *data, (void)data; if(imapc) { /* We cannot send quit unconditionally. If this connection is stale or - bad in any way, sending quit and waiting around here will make the + bad in any way (pingpong has pending data to send), + sending quit and waiting around here will make the disconnect wait in vain and cause more problems than we need to. */ - - /* The IMAP session may or may not have been allocated/setup at this - point! */ - if(!dead_connection && conn->bits.protoconnstart) { + if(!dead_connection && conn->bits.protoconnstart && + !Curl_pp_needs_flush(data, &imapc->pp)) { if(!imap_perform_logout(data, imapc)) (void)imap_block_statemach(data, imapc, TRUE); /* ignore errors */ } - - /* Cleanup the SASL module */ - Curl_sasl_cleanup(conn, imapc->sasl.authused); } return CURLE_OK; } @@ -2079,12 +2072,12 @@ static CURLcode imap_parse_url_options(struct connectdata *conn, while(*ptr && *ptr != ';') ptr++; - if(strncasecompare(key, "AUTH=+LOGIN", 11)) { + if(curl_strnequal(key, "AUTH=+LOGIN", 11)) { /* User prefers plaintext LOGIN over any SASL, including SASL LOGIN */ prefer_login = TRUE; imapc->sasl.prefmech = SASL_AUTH_NONE; } - else if(strncasecompare(key, "AUTH=", 5)) { + else if(curl_strnequal(key, "AUTH=", 5)) { prefer_login = FALSE; result = Curl_sasl_parse_url_auth_option(&imapc->sasl, value, ptr - value); @@ -2189,35 +2182,35 @@ static CURLcode imap_parse_url_path(struct Curl_easy *data, PARTIAL) stripping of the trailing slash character if it is present. Note: Unknown parameters trigger a URL_MALFORMAT error. */ - if(strcasecompare(name, "UIDVALIDITY") && !imap->uidvalidity) { + if(curl_strequal(name, "UIDVALIDITY") && !imap->uidvalidity) { if(valuelen > 0 && value[valuelen - 1] == '/') value[valuelen - 1] = '\0'; imap->uidvalidity = value; value = NULL; } - else if(strcasecompare(name, "UID") && !imap->uid) { + else if(curl_strequal(name, "UID") && !imap->uid) { if(valuelen > 0 && value[valuelen - 1] == '/') value[valuelen - 1] = '\0'; imap->uid = value; value = NULL; } - else if(strcasecompare(name, "MAILINDEX") && !imap->mindex) { + else if(curl_strequal(name, "MAILINDEX") && !imap->mindex) { if(valuelen > 0 && value[valuelen - 1] == '/') value[valuelen - 1] = '\0'; imap->mindex = value; value = NULL; } - else if(strcasecompare(name, "SECTION") && !imap->section) { + else if(curl_strequal(name, "SECTION") && !imap->section) { if(valuelen > 0 && value[valuelen - 1] == '/') value[valuelen - 1] = '\0'; imap->section = value; value = NULL; } - else if(strcasecompare(name, "PARTIAL") && !imap->partial) { + else if(curl_strequal(name, "PARTIAL") && !imap->partial) { if(valuelen > 0 && value[valuelen - 1] == '/') value[valuelen - 1] = '\0'; diff --git a/lib/krb5.c b/lib/krb5.c index 9ebba4e3d1..b5effa2a1b 100644 --- a/lib/krb5.c +++ b/lib/krb5.c @@ -56,7 +56,6 @@ #include "transfer.h" #include "curl_krb5.h" #include "curlx/warnless.h" -#include "strcase.h" #include "strdup.h" /* The last 3 #include files should be in this order */ @@ -215,12 +214,14 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) gss_ctx_id_t *context = app_data; struct gss_channel_bindings_struct chan; size_t base64_sz = 0; - struct sockaddr_in *remote_addr = - (struct sockaddr_in *)CURL_UNCONST(&conn->remote_addr->curl_sa_addr); + const struct Curl_sockaddr_ex *remote_addr = + Curl_conn_get_remote_addr(data, FIRSTSOCKET); + struct sockaddr_in *remote_in_addr = remote_addr ? + (struct sockaddr_in *)CURL_UNCONST(&remote_addr->curl_sa_addr) : NULL; char *stringp; struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN); - if(!ftpc) + if(!ftpc || !remote_in_addr) return -2; if(getsockname(conn->sock[FIRSTSOCKET], @@ -232,7 +233,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn) chan.initiator_address.value = &conn->local_addr.sin_addr.s_addr; chan.acceptor_addrtype = GSS_C_AF_INET; chan.acceptor_address.length = l - 4; - chan.acceptor_address.value = &remote_addr->sin_addr.s_addr; + chan.acceptor_address.value = &remote_in_addr->sin_addr.s_addr; chan.application_data.length = 0; chan.application_data.value = NULL; @@ -384,7 +385,8 @@ static void krb5_end(void *app_data) OM_uint32 min; gss_ctx_id_t *context = app_data; if(*context != GSS_C_NO_CONTEXT) { - OM_uint32 maj = gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER); + OM_uint32 maj = Curl_gss_delete_sec_context(&min, context, + GSS_C_NO_BUFFER); (void)maj; DEBUGASSERT(maj == GSS_S_COMPLETE); } @@ -479,19 +481,18 @@ socket_read(struct Curl_easy *data, int sockindex, void *to, size_t len) { char *to_p = to; CURLcode result; - ssize_t nread = 0; + size_t nread = 0; while(len > 0) { result = Curl_conn_recv(data, sockindex, to_p, len, &nread); - if(nread > 0) { - len -= nread; - to_p += nread; - } - else { - if(result == CURLE_AGAIN) - continue; + if(result == CURLE_AGAIN) + continue; + if(result) return result; - } + if(nread > len) + return CURLE_RECV_ERROR; + len -= nread; + to_p += nread; } return CURLE_OK; } @@ -523,8 +524,8 @@ socket_write(struct Curl_easy *data, int sockindex, const void *to, return CURLE_OK; } -static CURLcode read_data(struct Curl_easy *data, int sockindex, - struct krb5buffer *buf) +static CURLcode krb5_read_data(struct Curl_easy *data, int sockindex, + struct krb5buffer *buf) { struct connectdata *conn = data->conn; int len; @@ -579,52 +580,49 @@ buffer_read(struct krb5buffer *buf, void *data, size_t len) } /* Matches Curl_recv signature */ -static ssize_t sec_recv(struct Curl_easy *data, int sockindex, - char *buffer, size_t len, CURLcode *err) +static CURLcode sec_recv(struct Curl_easy *data, int sockindex, + char *buffer, size_t len, size_t *pnread) { - size_t bytes_read; - size_t total_read = 0; struct connectdata *conn = data->conn; - - *err = CURLE_OK; + CURLcode result = CURLE_OK; + size_t bytes_read; /* Handle clear text response. */ - if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR) { - ssize_t nread; - *err = Curl_conn_recv(data, sockindex, buffer, len, &nread); - return nread; - } + if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR) + return Curl_conn_recv(data, sockindex, buffer, len, pnread); if(conn->in_buffer.eof_flag) { conn->in_buffer.eof_flag = 0; - return 0; + *pnread = 0; + return CURLE_OK; } bytes_read = buffer_read(&conn->in_buffer, buffer, len); - len -= bytes_read; - total_read += bytes_read; buffer += bytes_read; + len -= bytes_read; + *pnread += bytes_read; while(len > 0) { - if(read_data(data, sockindex, &conn->in_buffer)) - return -1; + result = krb5_read_data(data, sockindex, &conn->in_buffer); + if(result) + return result; if(curlx_dyn_len(&conn->in_buffer.buf) == 0) { - if(bytes_read > 0) + if(*pnread > 0) conn->in_buffer.eof_flag = 1; - return bytes_read; + return result; } bytes_read = buffer_read(&conn->in_buffer, buffer, len); - len -= bytes_read; - total_read += bytes_read; buffer += bytes_read; + len -= bytes_read; + *pnread += bytes_read; } - return total_read; + return result; } /* Send |length| bytes from |from| to the |sockindex| socket taking care of encoding and negotiating with the server. |from| can be NULL. */ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn, - int sockindex, const char *from, int length) + int sockindex, const char *from, size_t length) { int bytes, htonl_bytes; /* 32-bit integers for htonl */ char *buffer = NULL; @@ -642,8 +640,8 @@ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn, else prot_level = conn->command_prot; } - bytes = conn->mech->encode(conn->app_data, from, length, (int)prot_level, - (void **)&buffer); + bytes = conn->mech->encode(conn->app_data, from, (int)length, + (int)prot_level, (void **)&buffer); if(!buffer || bytes <= 0) return; /* error */ @@ -677,34 +675,36 @@ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn, free(buffer); } -static ssize_t sec_write(struct Curl_easy *data, struct connectdata *conn, - int sockindex, const char *buffer, size_t length) +static CURLcode sec_write(struct Curl_easy *data, int sockindex, + const char *buffer, size_t length, + size_t *pnwritten) { - ssize_t tx = 0, len = conn->buffer_size; + struct connectdata *conn = data->conn; + size_t len = conn->buffer_size; + *pnwritten = 0; if(len <= 0) len = length; while(length) { - if(length < (size_t)len) + if(length < len) len = length; - do_sec_send(data, conn, sockindex, buffer, curlx_sztosi(len)); + /* WTF: this ignores all errors writing to the socket */ + do_sec_send(data, conn, sockindex, buffer, len); length -= len; buffer += len; - tx += len; + *pnwritten += len; } - return tx; + return CURLE_OK; } /* Matches Curl_send signature */ -static ssize_t sec_send(struct Curl_easy *data, int sockindex, - const void *buffer, size_t len, bool eos, - CURLcode *err) +static CURLcode sec_send(struct Curl_easy *data, int sockindex, + const void *buffer, size_t len, bool eos, + size_t *pnwritten) { - struct connectdata *conn = data->conn; (void)eos; /* unused */ - *err = CURLE_OK; - return sec_write(data, conn, sockindex, buffer, len); + return sec_write(data, sockindex, buffer, len, pnwritten); } int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn, diff --git a/lib/ldap.c b/lib/ldap.c index c1be2f4bda..40e72af0d7 100644 --- a/lib/ldap.c +++ b/lib/ldap.c @@ -88,7 +88,6 @@ #include "escape.h" #include "progress.h" #include "transfer.h" -#include "strcase.h" #include "curlx/strparse.h" #include "curl_ldap.h" #include "curlx/multibyte.h" @@ -393,7 +392,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done) if(conn->ssl_config.verifypeer) { /* OpenLDAP SDK supports BASE64 files. */ if((data->set.ssl.cert_type) && - (!strcasecompare(data->set.ssl.cert_type, "PEM"))) { + (!curl_strequal(data->set.ssl.cert_type, "PEM"))) { failf(data, "LDAP local: ERROR OpenLDAP only supports PEM cert-type"); result = CURLE_SSL_CERTPROBLEM; goto quit; @@ -748,15 +747,15 @@ static void _ldap_trace(const char *fmt, ...) */ static int str2scope(const char *p) { - if(strcasecompare(p, "one")) + if(curl_strequal(p, "one")) return LDAP_SCOPE_ONELEVEL; - if(strcasecompare(p, "onetree")) + if(curl_strequal(p, "onetree")) return LDAP_SCOPE_ONELEVEL; - if(strcasecompare(p, "base")) + if(curl_strequal(p, "base")) return LDAP_SCOPE_BASE; - if(strcasecompare(p, "sub")) + if(curl_strequal(p, "sub")) return LDAP_SCOPE_SUBTREE; - if(strcasecompare(p, "subtree")) + if(curl_strequal(p, "subtree")) return LDAP_SCOPE_SUBTREE; return -1; } @@ -801,7 +800,7 @@ static int _ldap_url_parse2(struct Curl_easy *data, if(!data || !data->state.up.path || data->state.up.path[0] != '/' || - !strncasecompare("LDAP", data->state.up.scheme, 4)) + !curl_strnequal("LDAP", data->state.up.scheme, 4)) return LDAP_INVALID_SYNTAX; ludp->lud_scope = LDAP_SCOPE_BASE; diff --git a/lib/llist.c b/lib/llist.c index 82934425cf..c9a7d4a8a7 100644 --- a/lib/llist.c +++ b/lib/llist.c @@ -181,7 +181,8 @@ void *Curl_node_take_elem(struct Curl_llist_node *e) /* * @unittest: 1300 */ -void +UNITTEST void Curl_node_uremove(struct Curl_llist_node *, void *); +UNITTEST void Curl_node_uremove(struct Curl_llist_node *e, void *user) { struct Curl_llist *list; diff --git a/lib/llist.h b/lib/llist.h index 597c0e00a3..0a2bc9a62f 100644 --- a/lib/llist.h +++ b/lib/llist.h @@ -57,7 +57,6 @@ void Curl_llist_insert_next(struct Curl_llist *, struct Curl_llist_node *, const void *, struct Curl_llist_node *node); void Curl_llist_append(struct Curl_llist *, const void *, struct Curl_llist_node *node); -void Curl_node_uremove(struct Curl_llist_node *, void *); void Curl_node_remove(struct Curl_llist_node *); void Curl_llist_destroy(struct Curl_llist *, void *); @@ -76,7 +75,7 @@ size_t Curl_llist_count(struct Curl_llist *list); void *Curl_node_elem(struct Curl_llist_node *n); /* Remove the node from the list and return the custom data - * from a Curl_llist_node. Will NOT incoke a registered `dtor`. */ + * from a Curl_llist_node. Will NOT invoke a registered `dtor`. */ void *Curl_node_take_elem(struct Curl_llist_node *); /* Curl_node_next() returns the next element in a list from a given diff --git a/lib/memdebug.c b/lib/memdebug.c index b351726b32..bdcdcbb907 100644 --- a/lib/memdebug.c +++ b/lib/memdebug.c @@ -30,8 +30,6 @@ #include "urldata.h" -#define MEMDEBUG_NODEFINES /* do not redefine the standard functions */ - /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" @@ -70,7 +68,7 @@ static void curl_dbg_cleanup(void) if(curl_dbg_logfile && curl_dbg_logfile != stderr && curl_dbg_logfile != stdout) { - fclose(curl_dbg_logfile); + (fclose)(curl_dbg_logfile); } curl_dbg_logfile = NULL; } @@ -80,7 +78,11 @@ void curl_dbg_memdebug(const char *logname) { if(!curl_dbg_logfile) { if(logname && *logname) - curl_dbg_logfile = fopen(logname, FOPEN_WRITETEXT); +#ifdef CURL_FOPEN + curl_dbg_logfile = CURL_FOPEN(logname, FOPEN_WRITETEXT); +#else + curl_dbg_logfile = (fopen)(logname, FOPEN_WRITETEXT); +#endif else curl_dbg_logfile = stderr; #ifdef MEMDEBUG_LOG_SYNC @@ -302,14 +304,14 @@ void curl_dbg_free(void *ptr, int line, const char *source) } curl_socket_t curl_dbg_socket(int domain, int type, int protocol, - int line, const char *source) + int line, const char *source) { curl_socket_t sockfd; if(countcheck("socket", line, source)) return CURL_SOCKET_BAD; - sockfd = socket(domain, type, protocol); + sockfd = (socket)(domain, type, protocol); if(source && (sockfd != CURL_SOCKET_BAD)) curl_dbg_log("FD %s:%d socket() = %" FMT_SOCKET_T "\n", @@ -326,7 +328,7 @@ SEND_TYPE_RETV curl_dbg_send(SEND_TYPE_ARG1 sockfd, SEND_TYPE_RETV rc; if(countcheck("send", line, source)) return -1; - rc = send(sockfd, buf, len, flags); + rc = (send)(sockfd, buf, len, flags); if(source) curl_dbg_log("SEND %s:%d send(%lu) = %ld\n", source, line, (unsigned long)len, (long)rc); @@ -340,7 +342,7 @@ RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd, RECV_TYPE_ARG2 buf, RECV_TYPE_RETV rc; if(countcheck("recv", line, source)) return -1; - rc = recv(sockfd, buf, len, flags); + rc = (recv)(sockfd, buf, len, flags); if(source) curl_dbg_log("RECV %s:%d recv(%lu) = %ld\n", source, line, (unsigned long)len, (long)rc); @@ -349,10 +351,10 @@ RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd, RECV_TYPE_ARG2 buf, #ifdef HAVE_SOCKETPAIR int curl_dbg_socketpair(int domain, int type, int protocol, - curl_socket_t socket_vector[2], - int line, const char *source) + curl_socket_t socket_vector[2], + int line, const char *source) { - int res = socketpair(domain, type, protocol, socket_vector); + int res = (socketpair)(domain, type, protocol, socket_vector); if(source && (0 == res)) curl_dbg_log("FD %s:%d socketpair() = " @@ -364,12 +366,12 @@ int curl_dbg_socketpair(int domain, int type, int protocol, #endif curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen, - int line, const char *source) + int line, const char *source) { struct sockaddr *addr = (struct sockaddr *)saddr; curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen; - curl_socket_t sockfd = accept(s, addr, addrlen); + curl_socket_t sockfd = (accept)(s, addr, addrlen); if(source && (sockfd != CURL_SOCKET_BAD)) curl_dbg_log("FD %s:%d accept() = %" FMT_SOCKET_T "\n", @@ -386,7 +388,7 @@ curl_socket_t curl_dbg_accept4(curl_socket_t s, void *saddr, void *saddrlen, struct sockaddr *addr = (struct sockaddr *)saddr; curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen; - curl_socket_t sockfd = accept4(s, addr, addrlen, flags); + curl_socket_t sockfd = (accept4)(s, addr, addrlen, flags); if(source && (sockfd != CURL_SOCKET_BAD)) curl_dbg_log("FD %s:%d accept() = %" FMT_SOCKET_T "\n", @@ -407,7 +409,7 @@ void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source) /* this is our own defined way to close sockets on *ALL* platforms */ int curl_dbg_sclose(curl_socket_t sockfd, int line, const char *source) { - int res = sclose(sockfd); + int res = CURL_SCLOSE(sockfd); curl_dbg_mark_sclose(sockfd, line, source); return res; } @@ -416,7 +418,12 @@ ALLOC_FUNC FILE *curl_dbg_fopen(const char *file, const char *mode, int line, const char *source) { - FILE *res = fopen(file, mode); + FILE *res; +#ifdef CURL_FOPEN + res = CURL_FOPEN(file, mode); +#else + res = (fopen)(file, mode); +#endif if(source) curl_dbg_log("FILE %s:%d fopen(\"%s\",\"%s\") = %p\n", @@ -429,7 +436,7 @@ ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode, int line, const char *source) { - FILE *res = fdopen(filedes, mode); + FILE *res = (fdopen)(filedes, mode); if(source) curl_dbg_log("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n", source, line, filedes, mode, (void *)res); @@ -446,7 +453,7 @@ int curl_dbg_fclose(FILE *file, int line, const char *source) curl_dbg_log("FILE %s:%d fclose(%p)\n", source, line, (void *)file); - res = fclose(file); + res = (fclose)(file); return res; } @@ -469,7 +476,7 @@ void curl_dbg_log(const char *format, ...) nchars = (int)sizeof(buf) - 1; if(nchars > 0) - fwrite(buf, 1, (size_t)nchars, curl_dbg_logfile); + (fwrite)(buf, 1, (size_t)nchars, curl_dbg_logfile); } #endif /* CURLDEBUG */ diff --git a/lib/memdebug.h b/lib/memdebug.h index 11b250dea2..03b04b12dc 100644 --- a/lib/memdebug.h +++ b/lib/memdebug.h @@ -56,8 +56,6 @@ # define ALLOC_SIZE2(n, s) #endif -#define CURL_MT_LOGFNAME_BUFSIZE 512 - /* Avoid redundant redeclaration warnings with modern compilers, when including this header multiple times. */ #ifndef HEADER_CURL_MEMDEBUG_H_EXTERNS @@ -126,8 +124,6 @@ CURL_EXTERN ALLOC_FUNC #endif /* HEADER_CURL_MEMDEBUG_H_EXTERNS */ -#ifndef MEMDEBUG_NODEFINES - /* Set this symbol on the command-line, recompile all lib-sources */ #undef strdup #define strdup(ptr) curl_dbg_strdup(ptr, __LINE__, __FILE__) @@ -145,34 +141,29 @@ CURL_EXTERN ALLOC_FUNC #define recv(a,b,c,d) curl_dbg_recv(a,b,c,d, __LINE__, __FILE__) #ifdef _WIN32 -# ifdef UNICODE -# undef wcsdup -# define wcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__) -# undef _wcsdup -# define _wcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__) -# undef _tcsdup -# define _tcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__) -# else -# undef _tcsdup -# define _tcsdup(ptr) curl_dbg_strdup(ptr, __LINE__, __FILE__) -# endif +#undef _tcsdup +#ifdef UNICODE +#define _tcsdup(ptr) curl_dbg_wcsdup(ptr, __LINE__, __FILE__) +#else +#define _tcsdup(ptr) curl_dbg_strdup(ptr, __LINE__, __FILE__) #endif +#endif /* _WIN32 */ #undef socket -#define socket(domain,type,protocol)\ - curl_dbg_socket((int)domain, type, protocol, __LINE__, __FILE__) +#define socket(domain,type,protocol) \ + curl_dbg_socket((int)domain, type, protocol, __LINE__, __FILE__) #undef accept /* for those with accept as a macro */ -#define accept(sock,addr,len)\ - curl_dbg_accept(sock, addr, len, __LINE__, __FILE__) +#define accept(sock,addr,len) \ + curl_dbg_accept(sock, addr, len, __LINE__, __FILE__) #ifdef HAVE_ACCEPT4 #undef accept4 /* for those with accept4 as a macro */ -#define accept4(sock,addr,len,flags)\ - curl_dbg_accept4(sock, addr, len, flags, __LINE__, __FILE__) +#define accept4(sock,addr,len,flags) \ + curl_dbg_accept4(sock, addr, len, flags, __LINE__, __FILE__) #endif #ifdef HAVE_SOCKETPAIR -#define socketpair(domain,type,protocol,socket_vector)\ - curl_dbg_socketpair((int)domain, type, protocol, socket_vector, \ - __LINE__, __FILE__) +#define socketpair(domain,type,protocol,socket_vector) \ + curl_dbg_socketpair((int)domain, type, protocol, socket_vector, \ + __LINE__, __FILE__) #endif /* sclose is probably already defined, redefine it! */ @@ -185,10 +176,9 @@ CURL_EXTERN ALLOC_FUNC #define fopen(file,mode) curl_dbg_fopen(file,mode,__LINE__,__FILE__) #undef fdopen #define fdopen(file,mode) curl_dbg_fdopen(file,mode,__LINE__,__FILE__) +#undef fclose #define fclose(file) curl_dbg_fclose(file,__LINE__,__FILE__) -#endif /* MEMDEBUG_NODEFINES */ - #endif /* CURLDEBUG */ /* diff --git a/lib/mime.c b/lib/mime.c index c90c34898d..06cd221abf 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -32,6 +32,7 @@ struct Curl_easy; #include "curlx/warnless.h" #include "urldata.h" #include "sendf.h" +#include "transfer.h" #include "strdup.h" #include "curlx/base64.h" @@ -45,7 +46,6 @@ struct Curl_easy; #include "rand.h" #include "slist.h" -#include "strcase.h" #include "curlx/dynbuf.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -67,12 +67,12 @@ static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems, /* Encoders. */ static size_t encoder_nop_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part); + curl_mimepart *part); static curl_off_t encoder_nop_size(curl_mimepart *part); static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof, curl_mimepart *part); static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part); + curl_mimepart *part); static curl_off_t encoder_base64_size(curl_mimepart *part); static size_t encoder_qp_read(char *buffer, size_t size, bool ateof, curl_mimepart *part); @@ -335,7 +335,7 @@ static char *match_header(struct curl_slist *hdr, const char *lbl, size_t len) { char *value = NULL; - if(strncasecompare(hdr->data, lbl, len) && hdr->data[len] == ':') + if(curl_strnequal(hdr->data, lbl, len) && hdr->data[len] == ':') for(value = hdr->data + len + 1; *value == ' '; value++) ; return value; @@ -433,7 +433,7 @@ static size_t encoder_7bit_read(char *buffer, size_t size, bool ateof, /* Base64 content encoder. */ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof, - curl_mimepart *part) + curl_mimepart *part) { struct mime_encoder_state *st = &part->encstate; size_t cursize = 0; @@ -1478,7 +1478,7 @@ CURLcode curl_mime_encoder(curl_mimepart *part, const char *encoding) return CURLE_OK; /* Removing current encoder. */ for(mep = encoders; mep->name; mep++) - if(strcasecompare(encoding, mep->name)) { + if(curl_strequal(encoding, mep->name)) { part->encoder = mep; result = CURLE_OK; } @@ -1742,7 +1742,7 @@ const char *Curl_mime_contenttype(const char *filename) for(i = 0; i < CURL_ARRAYSIZE(ctts); i++) { size_t len2 = strlen(ctts[i].extension); - if(len1 >= len2 && strcasecompare(nameend - len2, ctts[i].extension)) + if(len1 >= len2 && curl_strequal(nameend - len2, ctts[i].extension)) return ctts[i].type; } } @@ -1752,7 +1752,7 @@ const char *Curl_mime_contenttype(const char *filename) static bool content_type_match(const char *contenttype, const char *target, size_t len) { - if(contenttype && strncasecompare(contenttype, target, len)) + if(contenttype && curl_strnequal(contenttype, target, len)) switch(contenttype[len]) { case '\0': case '\t': @@ -1825,7 +1825,7 @@ CURLcode Curl_mime_prepare_headers(struct Curl_easy *data, if(!search_header(part->userheaders, STRCONST("Content-Disposition"))) { if(!disposition) if(part->filename || part->name || - (contenttype && !strncasecompare(contenttype, "multipart/", 10))) + (contenttype && !curl_strnequal(contenttype, "multipart/", 10))) disposition = DISPOSITION_DEFAULT; if(disposition && curl_strequal(disposition, "attachment") && !part->name && !part->filename) @@ -1962,6 +1962,7 @@ static CURLcode cr_mime_read(struct Curl_easy *data, size_t *pnread, bool *peos) { struct cr_mime_ctx *ctx = reader->ctx; + CURLcode result = CURLE_OK; size_t nread; char tmp[256]; @@ -1990,15 +1991,12 @@ static CURLcode cr_mime_read(struct Curl_easy *data, } if(!Curl_bufq_is_empty(&ctx->tmpbuf)) { - CURLcode result = CURLE_OK; - ssize_t n = Curl_bufq_read(&ctx->tmpbuf, (unsigned char *)buf, blen, - &result); - if(n < 0) { + result = Curl_bufq_read(&ctx->tmpbuf, (unsigned char *)buf, blen, &nread); + if(result) { ctx->errored = TRUE; ctx->error_result = result; return result; } - nread = (size_t)n; } else if(blen <= 4) { /* Curl_mime_read() may go into an infinite loop when reading @@ -2008,22 +2006,20 @@ static CURLcode cr_mime_read(struct Curl_easy *data, CURL_TRC_READ(data, "cr_mime_read(len=%zu), small read, using tmp", blen); nread = Curl_mime_read(tmp, 1, sizeof(tmp), ctx->part); if(nread <= sizeof(tmp)) { - CURLcode result = CURLE_OK; - ssize_t n = Curl_bufq_write(&ctx->tmpbuf, (unsigned char *)tmp, nread, - &result); - if(n < 0) { + size_t n; + result = Curl_bufq_write(&ctx->tmpbuf, (unsigned char *)tmp, nread, &n); + if(result) { ctx->errored = TRUE; ctx->error_result = result; return result; } /* stored it, read again */ - n = Curl_bufq_read(&ctx->tmpbuf, (unsigned char *)buf, blen, &result); - if(n < 0) { + result = Curl_bufq_cread(&ctx->tmpbuf, buf, blen, &nread); + if(result) { ctx->errored = TRUE; ctx->error_result = result; return result; } - nread = (size_t)n; } } else @@ -2051,14 +2047,15 @@ static CURLcode cr_mime_read(struct Curl_easy *data, *peos = FALSE; ctx->errored = TRUE; ctx->error_result = CURLE_ABORTED_BY_CALLBACK; - return CURLE_ABORTED_BY_CALLBACK; + result = CURLE_ABORTED_BY_CALLBACK; + break; case CURL_READFUNC_PAUSE: /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */ CURL_TRC_READ(data, "cr_mime_read(len=%zu), paused by callback", blen); - data->req.keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */ *pnread = 0; *peos = FALSE; + result = Curl_xfer_pause_send(data, TRUE); break; /* nothing was read */ case STOP_FILLING: @@ -2068,7 +2065,8 @@ static CURLcode cr_mime_read(struct Curl_easy *data, *peos = FALSE; ctx->errored = TRUE; ctx->error_result = CURLE_READ_ERROR; - return CURLE_READ_ERROR; + result = CURLE_READ_ERROR; + break; default: if(nread > blen) { @@ -2090,8 +2088,8 @@ static CURLcode cr_mime_read(struct Curl_easy *data, CURL_TRC_READ(data, "cr_mime_read(len=%zu, total=%" FMT_OFF_T ", read=%"FMT_OFF_T") -> %d, %zu, %d", - blen, ctx->total_len, ctx->read_len, CURLE_OK, *pnread, *peos); - return CURLE_OK; + blen, ctx->total_len, ctx->read_len, result, *pnread, *peos); + return result; } static bool cr_mime_needs_rewind(struct Curl_easy *data, diff --git a/lib/mqtt.c b/lib/mqtt.c index 77e7334613..90f857dca5 100644 --- a/lib/mqtt.c +++ b/lib/mqtt.c @@ -240,7 +240,7 @@ static int mqtt_encode_len(char *buf, size_t len) /* add the passwd to the CONNECT packet */ static int add_passwd(const char *passwd, const size_t plen, - char *pkt, const size_t start, int remain_pos) + char *pkt, const size_t start, int remain_pos) { /* magic number that need to be set properly */ const size_t conn_flags_pos = remain_pos + 8; @@ -428,14 +428,13 @@ static CURLcode mqtt_recv_atleast(struct Curl_easy *data, size_t nbytes) if(rlen < nbytes) { unsigned char readbuf[1024]; - ssize_t nread; + size_t nread; DEBUGASSERT(nbytes - rlen < sizeof(readbuf)); result = Curl_xfer_recv(data, (char *)readbuf, nbytes - rlen, &nread); if(result) return result; - DEBUGASSERT(nread >= 0); - if(curlx_dyn_addn(&mq->recvbuf, readbuf, (size_t)nread)) + if(curlx_dyn_addn(&mq->recvbuf, readbuf, nread)) return CURLE_OUT_OF_MEMORY; rlen = curlx_dyn_len(&mq->recvbuf); } @@ -703,7 +702,7 @@ static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done) { CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; - ssize_t nread; + size_t nread; size_t remlen; struct mqtt_conn *mqtt = Curl_conn_meta_get(conn, CURL_META_MQTT_CONN); struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY); @@ -868,7 +867,7 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done) { struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY); CURLcode result = CURLE_OK; - ssize_t nread; + size_t nread; unsigned char recvbyte; struct mqtt_conn *mqtt = Curl_conn_meta_get(data->conn, CURL_META_MQTT_CONN); diff --git a/lib/multi.c b/lib/multi.c index 64eb1ae854..b3a7938e33 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -42,6 +42,7 @@ #include "http.h" #include "select.h" #include "curlx/warnless.h" +#include "curlx/wait.h" #include "speedcheck.h" #include "conncache.h" #include "multihandle.h" @@ -234,6 +235,7 @@ struct Curl_multi *Curl_multi_handle(unsigned int xfer_table_size, Curl_multi_ev_init(multi, ev_hashsize); Curl_uint_tbl_init(&multi->xfers, NULL); Curl_uint_bset_init(&multi->process); + Curl_uint_bset_init(&multi->dirty); Curl_uint_bset_init(&multi->pending); Curl_uint_bset_init(&multi->msgsent); Curl_hash_init(&multi->proto_hash, 23, @@ -246,6 +248,7 @@ struct Curl_multi *Curl_multi_handle(unsigned int xfer_table_size, if(Curl_uint_bset_resize(&multi->process, xfer_table_size) || Curl_uint_bset_resize(&multi->pending, xfer_table_size) || + Curl_uint_bset_resize(&multi->dirty, xfer_table_size) || Curl_uint_bset_resize(&multi->msgsent, xfer_table_size) || Curl_uint_tbl_resize(&multi->xfers, xfer_table_size)) goto error; @@ -268,20 +271,22 @@ struct Curl_multi *Curl_multi_handle(unsigned int xfer_table_size, Curl_cpool_init(&multi->cpool, multi->admin, NULL, chashsize); +#ifdef USE_SSL if(Curl_ssl_scache_create(sesssize, 2, &multi->ssl_scache)) goto error; +#else + (void)sesssize; +#endif #ifdef USE_WINSOCK multi->wsa_event = WSACreateEvent(); if(multi->wsa_event == WSA_INVALID_EVENT) goto error; -#else -#ifdef ENABLE_WAKEUP +#elif defined(ENABLE_WAKEUP) if(wakeup_create(multi->wakeup_pair, TRUE) < 0) { multi->wakeup_pair[0] = CURL_SOCKET_BAD; multi->wakeup_pair[1] = CURL_SOCKET_BAD; } -#endif #endif return multi; @@ -293,13 +298,16 @@ error: Curl_dnscache_destroy(&multi->dnscache); Curl_cpool_destroy(&multi->cpool); Curl_cshutdn_destroy(&multi->cshutdn, multi->admin); +#ifdef USE_SSL Curl_ssl_scache_destroy(multi->ssl_scache); +#endif if(multi->admin) { multi->admin->multi = NULL; Curl_close(&multi->admin); } Curl_uint_bset_destroy(&multi->process); + Curl_uint_bset_destroy(&multi->dirty); Curl_uint_bset_destroy(&multi->pending); Curl_uint_bset_destroy(&multi->msgsent); Curl_uint_tbl_destroy(&multi->xfers); @@ -335,34 +343,57 @@ static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data) static CURLMcode multi_xfers_add(struct Curl_multi *multi, struct Curl_easy *data) { - /* We want `multi->xfers` to have "sufficient" free rows, so that we do - * have to reuse the `mid` from a just removed easy right away. - * Since uint_tbl and uint_bset is quite memory efficient, - * regard less than 25% free as insufficient. - * (for low capacities, e.g. multi_easy, 4 or less). */ unsigned int capacity = Curl_uint_tbl_capacity(&multi->xfers); - unsigned int unused = capacity - Curl_uint_tbl_count(&multi->xfers); - unsigned int min_unused = CURLMAX(capacity >> 2, 4); + unsigned int new_size = 0; + /* Prepare to make this into a CURLMOPT_MAX_TRANSFERS, because some + * applications may want to prevent a run-away of their memory use. */ + /* UINT_MAX is our "invalid" id, do not let the table grow up to that. */ + const unsigned int max_capacity = UINT_MAX - 1; - if(unused <= min_unused) { - /* make it a 64 multiple, since our bitsets frow by that and - * small (easy_multi) grows to at least 64 on first resize. */ - unsigned int newsize = (((capacity + min_unused) + 63) / 64) * 64; - DEBUGASSERT(newsize > capacity); + if(capacity < max_capacity) { + /* We want `multi->xfers` to have "sufficient" free rows, so that we do + * have to reuse the `mid` from a just removed easy right away. + * Since uint_tbl and uint_bset are quite memory efficient, + * regard less than 25% free as insufficient. + * (for low capacities, e.g. multi_easy, 4 or less). */ + unsigned int used = Curl_uint_tbl_count(&multi->xfers); + unsigned int unused = capacity - used; + unsigned int min_unused = CURLMAX(capacity >> 2, 4); + if(unused <= min_unused) { + /* Make sure the uint arithmetic here works on the corner + * cases where we are close to max_capacity or UINT_MAX */ + if((min_unused >= max_capacity) || + ((max_capacity - min_unused) <= capacity) || + ((UINT_MAX - min_unused - 63) <= capacity)) { + new_size = max_capacity; /* can not be larger than this */ + } + else { + /* make it a 64 multiple, since our bitsets frow by that and + * small (easy_multi) grows to at least 64 on first resize. */ + new_size = (((used + min_unused) + 63) / 64) * 64; + } + } + } + + if(new_size > capacity) { /* Grow the bitsets first. Should one fail, we do not need * to downsize the already resized ones. The sets continue * to work properly when larger than the table, but not * the other way around. */ - if(Curl_uint_bset_resize(&multi->process, newsize) || - Curl_uint_bset_resize(&multi->pending, newsize) || - Curl_uint_bset_resize(&multi->msgsent, newsize) || - Curl_uint_tbl_resize(&multi->xfers, newsize)) + CURL_TRC_M(data, "increasing xfer table size to %u", new_size); + if(Curl_uint_bset_resize(&multi->process, new_size) || + Curl_uint_bset_resize(&multi->dirty, new_size) || + Curl_uint_bset_resize(&multi->pending, new_size) || + Curl_uint_bset_resize(&multi->msgsent, new_size) || + Curl_uint_tbl_resize(&multi->xfers, new_size)) return CURLM_OUT_OF_MEMORY; - CURL_TRC_M(data, "increased xfer table size to %u", newsize); } - /* Insert the easy into the table now that MUST have room for it */ + + /* Insert the easy into the table now */ if(!Curl_uint_tbl_add(&multi->xfers, data, &data->mid)) { - DEBUGASSERT(0); + /* MUST only happen when table is full */ + DEBUGASSERT(Curl_uint_tbl_capacity(&multi->xfers) <= + Curl_uint_tbl_count(&multi->xfers)); return CURLM_OUT_OF_MEMORY; } return CURLM_OK; @@ -400,6 +431,7 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d) return CURLM_ABORTED_BY_CALLBACK; multi->dead = FALSE; Curl_uint_bset_clear(&multi->process); + Curl_uint_bset_clear(&multi->dirty); Curl_uint_bset_clear(&multi->pending); Curl_uint_bset_clear(&multi->msgsent); } @@ -589,7 +621,7 @@ static CURLcode multi_done(struct Curl_easy *data, after an error was detected */ bool premature) { - CURLcode result, r2; + CURLcode result; struct connectdata *conn = data->conn; struct multi_done_ctx mdctx; @@ -638,9 +670,7 @@ static CURLcode multi_done(struct Curl_easy *data, } /* Make sure that transfer client writes are really done now. */ - r2 = Curl_xfer_write_done(data, premature); - if(r2 && !result) - result = r2; + result = Curl_1st_err(result, Curl_xfer_write_done(data, premature)); /* Inform connection filters that this transfer is done */ Curl_conn_ev_data_done(data, premature); @@ -794,6 +824,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d) DEBUGASSERT(Curl_uint_tbl_contains(&multi->xfers, mid)); Curl_uint_tbl_remove(&multi->xfers, mid); Curl_uint_bset_remove(&multi->process, mid); + Curl_uint_bset_remove(&multi->dirty, mid); Curl_uint_bset_remove(&multi->pending, mid); Curl_uint_bset_remove(&multi->msgsent, mid); data->multi = NULL; @@ -1038,11 +1069,25 @@ void Curl_multi_getsock(struct Curl_easy *data, break; } + + /* Unblocked and waiting to receive with buffered input. + * Make transfer run again at next opportunity. */ + if(!Curl_xfer_is_blocked(data) && + ((Curl_pollset_want_read(data, ps, data->conn->sock[FIRSTSOCKET]) && + Curl_conn_data_pending(data, FIRSTSOCKET)) || + (Curl_pollset_want_read(data, ps, data->conn->sock[SECONDARYSOCKET]) && + Curl_conn_data_pending(data, SECONDARYSOCKET)))) { + CURL_TRC_M(data, "%s pollset[] has POLLIN, but there is still " + "buffered input to consume -> mark as dirty", caller); + Curl_multi_mark_dirty(data); + } + switch(ps->num) { case 0: CURL_TRC_M(data, "%s pollset[], timeouts=%zu, paused %d/%d (r/w)", caller, Curl_llist_count(&data->state.timeoutlist), - Curl_creader_is_paused(data), Curl_cwriter_is_paused(data)); + Curl_xfer_send_is_paused(data), + Curl_xfer_recv_is_paused(data)); break; case 1: CURL_TRC_M(data, "%s pollset[fd=%" FMT_SOCKET_T " %s%s], timeouts=%zu", @@ -1166,6 +1211,7 @@ CURLMcode curl_multi_waitfds(CURLM *m, if(!data) { DEBUGASSERT(0); Curl_uint_bset_remove(&multi->process, mid); + Curl_uint_bset_remove(&multi->dirty, mid); continue; } Curl_multi_getsock(data, &ps, "curl_multi_waitfds"); @@ -1218,6 +1264,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK]; struct curl_pollfds cpfds; unsigned int curl_nfds = 0; /* how many pfds are for curl transfers */ + struct Curl_easy *data = NULL; CURLMcode result = CURLM_OK; unsigned int mid; @@ -1243,11 +1290,12 @@ static CURLMcode multi_wait(struct Curl_multi *multi, /* Add the curl handles to our pollfds first */ if(Curl_uint_bset_first(&multi->process, &mid)) { do { - struct Curl_easy *data = Curl_multi_get_easy(multi, mid); struct easy_pollset ps; + data = Curl_multi_get_easy(multi, mid); if(!data) { DEBUGASSERT(0); Curl_uint_bset_remove(&multi->process, mid); + Curl_uint_bset_remove(&multi->dirty, mid); continue; } Curl_multi_getsock(data, &ps, "multi_wait"); @@ -1320,6 +1368,9 @@ static CURLMcode multi_wait(struct Curl_multi *multi, if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms)) timeout_ms = (int)timeout_internal; + if(data) + CURL_TRC_M(data, "multi_wait(fds=%d, timeout=%d) tinternal=%ld", + cpfds.n, timeout_ms, timeout_internal); #if defined(ENABLE_WAKEUP) && defined(USE_WINSOCK) if(cpfds.n || use_wakeup) { #else @@ -1441,7 +1492,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi, timeout */ else if(sleep_ms < 0) sleep_ms = timeout_ms; - Curl_wait_ms(sleep_ms); + curlx_wait_ms(sleep_ms); } } @@ -1946,12 +1997,6 @@ static CURLMcode state_performing(struct Curl_easy *data, } } } - else if(data->state.select_bits && !Curl_xfer_is_blocked(data)) { - /* This avoids CURLM_CALL_MULTI_PERFORM so that a very fast transfer does - not get stuck on this transfer at the expense of other concurrent - transfers */ - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } free(newurl); *resultp = result; return rc; @@ -2215,6 +2260,7 @@ static CURLMcode state_connect(struct Curl_multi *multi, multistate(data, MSTATE_PENDING); /* move from process to pending set */ Curl_uint_bset_remove(&multi->process, data->mid); + Curl_uint_bset_remove(&multi->dirty, data->mid); Curl_uint_bset_add(&multi->pending, data->mid); *resultp = CURLE_OK; return rc; @@ -2276,6 +2322,10 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, multi_warn_debug(multi, data); + /* transfer runs now, clear the dirty bit. This may be set + * again during processing, triggering a re-run later. */ + Curl_uint_bset_remove(&multi->dirty, data->mid); + do { /* A "stream" here is a logical stream if the protocol can handle that (HTTP/2), or the full connection for older protocols */ @@ -2642,6 +2692,7 @@ statemachine_end: /* remove from the other sets, add to msgsent */ Curl_uint_bset_remove(&multi->process, data->mid); + Curl_uint_bset_remove(&multi->dirty, data->mid); Curl_uint_bset_remove(&multi->pending, data->mid); Curl_uint_bset_add(&multi->msgsent, data->mid); --multi->xfers_alive; @@ -2679,6 +2730,7 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles) if(!data) { DEBUGASSERT(0); Curl_uint_bset_remove(&multi->process, mid); + Curl_uint_bset_remove(&multi->dirty, mid); continue; } if(data != multi->admin) { @@ -2757,7 +2809,7 @@ CURLMcode curl_multi_cleanup(CURLM *m) #ifdef DEBUGBUILD if(mid != data->mid) { CURL_TRC_M(data, "multi_cleanup: still present with mid=%u, " - "but unexpected data->mid=%u\n", mid, data->mid); + "but unexpected data->mid=%u\n", mid, data->mid); DEBUGASSERT(0); } #endif @@ -2798,7 +2850,9 @@ CURLMcode curl_multi_cleanup(CURLM *m) Curl_hash_destroy(&multi->proto_hash); Curl_dnscache_destroy(&multi->dnscache); Curl_psl_destroy(&multi->psl); +#ifdef USE_SSL Curl_ssl_scache_destroy(multi->ssl_scache); +#endif #ifdef USE_WINSOCK WSACloseEvent(multi->wsa_event); @@ -2819,6 +2873,7 @@ CURLMcode curl_multi_cleanup(CURLM *m) } #endif Curl_uint_bset_destroy(&multi->process); + Curl_uint_bset_destroy(&multi->dirty); Curl_uint_bset_destroy(&multi->pending); Curl_uint_bset_destroy(&multi->msgsent); Curl_uint_tbl_destroy(&multi->xfers); @@ -2942,12 +2997,11 @@ struct multi_run_ctx { bool run_cpool; }; -static CURLMcode multi_run_expired(struct multi_run_ctx *mrc) +static void multi_mark_expired_as_dirty(struct multi_run_ctx *mrc) { struct Curl_multi *multi = mrc->multi; struct Curl_easy *data = NULL; struct Curl_tree *t = NULL; - CURLMcode result = CURLM_OK; /* * The loop following here will go on as long as there are expire-times left @@ -2959,33 +3013,64 @@ static CURLMcode multi_run_expired(struct multi_run_ctx *mrc) extracts a matching node if there is one */ multi->timetree = Curl_splaygetbest(mrc->now, multi->timetree, &t); if(!t) - goto out; + return; data = Curl_splayget(t); /* assign this for next loop */ if(!data) continue; (void)add_next_timeout(mrc->now, multi, data); - if(data == multi->admin) { - mrc->run_cpool = TRUE; - continue; - } + Curl_multi_mark_dirty(data); + } +} - mrc->run_xfers++; - sigpipe_apply(data, &mrc->pipe_st); - result = multi_runsingle(multi, &mrc->now, data); +static CURLMcode multi_run_dirty(struct multi_run_ctx *mrc) +{ + struct Curl_multi *multi = mrc->multi; + CURLMcode result = CURLM_OK; + unsigned int mid; - if(CURLM_OK >= result) { - /* reassess event handling of data */ - result = Curl_multi_ev_assess_xfer(multi, data); - if(result) - goto out; + if(Curl_uint_bset_first(&multi->dirty, &mid)) { + do { + struct Curl_easy *data = Curl_multi_get_easy(multi, mid); + if(data) { + CURL_TRC_M(data, "multi_run_dirty"); + + if(data == multi->admin) { + Curl_uint_bset_remove(&multi->dirty, mid); + mrc->run_cpool = TRUE; + continue; + } + else if(!Curl_uint_bset_contains(&multi->process, mid)) { + /* We are no longer processing this transfer */ + Curl_uint_bset_remove(&multi->dirty, mid); + continue; + } + + mrc->run_xfers++; + sigpipe_apply(data, &mrc->pipe_st); + /* runsingle() clears the dirty mid */ + result = multi_runsingle(multi, &mrc->now, data); + + if(CURLM_OK >= result) { + /* reassess event handling of data */ + result = Curl_multi_ev_assess_xfer(multi, data); + if(result) + goto out; + } + } + else { + CURL_TRC_M(multi->admin, "multi_run_dirty, %u no longer found", mid); + Curl_uint_bset_remove(&multi->dirty, mid); + } } + while(Curl_uint_bset_next(&multi->dirty, mid, &mid)); } out: return result; } + static CURLMcode multi_socket(struct Curl_multi *multi, bool checkall, curl_socket_t s, @@ -3014,7 +3099,8 @@ static CURLMcode multi_socket(struct Curl_multi *multi, } if(s != CURL_SOCKET_TIMEOUT) { - Curl_multi_ev_expire_xfers(multi, s, &mrc.now, &mrc.run_cpool); + /* Mark all transfers of that socket as dirty */ + Curl_multi_ev_dirty_xfers(multi, s, &mrc.run_cpool); } else { /* Asked to run due to time-out. Clear the 'last_expire_ts' variable to @@ -3026,7 +3112,8 @@ static CURLMcode multi_socket(struct Curl_multi *multi, mrc.run_cpool = TRUE; } - result = multi_run_expired(&mrc); + multi_mark_expired_as_dirty(&mrc); + result = multi_run_dirty(&mrc); if(result) goto out; @@ -3037,7 +3124,8 @@ static CURLMcode multi_socket(struct Curl_multi *multi, * Do that only once or it might be unfair to transfers on other * sockets. */ mrc.now = curlx_now(); - result = multi_run_expired(&mrc); + multi_mark_expired_as_dirty(&mrc); + result = multi_run_dirty(&mrc); } out: @@ -3165,6 +3253,29 @@ CURLMcode curl_multi_socket_all(CURLM *m, int *running_handles) return multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles); } + +static bool multi_has_dirties(struct Curl_multi *multi) +{ + unsigned int mid; + if(Curl_uint_bset_first(&multi->dirty, &mid)) { + do { + struct Curl_easy *data = Curl_multi_get_easy(multi, mid); + if(data) { + if(Curl_uint_bset_contains(&multi->process, mid)) + return TRUE; + /* We are no longer processing this transfer */ + Curl_uint_bset_remove(&multi->dirty, mid); + } + else { + CURL_TRC_M(multi->admin, "dirty transfer %u no longer found", mid); + Curl_uint_bset_remove(&multi->dirty, mid); + } + } + while(Curl_uint_bset_next(&multi->dirty, mid, &mid)); + } + return FALSE; +} + static CURLMcode multi_timeout(struct Curl_multi *multi, struct curltime *expire_time, long *timeout_ms) @@ -3176,7 +3287,12 @@ static CURLMcode multi_timeout(struct Curl_multi *multi, return CURLM_OK; } - if(multi->timetree) { + if(multi_has_dirties(multi)) { + *expire_time = curlx_now(); + *timeout_ms = 0; + return CURLM_OK; + } + else if(multi->timetree) { /* we have a tree of expire times */ struct curltime now = curlx_now(); @@ -3197,6 +3313,10 @@ static CURLMcode multi_timeout(struct Curl_multi *multi, *timeout_ms = (long)diff; } else { + if(multi->timetree) { + struct Curl_easy *data = Curl_splayget(multi->timetree); + CURL_TRC_M(data, "multi_timeout() says this has expired"); + } /* 0 means immediately */ *timeout_ms = 0; } @@ -3420,6 +3540,9 @@ void Curl_expire_ex(struct Curl_easy *data, Curl_splayset(&data->state.timenode, data); multi->timetree = Curl_splayinsert(*curr_expire, multi->timetree, &data->state.timenode); + if(data->id >= 0) + CURL_TRC_M(data, "set expire[%d] in %" FMT_TIMEDIFF_T "ns", + id, curlx_timediff_us(set, *nowp)); } /* @@ -3763,9 +3886,15 @@ unsigned int Curl_multi_xfers_running(struct Curl_multi *multi) return multi->xfers_alive; } +void Curl_multi_mark_dirty(struct Curl_easy *data) +{ + if(data->multi && data->mid != UINT_MAX) + Curl_uint_bset_add(&data->multi->dirty, data->mid); +} + #ifdef DEBUGBUILD static void multi_xfer_dump(struct Curl_multi *multi, unsigned int mid, - void *entry) + void *entry) { struct Curl_easy *data = entry; diff --git a/lib/multi_ev.c b/lib/multi_ev.c index 21d2867619..0b4c472849 100644 --- a/lib/multi_ev.c +++ b/lib/multi_ev.c @@ -563,10 +563,9 @@ CURLMcode Curl_multi_ev_assign(struct Curl_multi *multi, return CURLM_OK; } -void Curl_multi_ev_expire_xfers(struct Curl_multi *multi, - curl_socket_t s, - const struct curltime *nowp, - bool *run_cpool) +void Curl_multi_ev_dirty_xfers(struct Curl_multi *multi, + curl_socket_t s, + bool *run_cpool) { struct mev_sh_entry *entry; @@ -586,9 +585,11 @@ void Curl_multi_ev_expire_xfers(struct Curl_multi *multi, do { data = Curl_multi_get_easy(multi, mid); if(data) { - /* Expire with out current now, so we will get it below when - * asking the splaytree for expired transfers. */ - Curl_expire_ex(data, nowp, 0, EXPIRE_RUN_NOW); + Curl_multi_mark_dirty(data); + } + else { + CURL_TRC_M(multi->admin, "socket transfer %u no longer found", mid); + Curl_uint_spbset_remove(&entry->xfers, mid); } } while(Curl_uint_spbset_next(&entry->xfers, mid, &mid)); diff --git a/lib/multi_ev.h b/lib/multi_ev.h index 06be842fb9..20c1aeac81 100644 --- a/lib/multi_ev.h +++ b/lib/multi_ev.h @@ -61,11 +61,10 @@ CURLMcode Curl_multi_ev_assess_conn(struct Curl_multi *multi, struct Curl_easy *data, struct connectdata *conn); -/* Expire all transfers tied to the given socket */ -void Curl_multi_ev_expire_xfers(struct Curl_multi *multi, - curl_socket_t s, - const struct curltime *nowp, - bool *run_cpool); +/* Mark all transfers tied to the given socket as dirty */ +void Curl_multi_ev_dirty_xfers(struct Curl_multi *multi, + curl_socket_t s, + bool *run_cpool); /* Socket will be closed, forget anything we know about it. */ void Curl_multi_ev_socket_done(struct Curl_multi *multi, diff --git a/lib/multihandle.h b/lib/multihandle.h index 04db02f0b1..4bf2dd811c 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -98,6 +98,7 @@ struct Curl_multi { struct uint_tbl xfers; /* transfers added to this multi */ /* Each transfer's mid may be present in at most one of these */ struct uint_bset process; /* transfer being processed */ + struct uint_bset dirty; /* transfer to be run NOW, e.g. ASAP. */ struct uint_bset pending; /* transfers in waiting (conn limit etc.) */ struct uint_bset msgsent; /* transfers done with message for application */ diff --git a/lib/multiif.h b/lib/multiif.h index eae634ab40..1ba0d0f838 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -36,7 +36,7 @@ bool Curl_expire_clear(struct Curl_easy *data); void Curl_expire_done(struct Curl_easy *data, expire_id id); CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT; void Curl_attach_connection(struct Curl_easy *data, - struct connectdata *conn); + struct connectdata *conn); void Curl_detach_connection(struct Curl_easy *data); bool Curl_multiplex_wanted(const struct Curl_multi *multi); void Curl_set_in_callback(struct Curl_easy *data, bool value); @@ -106,7 +106,7 @@ void Curl_multi_getsock(struct Curl_easy *data, * CURLE_AGAIN if the buffer is borrowed already. */ CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data, - char **pbuf, size_t *pbuflen); + char **pbuf, size_t *pbuflen); /** * Release the borrowed buffer. All references into the buffer become * invalid after this. @@ -173,4 +173,8 @@ struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi, /* Get the # of transfers current in process/pending. */ unsigned int Curl_multi_xfers_running(struct Curl_multi *multi); +/* Mark a transfer as dirty, e.g. to be rerun at earliest convenience. + * A cheap operation, can be done many times repeatedly. */ +void Curl_multi_mark_dirty(struct Curl_easy *data); + #endif /* HEADER_CURL_MULTIIF_H */ diff --git a/lib/netrc.c b/lib/netrc.c index 7df3f17fc3..bf53fc01e1 100644 --- a/lib/netrc.c +++ b/lib/netrc.c @@ -229,12 +229,12 @@ static NETRCcode parsenetrc(struct store_netrc *store, switch(state) { case NOTHING: - if(strcasecompare("macdef", tok)) + if(curl_strequal("macdef", tok)) /* Define a macro. A macro is defined with the specified name; its contents begin with the next .netrc line and continue until a null line (consecutive new-line characters) is encountered. */ state = MACDEF; - else if(strcasecompare("machine", tok)) { + else if(curl_strequal("machine", tok)) { /* the next tok is the machine name, this is in itself the delimiter that starts the stuff entered for this machine, after this we need to search for 'login' and 'password'. */ @@ -246,7 +246,7 @@ static NETRCcode parsenetrc(struct store_netrc *store, if(!specific_login) Curl_safefree(login); } - else if(strcasecompare("default", tok)) { + else if(curl_strequal("default", tok)) { state = HOSTVALID; retcode = NETRC_OK; /* we did find our host */ } @@ -256,7 +256,7 @@ static NETRCcode parsenetrc(struct store_netrc *store, state = NOTHING; break; case HOSTFOUND: - if(strcasecompare(host, tok)) { + if(curl_strequal(host, tok)) { /* and yes, this is our host! */ state = HOSTVALID; retcode = NETRC_OK; /* we did find our host */ @@ -293,11 +293,11 @@ static NETRCcode parsenetrc(struct store_netrc *store, found |= FOUND_PASSWORD; keyword = NONE; } - else if(strcasecompare("login", tok)) + else if(curl_strequal("login", tok)) keyword = LOGIN; - else if(strcasecompare("password", tok)) + else if(curl_strequal("password", tok)) keyword = PASSWORD; - else if(strcasecompare("machine", tok)) { + else if(curl_strequal("machine", tok)) { /* a new machine here */ if(found & FOUND_PASSWORD) { done = TRUE; @@ -310,7 +310,7 @@ static NETRCcode parsenetrc(struct store_netrc *store, if(!specific_login) Curl_safefree(login); } - else if(strcasecompare("default", tok)) { + else if(curl_strequal("default", tok)) { state = HOSTVALID; retcode = NETRC_OK; /* we did find our host */ Curl_safefree(password); diff --git a/lib/noproxy.c b/lib/noproxy.c index f70f57eb5a..306cd6d1f9 100644 --- a/lib/noproxy.c +++ b/lib/noproxy.c @@ -26,8 +26,8 @@ #ifndef CURL_DISABLE_PROXY +#include /* for curl_strnequal() */ #include "curlx/inet_pton.h" -#include "strcase.h" #include "noproxy.h" #include "curlx/strparse.h" @@ -205,12 +205,12 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy) */ if(tokenlen == namelen) /* case A, exact match */ - match = strncasecompare(token, name, namelen); + match = curl_strnequal(token, name, namelen); else if(tokenlen < namelen) { /* case B, tailmatch domain */ match = (name[namelen - tokenlen - 1] == '.') && - strncasecompare(token, name + (namelen - tokenlen), - tokenlen); + curl_strnequal(token, name + (namelen - tokenlen), + tokenlen); } /* case C passes through, not a match */ break; diff --git a/lib/openldap.c b/lib/openldap.c index 6343c40c09..e4714b5580 100644 --- a/lib/openldap.c +++ b/lib/openldap.c @@ -941,7 +941,6 @@ static CURLcode oldap_disconnect(struct Curl_easy *data, ldap_unbind_ext(li->ld, NULL, NULL); li->ld = NULL; } - Curl_sasl_cleanup(conn, li->sasl.authused); } return CURLE_OK; } @@ -1048,8 +1047,8 @@ static CURLcode client_write(struct Curl_easy *data, return result; } -static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, - size_t len, CURLcode *err) +static CURLcode oldap_recv(struct Curl_easy *data, int sockindex, char *buf, + size_t len, size_t *pnread) { struct connectdata *conn = data->conn; struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); @@ -1067,10 +1066,9 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, (void)len; (void)buf; (void)sockindex; - if(!li || !lr) { - *err = CURLE_FAILED_INIT; - return -1; - } + *pnread = 0; + if(!li || !lr) + return CURLE_FAILED_INIT; rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg); if(rc < 0) { @@ -1078,11 +1076,9 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, result = CURLE_RECV_ERROR; } - *err = result; - /* error or timed out */ if(!msg) - return -1; + return result; result = CURLE_OK; @@ -1210,8 +1206,7 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf, } ldap_msgfree(msg); - *err = result; - return result ? -1 : 0; + return result; } #ifdef USE_SSL @@ -1258,15 +1253,17 @@ ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) if(conn) { struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); CURLcode err = CURLE_RECV_ERROR; + size_t nread; if(!li) { SET_SOCKERRNO(SOCKEINVAL); return -1; } - ret = (li->recv)(data, FIRSTSOCKET, buf, len, &err); - if(ret < 0 && err == CURLE_AGAIN) { + err = (li->recv)(data, FIRSTSOCKET, buf, len, &nread); + if(err == CURLE_AGAIN) { SET_SOCKERRNO(SOCKEWOULDBLOCK); } + ret = err ? -1 : (ber_slen_t)nread; } } return ret; @@ -1281,15 +1278,17 @@ ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) if(conn) { struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN); CURLcode err = CURLE_SEND_ERROR; + size_t nwritten; if(!li) { SET_SOCKERRNO(SOCKEINVAL); return -1; } - ret = (li->send)(data, FIRSTSOCKET, buf, len, FALSE, &err); - if(ret < 0 && err == CURLE_AGAIN) { + err = (li->send)(data, FIRSTSOCKET, buf, len, FALSE, &nwritten); + if(err == CURLE_AGAIN) { SET_SOCKERRNO(SOCKEWOULDBLOCK); } + ret = err ? -1 : (ber_slen_t)nwritten; } } return ret; diff --git a/lib/parsedate.c b/lib/parsedate.c index 7e0c69106c..65730d6c99 100644 --- a/lib/parsedate.c +++ b/lib/parsedate.c @@ -80,7 +80,6 @@ #include #include -#include "strcase.h" #include "curlx/warnless.h" #include "parsedate.h" #include "curlx/strparse.h" @@ -224,7 +223,7 @@ static int checkday(const char *check, size_t len) for(i = 0; i < 7; i++) { size_t ilen = strlen(what[0]); if((ilen == len) && - strncasecompare(check, what[0], len)) + curl_strnequal(check, what[0], len)) return i; what++; } @@ -239,7 +238,7 @@ static int checkmonth(const char *check, size_t len) return -1; /* not a month */ for(i = 0; i < 12; i++) { - if(strncasecompare(check, what[0], 3)) + if(curl_strnequal(check, what[0], 3)) return i; what++; } @@ -259,7 +258,7 @@ static int checktz(const char *check, size_t len) for(i = 0; i < CURL_ARRAYSIZE(tz); i++) { size_t ilen = strlen(what->name); if((ilen == len) && - strncasecompare(check, what->name, len)) + curl_strnequal(check, what->name, len)) return what->offset*60; what++; } diff --git a/lib/pingpong.c b/lib/pingpong.c index c5513f6050..a661a0285b 100644 --- a/lib/pingpong.c +++ b/lib/pingpong.c @@ -52,7 +52,7 @@ timediff_t Curl_pp_state_timeout(struct Curl_easy *data, struct pingpong *pp, bool disconnecting) { timediff_t timeout_ms; /* in milliseconds */ - timediff_t response_time = (data->set.server_response_timeout) ? + timediff_t response_time = (data->set.server_response_timeout > 0) ? data->set.server_response_timeout : pp->response_time; struct curltime now = curlx_now(); @@ -65,7 +65,7 @@ timediff_t Curl_pp_state_timeout(struct Curl_easy *data, full response to arrive before we bail out */ timeout_ms = response_time - curlx_timediff(now, pp->response); - if(data->set.timeout && !disconnecting) { + if((data->set.timeout > 0) && !disconnecting) { /* if timeout is requested, find out how much overall remains */ timediff_t timeout2_ms = Curl_timeleft(data, &now, FALSE); /* pick the lowest number */ @@ -116,11 +116,15 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data, else if(!pp->sendleft && Curl_conn_data_pending(data, FIRSTSOCKET)) /* We are receiving and there is data ready in the SSL library */ rc = 1; - else + else { + DEBUGF(infof(data, "pp_statematch, select, timeout=%" FMT_TIMEDIFF_T + ", sendleft=%zu", + timeout_ms, pp->sendleft)); rc = Curl_socket_check(pp->sendleft ? CURL_SOCKET_BAD : sock, /* reading */ CURL_SOCKET_BAD, pp->sendleft ? sock : CURL_SOCKET_BAD, /* writing */ interval_ms); + } if(block) { /* if we did not wait, we do not have to spend time on this now */ @@ -266,7 +270,7 @@ static CURLcode pingpong_read(struct Curl_easy *data, int sockindex, char *buffer, size_t buflen, - ssize_t *nread) + size_t *nread) { CURLcode result; #ifdef HAVE_GSSAPI @@ -294,7 +298,7 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data, { struct connectdata *conn = data->conn; CURLcode result = CURLE_OK; - ssize_t gotbytes; + size_t gotbytes; char buffer[900]; *code = 0; /* 0 for errors or not done */ @@ -321,7 +325,7 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data, if(result) return result; - if(gotbytes <= 0) { + if(!gotbytes) { failf(data, "response reading failed (errno: %d)", SOCKERRNO); return CURLE_RECV_ERROR; } diff --git a/lib/pingpong.h b/lib/pingpong.h index 0665b83659..c6d0a56f0d 100644 --- a/lib/pingpong.h +++ b/lib/pingpong.h @@ -59,7 +59,7 @@ struct pingpong { struct dynbuf recvbuf; size_t overflow; /* number of bytes left after a final response line */ size_t nfinal; /* number of bytes in the final response line, which - after a match is first in the receice buffer */ + after a match is first in the receive buffer */ /* Function pointers the protocols MUST implement and provide for the pingpong layer to function */ diff --git a/lib/pop3.c b/lib/pop3.c index aa8c2d834a..514c4b8fa5 100644 --- a/lib/pop3.c +++ b/lib/pop3.c @@ -66,7 +66,6 @@ #include "socks.h" #include "pingpong.h" #include "pop3.h" -#include "strcase.h" #include "vtls/vtls.h" #include "cfilters.h" #include "connect.h" @@ -285,7 +284,7 @@ static bool pop3_is_multiline(const char *cmdline) { size_t i; for(i = 0; i < CURL_ARRAYSIZE(pop3cmds); ++i) { - if(strncasecompare(pop3cmds[i].name, cmdline, pop3cmds[i].nlen)) { + if(curl_strnequal(pop3cmds[i].name, cmdline, pop3cmds[i].nlen)) { if(!cmdline[pop3cmds[i].nlen]) return pop3cmds[i].multiline; else if(cmdline[pop3cmds[i].nlen] == ' ') @@ -1450,7 +1449,8 @@ static CURLcode pop3_disconnect(struct Curl_easy *data, bad in any way, sending quit and waiting around here will make the disconnect wait in vain and cause more problems than we need to. */ - if(!dead_connection && conn->bits.protoconnstart) { + if(!dead_connection && conn->bits.protoconnstart && + !Curl_pp_needs_flush(data, &pop3c->pp)) { if(!pop3_perform_quit(data, conn)) (void)pop3_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */ } @@ -1458,9 +1458,6 @@ static CURLcode pop3_disconnect(struct Curl_easy *data, /* Disconnect from the server */ Curl_pp_disconnect(&pop3c->pp); - /* Cleanup the SASL module */ - Curl_sasl_cleanup(conn, pop3c->sasl.authused); - /* Cleanup our connection based variables */ Curl_safefree(pop3c->apoptimestamp); @@ -1593,11 +1590,11 @@ static CURLcode pop3_parse_url_options(struct connectdata *conn) while(*ptr && *ptr != ';') ptr++; - if(strncasecompare(key, "AUTH=", 5)) { + if(curl_strnequal(key, "AUTH=", 5)) { result = Curl_sasl_parse_url_auth_option(&pop3c->sasl, value, ptr - value); - if(result && strncasecompare(value, "+APOP", ptr - value)) { + if(result && curl_strnequal(value, "+APOP", ptr - value)) { pop3c->preftype = POP3_TYPE_APOP; pop3c->sasl.prefmech = SASL_AUTH_NONE; result = CURLE_OK; diff --git a/lib/progress.c b/lib/progress.c index 8e6d98f0d9..746805f20f 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -44,18 +44,18 @@ static void time2str(char *r, curl_off_t seconds) strcpy(r, "--:--:--"); return; } - h = seconds / CURL_OFF_T_C(3600); - if(h <= CURL_OFF_T_C(99)) { - curl_off_t m = (seconds - (h*CURL_OFF_T_C(3600))) / CURL_OFF_T_C(60); - curl_off_t s = (seconds - (h*CURL_OFF_T_C(3600))) - (m*CURL_OFF_T_C(60)); + h = seconds / 3600; + if(h <= 99) { + curl_off_t m = (seconds - (h * 3600)) / 60; + curl_off_t s = (seconds - (h * 3600)) - (m * 60); msnprintf(r, 9, "%2" FMT_OFF_T ":%02" FMT_OFF_T ":%02" FMT_OFF_T, h, m, s); } else { /* this equals to more than 99 hours, switch to a more suitable output format to fit within the limits. */ - curl_off_t d = seconds / CURL_OFF_T_C(86400); - h = (seconds - (d*CURL_OFF_T_C(86400))) / CURL_OFF_T_C(3600); - if(d <= CURL_OFF_T_C(999)) + curl_off_t d = seconds / 86400; + h = (seconds - (d * 86400)) / 3600; + if(d <= 999) msnprintf(r, 9, "%3" FMT_OFF_T "d %02" FMT_OFF_T "h", d, h); else msnprintf(r, 9, "%7" FMT_OFF_T "d", d); @@ -67,39 +67,39 @@ static void time2str(char *r, curl_off_t seconds) Add suffix k, M, G when suitable... */ static char *max5data(curl_off_t bytes, char *max5) { -#define ONE_KILOBYTE CURL_OFF_T_C(1024) -#define ONE_MEGABYTE (CURL_OFF_T_C(1024) * ONE_KILOBYTE) -#define ONE_GIGABYTE (CURL_OFF_T_C(1024) * ONE_MEGABYTE) -#define ONE_TERABYTE (CURL_OFF_T_C(1024) * ONE_GIGABYTE) -#define ONE_PETABYTE (CURL_OFF_T_C(1024) * ONE_TERABYTE) +#define ONE_KILOBYTE (curl_off_t)1024 +#define ONE_MEGABYTE (1024 * ONE_KILOBYTE) +#define ONE_GIGABYTE (1024 * ONE_MEGABYTE) +#define ONE_TERABYTE (1024 * ONE_GIGABYTE) +#define ONE_PETABYTE (1024 * ONE_TERABYTE) - if(bytes < CURL_OFF_T_C(100000)) + if(bytes < 100000) msnprintf(max5, 6, "%5" FMT_OFF_T, bytes); - else if(bytes < CURL_OFF_T_C(10000) * ONE_KILOBYTE) + else if(bytes < 10000 * ONE_KILOBYTE) msnprintf(max5, 6, "%4" FMT_OFF_T "k", bytes/ONE_KILOBYTE); - else if(bytes < CURL_OFF_T_C(100) * ONE_MEGABYTE) + else if(bytes < 100 * ONE_MEGABYTE) /* 'XX.XM' is good as long as we are less than 100 megs */ msnprintf(max5, 6, "%2" FMT_OFF_T ".%0" FMT_OFF_T "M", bytes/ONE_MEGABYTE, - (bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/CURL_OFF_T_C(10)) ); + (bytes%ONE_MEGABYTE) / (ONE_MEGABYTE/10) ); - else if(bytes < CURL_OFF_T_C(10000) * ONE_MEGABYTE) + else if(bytes < 10000 * ONE_MEGABYTE) /* 'XXXXM' is good until we are at 10000MB or above */ msnprintf(max5, 6, "%4" FMT_OFF_T "M", bytes/ONE_MEGABYTE); - else if(bytes < CURL_OFF_T_C(100) * ONE_GIGABYTE) + else if(bytes < 100 * ONE_GIGABYTE) /* 10000 MB - 100 GB, we show it as XX.XG */ msnprintf(max5, 6, "%2" FMT_OFF_T ".%0" FMT_OFF_T "G", bytes/ONE_GIGABYTE, - (bytes%ONE_GIGABYTE) / (ONE_GIGABYTE/CURL_OFF_T_C(10)) ); + (bytes%ONE_GIGABYTE) / (ONE_GIGABYTE/10) ); - else if(bytes < CURL_OFF_T_C(10000) * ONE_GIGABYTE) + else if(bytes < 10000 * ONE_GIGABYTE) /* up to 10000GB, display without decimal: XXXXG */ msnprintf(max5, 6, "%4" FMT_OFF_T "G", bytes/ONE_GIGABYTE); - else if(bytes < CURL_OFF_T_C(10000) * ONE_TERABYTE) + else if(bytes < 10000 * ONE_TERABYTE) /* up to 10000TB, display without decimal: XXXXT */ msnprintf(max5, 6, "%4" FMT_OFF_T "T", bytes/ONE_TERABYTE); @@ -296,7 +296,7 @@ timediff_t Curl_pgrsLimitWaitTime(struct pgrs_dir *d, * stay below 'limit'. */ if(size < CURL_OFF_T_MAX/1000) - minimum = (timediff_t) (CURL_OFF_T_C(1000) * size / speed_limit); + minimum = (timediff_t) (1000 * size / speed_limit); else { minimum = (timediff_t) (size / speed_limit); if(minimum < TIMEDIFF_T_MAX/1000) @@ -455,7 +455,7 @@ static bool progress_calc(struct Curl_easy *data, struct curltime now) /* Calculate the average speed the last 'span_ms' milliseconds */ amount = p->speeder[nowindex]- p->speeder[checkindex]; - if(amount > CURL_OFF_T_C(4294967) /* 0xffffffff/1000 */) + if(amount > (0xffffffff/1000)) /* the 'amount' value is bigger than would fit in 32 bits if multiplied with 1000, so we use the double math for this */ p->current_speed = (curl_off_t) @@ -463,7 +463,7 @@ static bool progress_calc(struct Curl_easy *data, struct curltime now) else /* the 'amount' value is small enough to fit within 32 bits even when multiplied with 1000 */ - p->current_speed = amount*CURL_OFF_T_C(1000)/span_ms; + p->current_speed = amount * 1000/span_ms; } else /* the first second we use the average */ @@ -482,9 +482,9 @@ struct pgrs_estimate { static curl_off_t pgrs_est_percent(curl_off_t total, curl_off_t cur) { - if(total > CURL_OFF_T_C(10000)) - return cur / (total/CURL_OFF_T_C(100)); - else if(total > CURL_OFF_T_C(0)) + if(total > 10000) + return cur / (total / 100); + else if(total > 0) return (cur*100) / total; return 0; } @@ -495,7 +495,7 @@ static void pgrs_estimates(struct pgrs_dir *d, { est->secs = 0; est->percent = 0; - if(total_known && (d->speed > CURL_OFF_T_C(0))) { + if(total_known && (d->speed > 0)) { est->secs = d->total_size / d->speed; est->percent = pgrs_est_percent(d->total_size, d->cur_size); } diff --git a/lib/rand.c b/lib/rand.c index c0368dd763..72545e0867 100644 --- a/lib/rand.c +++ b/lib/rand.c @@ -103,8 +103,8 @@ CURLcode Curl_win32_random(unsigned char *entropy, size_t length) #if !defined(USE_SSL) /* ---- possibly non-cryptographic version following ---- */ static CURLcode weak_random(struct Curl_easy *data, - unsigned char *entropy, - size_t length) /* always 4, size of int */ + unsigned char *entropy, + size_t length) /* always 4, size of int */ { unsigned int r; DEBUGASSERT(length == sizeof(int)); diff --git a/lib/request.c b/lib/request.c index f937a7f4bf..0d9b23ef0c 100644 --- a/lib/request.c +++ b/lib/request.c @@ -338,20 +338,18 @@ static CURLcode req_flush(struct Curl_easy *data) return CURLE_OK; } -static ssize_t add_from_client(void *reader_ctx, - unsigned char *buf, size_t buflen, - CURLcode *err) +static CURLcode add_from_client(void *reader_ctx, + unsigned char *buf, size_t buflen, + size_t *pnread) { struct Curl_easy *data = reader_ctx; - size_t nread; + CURLcode result; bool eos; - *err = Curl_client_read(data, (char *)buf, buflen, &nread, &eos); - if(*err) - return -1; - if(eos) + result = Curl_client_read(data, (char *)buf, buflen, pnread, &eos); + if(!result && eos) data->req.eos_read = TRUE; - return (ssize_t)nread; + return result; } static CURLcode req_send_buffer_add(struct Curl_easy *data, @@ -359,13 +357,12 @@ static CURLcode req_send_buffer_add(struct Curl_easy *data, size_t hds_len) { CURLcode result = CURLE_OK; - ssize_t n; - n = Curl_bufq_write(&data->req.sendbuf, - (const unsigned char *)buf, blen, &result); - if(n < 0) + size_t n; + result = Curl_bufq_cwrite(&data->req.sendbuf, buf, blen, &n); + if(result) return result; /* We rely on a SOFTLIMIT on sendbuf, so it can take all data in */ - DEBUGASSERT((size_t)n == blen); + DEBUGASSERT(n == blen); data->req.sendbuf_hds_len += hds_len; return CURLE_OK; } @@ -435,11 +432,12 @@ CURLcode Curl_req_send_more(struct Curl_easy *data) /* Fill our send buffer if more from client can be read. */ if(!data->req.upload_aborted && !data->req.eos_read && - !(data->req.keepon & KEEP_SEND_PAUSE) && + !Curl_xfer_send_is_paused(data) && !Curl_bufq_is_full(&data->req.sendbuf)) { - ssize_t nread = Curl_bufq_sipn(&data->req.sendbuf, 0, - add_from_client, data, &result); - if(nread < 0 && result != CURLE_AGAIN) + size_t nread; + result = Curl_bufq_sipn(&data->req.sendbuf, 0, + add_from_client, data, &nread); + if(result && result != CURLE_AGAIN) return result; } diff --git a/lib/select.c b/lib/select.c index ee239b7321..2353c474df 100644 --- a/lib/select.c +++ b/lib/select.c @@ -36,75 +36,19 @@ #include #endif -#ifdef MSDOS -#include /* delay() */ -#endif - #include #include "urldata.h" #include "connect.h" #include "select.h" #include "curlx/timediff.h" +#include "curlx/wait.h" #include "curlx/warnless.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" -/* - * Internal function used for waiting a specific amount of ms in - * Curl_socket_check() and Curl_poll() when no file descriptor is provided to - * wait on, just being used to delay execution. Winsock select() and poll() - * timeout mechanisms need a valid socket descriptor in a not null file - * descriptor set to work. Waiting indefinitely with this function is not - * allowed, a zero or negative timeout value will return immediately. Timeout - * resolution, accuracy, as well as maximum supported value is system - * dependent, neither factor is a critical issue for the intended use of this - * function in the library. - * - * Return values: - * -1 = system call error, or invalid timeout value - * 0 = specified timeout has elapsed, or interrupted - */ -int Curl_wait_ms(timediff_t timeout_ms) -{ - int r = 0; - - if(!timeout_ms) - return 0; - if(timeout_ms < 0) { - SET_SOCKERRNO(SOCKEINVAL); - return -1; - } -#if defined(MSDOS) - delay((unsigned int)timeout_ms); -#elif defined(_WIN32) - /* prevent overflow, timeout_ms is typecast to ULONG/DWORD. */ -#if TIMEDIFF_T_MAX >= ULONG_MAX - if(timeout_ms >= ULONG_MAX) - timeout_ms = ULONG_MAX-1; - /* do not use ULONG_MAX, because that is equal to INFINITE */ -#endif - Sleep((DWORD)timeout_ms); -#else - /* avoid using poll() for this since it behaves incorrectly with no sockets - on Apple operating systems */ - { - struct timeval pending_tv; - r = select(0, NULL, NULL, NULL, curlx_mstotv(&pending_tv, timeout_ms)); - } -#endif /* _WIN32 */ - if(r) { - if((r == -1) && (SOCKERRNO == SOCKEINTR)) - /* make EINTR from select or poll not a "lethal" error */ - r = 0; - else - r = -1; - } - return r; -} - #ifndef HAVE_POLL /* * This is a wrapper around select() to aid in Windows compatibility. A @@ -132,7 +76,7 @@ static int our_select(curl_socket_t maxfd, /* highest socket number */ (!fds_write || fds_write->fd_count == 0) && (!fds_err || fds_err->fd_count == 0)) { /* no sockets, just wait */ - return Curl_wait_ms(timeout_ms); + return curlx_wait_ms(timeout_ms); } #endif @@ -194,7 +138,7 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */ if((readfd0 == CURL_SOCKET_BAD) && (readfd1 == CURL_SOCKET_BAD) && (writefd == CURL_SOCKET_BAD)) { /* no sockets, just wait */ - return Curl_wait_ms(timeout_ms); + return curlx_wait_ms(timeout_ms); } /* Avoid initial timestamp, avoid curlx_now() call, when elapsed @@ -289,7 +233,7 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms) } if(fds_none) { /* no sockets, just wait */ - return Curl_wait_ms(timeout_ms); + return curlx_wait_ms(timeout_ms); } /* Avoid initial timestamp, avoid curlx_now() call, when elapsed diff --git a/lib/select.h b/lib/select.h index 10968fab7f..e9cec600f3 100644 --- a/lib/select.h +++ b/lib/select.h @@ -82,7 +82,6 @@ int Curl_socket_check(curl_socket_t readfd, curl_socket_t readfd2, Curl_socket_check(CURL_SOCKET_BAD, CURL_SOCKET_BAD, x, z) int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms); -int Curl_wait_ms(timediff_t timeout_ms); /* With Winsock the valid range is [0..INVALID_SOCKET-1] according to diff --git a/lib/sendf.c b/lib/sendf.c index feb4598b06..339a8773b4 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -38,6 +38,7 @@ #include "urldata.h" #include "sendf.h" +#include "transfer.h" #include "cfilters.h" #include "connect.h" #include "content_encoding.h" @@ -176,8 +177,8 @@ void Curl_creader_set_rewind(struct Curl_easy *data, bool enable) /* Write data using an unencoding writer stack. */ CURLcode Curl_cwriter_write(struct Curl_easy *data, - struct Curl_cwriter *writer, int type, - const char *buf, size_t nbytes) + struct Curl_cwriter *writer, int type, + const char *buf, size_t nbytes) { if(!writer) return CURLE_WRITE_ERROR; @@ -376,9 +377,9 @@ static const struct Curl_cwtype cw_raw = { /* Create an unencoding writer stage using the given handler. */ CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter, - struct Curl_easy *data, - const struct Curl_cwtype *cwt, - Curl_cwriter_phase phase) + struct Curl_easy *data, + const struct Curl_cwtype *cwt, + Curl_cwriter_phase phase) { struct Curl_cwriter *writer = NULL; CURLcode result = CURLE_OUT_OF_MEMORY; @@ -403,7 +404,7 @@ out: } void Curl_cwriter_free(struct Curl_easy *data, - struct Curl_cwriter *writer) + struct Curl_cwriter *writer) { if(writer) { writer->cwt->do_close(data, writer); @@ -660,6 +661,7 @@ static CURLcode cr_in_read(struct Curl_easy *data, size_t *pnread, bool *peos) { struct cr_in_ctx *ctx = reader->ctx; + CURLcode result = CURLE_OK; size_t nread; ctx->is_paused = FALSE; @@ -697,7 +699,8 @@ static CURLcode cr_in_read(struct Curl_easy *data, failf(data, "client read function EOF fail, " "only %"FMT_OFF_T"/%"FMT_OFF_T " of needed bytes read", ctx->read_len, ctx->total_len); - return CURLE_READ_ERROR; + result = CURLE_READ_ERROR; + break; } *pnread = 0; *peos = TRUE; @@ -710,7 +713,8 @@ static CURLcode cr_in_read(struct Curl_easy *data, *peos = FALSE; ctx->errored = TRUE; ctx->error_result = CURLE_ABORTED_BY_CALLBACK; - return CURLE_ABORTED_BY_CALLBACK; + result = CURLE_ABORTED_BY_CALLBACK; + break; case CURL_READFUNC_PAUSE: if(data->conn->handler->flags & PROTOPT_NONETWORK) { @@ -718,14 +722,15 @@ static CURLcode cr_in_read(struct Curl_easy *data, actually only FILE:// just now, and it cannot pause since the transfer is not done using the "normal" procedure. */ failf(data, "Read callback asked for PAUSE when not supported"); - return CURLE_READ_ERROR; + result = CURLE_READ_ERROR; + break; } /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */ CURL_TRC_READ(data, "cr_in_read, callback returned CURL_READFUNC_PAUSE"); ctx->is_paused = TRUE; - data->req.keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */ *pnread = 0; *peos = FALSE; + result = Curl_xfer_pause_send(data, TRUE); break; /* nothing was read */ default: @@ -736,7 +741,8 @@ static CURLcode cr_in_read(struct Curl_easy *data, *peos = FALSE; ctx->errored = TRUE; ctx->error_result = CURLE_READ_ERROR; - return CURLE_READ_ERROR; + result = CURLE_READ_ERROR; + break; } ctx->read_len += nread; if(ctx->total_len >= 0) @@ -747,9 +753,9 @@ static CURLcode cr_in_read(struct Curl_easy *data, } CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%"FMT_OFF_T ", read=%"FMT_OFF_T") -> %d, nread=%zu, eos=%d", - blen, ctx->total_len, ctx->read_len, CURLE_OK, + blen, ctx->total_len, ctx->read_len, result, *pnread, *peos); - return CURLE_OK; + return result; } static bool cr_in_needs_rewind(struct Curl_easy *data, @@ -1362,7 +1368,7 @@ static const struct Curl_crtype cr_buf = { }; CURLcode Curl_creader_set_buf(struct Curl_easy *data, - const char *buf, size_t blen) + const char *buf, size_t blen) { CURLcode result; struct Curl_creader *r; diff --git a/lib/setopt.c b/lib/setopt.c index d5ddf6d0c2..1380c33db6 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -60,6 +60,35 @@ #include "curl_memory.h" #include "memdebug.h" + +static CURLcode setopt_set_timeout_sec(timediff_t *ptimeout_ms, long secs) +{ + if(secs < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; +#if LONG_MAX > (TIMEDIFF_T_MAX/1000) + if(secs > (TIMEDIFF_T_MAX/1000)) { + *ptimeout_ms = TIMEDIFF_T_MAX; + return CURLE_OK; + } +#endif + *ptimeout_ms = (timediff_t)secs * 1000; + return CURLE_OK; +} + +static CURLcode setopt_set_timeout_ms(timediff_t *ptimeout_ms, long ms) +{ + if(ms < 0) + return CURLE_BAD_FUNCTION_ARGUMENT; +#if LONG_MAX > TIMEDIFF_T_MAX + if(ms > TIMEDIFF_T_MAX) { + *ptimeout_ms = TIMEDIFF_T_MAX; + return CURLE_OK; + } +#endif + *ptimeout_ms = (timediff_t)ms; + return CURLE_OK; +} + CURLcode Curl_setstropt(char **charp, const char *s) { /* Release the previous storage at `charp' and replace by a dynamic storage @@ -211,6 +240,7 @@ static CURLcode protocol2num(const char *str, curl_prot_t *val) return CURLE_OK; } +#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_PROXY) static CURLcode httpauth(struct Curl_easy *data, bool proxy, unsigned long auth) { @@ -255,6 +285,7 @@ static CURLcode httpauth(struct Curl_easy *data, bool proxy, data->set.httpauth = auth; return CURLE_OK; } +#endif /* !CURL_DISABLE_HTTP || !CURL_DISABLE_PROXY */ #ifndef CURL_DISABLE_HTTP static CURLcode setopt_HTTP_VERSION(struct Curl_easy *data, long arg) @@ -526,21 +557,15 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, * Option that specifies how quickly a server response must be obtained * before it is considered failure. For pingpong protocols. */ - if((arg >= 0) && (arg <= (INT_MAX/1000))) - data->set.server_response_timeout = (unsigned int)arg * 1000; - else - return CURLE_BAD_FUNCTION_ARGUMENT; - break; + return setopt_set_timeout_sec(&data->set.server_response_timeout, arg); + case CURLOPT_SERVER_RESPONSE_TIMEOUT_MS: /* * Option that specifies how quickly a server response must be obtained * before it is considered failure. For pingpong protocols. */ - if((arg >= 0) && (arg <= INT_MAX)) - data->set.server_response_timeout = (unsigned int)arg; - else - return CURLE_BAD_FUNCTION_ARGUMENT; - break; + return setopt_set_timeout_ms(&data->set.server_response_timeout, arg); + #ifndef CURL_DISABLE_TFTP case CURLOPT_TFTP_NO_OPTIONS: /* @@ -883,10 +908,8 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, /* * The maximum time for curl to wait for FTP server connect */ - if(uarg > UINT_MAX) - uarg = UINT_MAX; - data->set.accepttimeout = (unsigned int)uarg; - break; + return setopt_set_timeout_ms(&data->set.accepttimeout, arg); + case CURLOPT_WILDCARDMATCH: data->set.wildcard_enabled = enabled; break; @@ -943,33 +966,19 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, * The maximum time you allow curl to use for a single transfer * operation. */ - if((arg >= 0) && (arg <= (INT_MAX/1000))) - data->set.timeout = (unsigned int)arg * 1000; - else - return CURLE_BAD_FUNCTION_ARGUMENT; - break; + return setopt_set_timeout_sec(&data->set.timeout, arg); case CURLOPT_TIMEOUT_MS: - if(uarg > UINT_MAX) - uarg = UINT_MAX; - data->set.timeout = (unsigned int)uarg; - break; + return setopt_set_timeout_ms(&data->set.timeout, arg); case CURLOPT_CONNECTTIMEOUT: /* * The maximum time you allow curl to use to connect. */ - if((arg >= 0) && (arg <= (INT_MAX/1000))) - data->set.connecttimeout = (unsigned int)arg * 1000; - else - return CURLE_BAD_FUNCTION_ARGUMENT; - break; + return setopt_set_timeout_sec(&data->set.connecttimeout, arg); case CURLOPT_CONNECTTIMEOUT_MS: - if(uarg > UINT_MAX) - uarg = UINT_MAX; - data->set.connecttimeout = (unsigned int)uarg; - break; + return setopt_set_timeout_ms(&data->set.connecttimeout, arg); case CURLOPT_RESUME_FROM: /* @@ -1074,13 +1083,9 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, break; case CURLOPT_SSL_FALSESTART: /* - * Enable TLS false start. + * No TLS backends support false start anymore. */ - if(!Curl_ssl_false_start()) - return CURLE_NOT_BUILT_IN; - - data->set.ssl.falsestart = enabled; - break; + return CURLE_NOT_BUILT_IN; case CURLOPT_CERTINFO: #ifdef USE_SSL if(Curl_ssl_supports(data, SSLSUPP_CERTINFO)) @@ -1347,10 +1352,8 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, data->set.suppress_connect_headers = enabled; break; case CURLOPT_HAPPY_EYEBALLS_TIMEOUT_MS: - if(uarg > UINT_MAX) - uarg = UINT_MAX; - data->set.happy_eyeballs_timeout = (unsigned int)uarg; - break; + return setopt_set_timeout_ms(&data->set.happy_eyeballs_timeout, arg); + #ifndef CURL_DISABLE_SHUFFLE_DNS case CURLOPT_DNS_SHUFFLE_ADDRESSES: data->set.dns_shuffle_addresses = enabled; @@ -1366,15 +1369,11 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option, data->set.upkeep_interval_ms = arg; break; case CURLOPT_MAXAGE_CONN: - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.maxage_conn = arg; - break; + return setopt_set_timeout_sec(&data->set.conn_max_idle_ms, arg); + case CURLOPT_MAXLIFETIME_CONN: - if(arg < 0) - return CURLE_BAD_FUNCTION_ARGUMENT; - data->set.maxlifetime_conn = arg; - break; + return setopt_set_timeout_sec(&data->set.conn_max_age_ms, arg); + #ifndef CURL_DISABLE_HSTS case CURLOPT_HSTS_CTRL: if(arg & CURLHSTS_ENABLE) { @@ -1877,23 +1876,23 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, if(!ptr) break; - if(strcasecompare(ptr, "ALL")) { + if(curl_strequal(ptr, "ALL")) { /* clear all cookies */ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); Curl_cookie_clearall(data->cookies); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } - else if(strcasecompare(ptr, "SESS")) { + else if(curl_strequal(ptr, "SESS")) { /* clear session cookies */ Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); Curl_cookie_clearsess(data->cookies); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } - else if(strcasecompare(ptr, "FLUSH")) { + else if(curl_strequal(ptr, "FLUSH")) { /* flush cookies to file, takes care of the locking */ Curl_flush_cookies(data, FALSE); } - else if(strcasecompare(ptr, "RELOAD")) { + else if(curl_strequal(ptr, "RELOAD")) { /* reload cookies from file */ Curl_cookie_loadfiles(data); break; @@ -2557,12 +2556,12 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option, return Curl_setstropt(&data->set.str[STRING_TLSAUTH_PASSWORD_PROXY], ptr); #endif case CURLOPT_TLSAUTH_TYPE: - if(ptr && !strcasecompare(ptr, "SRP")) + if(ptr && !curl_strequal(ptr, "SRP")) return CURLE_BAD_FUNCTION_ARGUMENT; break; #ifndef CURL_DISABLE_PROXY case CURLOPT_PROXY_TLSAUTH_TYPE: - if(ptr && !strcasecompare(ptr, "SRP")) + if(ptr && !curl_strequal(ptr, "SRP")) return CURLE_BAD_FUNCTION_ARGUMENT; break; #endif diff --git a/lib/setup-vms.h b/lib/setup-vms.h index d74f4ad630..0fd5e542b7 100644 --- a/lib/setup-vms.h +++ b/lib/setup-vms.h @@ -374,11 +374,9 @@ static struct passwd *vms_getpwuid(uid_t uid) #ifdef HAVE_NETDB_H #include #ifndef AI_NUMERICHOST -#ifdef USE_IPV6 #undef USE_IPV6 #endif #endif -#endif /* VAX symbols are always in uppercase */ #ifdef __VAX diff --git a/lib/setup-win32.h b/lib/setup-win32.h index 35fe513a84..02177a7a3b 100644 --- a/lib/setup-win32.h +++ b/lib/setup-win32.h @@ -30,10 +30,10 @@ /* ---------------------------------------------------------------- */ #ifdef USE_WATT32 # include +# include # undef byte # undef word # define HAVE_SYS_IOCTL_H -# define HAVE_SYS_SOCKET_H # define HAVE_NETINET_IN_H # define HAVE_NETDB_H # define HAVE_ARPA_INET_H @@ -82,9 +82,6 @@ # include # include # include -# ifdef UNICODE - typedef wchar_t *(*curl_wcsdup_callback)(const wchar_t *str); -# endif #endif /* diff --git a/lib/smb.c b/lib/smb.c index ceca9564bf..3eee16468d 100644 --- a/lib/smb.c +++ b/lib/smb.c @@ -543,7 +543,7 @@ static CURLcode smb_recv_message(struct Curl_easy *data, void **msg) { char *buf = smbc->recv_buf; - ssize_t bytes_read; + size_t bytes_read; size_t nbt_size; size_t msg_size; size_t len = MAX_MESSAGE_SIZE - smbc->got; @@ -999,8 +999,8 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done) */ static void get_posix_time(time_t *out, curl_off_t timestamp) { - if(timestamp >= CURL_OFF_T_C(116444736000000000)) { - timestamp -= CURL_OFF_T_C(116444736000000000); + if(timestamp >= (curl_off_t)116444736000000000) { + timestamp -= (curl_off_t)116444736000000000; timestamp /= 10000000; #if SIZEOF_TIME_T < SIZEOF_CURL_OFF_T if(timestamp > TIME_T_MAX) diff --git a/lib/smtp.c b/lib/smtp.c index d39bb58d5d..d2adf43a7c 100644 --- a/lib/smtp.c +++ b/lib/smtp.c @@ -68,7 +68,6 @@ #include "mime.h" #include "socks.h" #include "smtp.h" -#include "strcase.h" #include "vtls/vtls.h" #include "cfilters.h" #include "connect.h" @@ -1625,13 +1624,12 @@ static CURLcode smtp_disconnect(struct Curl_easy *data, bad in any way, sending quit and waiting around here will make the disconnect wait in vain and cause more problems than we need to. */ - if(!dead_connection && conn->bits.protoconnstart) { + if(!dead_connection && conn->bits.protoconnstart && + !Curl_pp_needs_flush(data, &smtpc->pp)) { if(!smtp_perform_quit(data, smtpc)) (void)smtp_block_statemach(data, smtpc, TRUE); /* ignore on QUIT */ } - /* Cleanup the SASL module */ - Curl_sasl_cleanup(conn, smtpc->sasl.authused); CURL_TRC_SMTP(data, "smtp_disconnect(), finished"); return CURLE_OK; } @@ -1776,7 +1774,7 @@ static CURLcode smtp_parse_url_options(struct connectdata *conn, while(*ptr && *ptr != ';') ptr++; - if(strncasecompare(key, "AUTH=", 5)) + if(curl_strnequal(key, "AUTH=", 5)) result = Curl_sasl_parse_url_auth_option(&smtpc->sasl, value, ptr - value); else @@ -2007,7 +2005,7 @@ static CURLcode cr_eob_read(struct Curl_easy *data, eob = &SMTP_EOB[2]; break; case 3: - /* ended with '\r\n.', we should escpe the last '.' */ + /* ended with '\r\n.', we should escape the last '.' */ eob = "." SMTP_EOB; break; default: diff --git a/lib/socks.c b/lib/socks.c index 08bc874f11..7d25ef5208 100644 --- a/lib/socks.c +++ b/lib/socks.c @@ -81,7 +81,7 @@ enum connect_t { struct socks_state { enum connect_t state; - ssize_t outstanding; /* send this many bytes more */ + size_t outstanding; /* send this many bytes more */ unsigned char buffer[CURL_SOCKS_BUF_SIZE]; unsigned char *outp; /* send from this pointer */ @@ -101,54 +101,41 @@ struct socks_state { int Curl_blockread_all(struct Curl_cfilter *cf, struct Curl_easy *data, /* transfer */ char *buf, /* store read data here */ - ssize_t buffersize, /* max amount to read */ - ssize_t *n) /* amount bytes read */ + size_t blen, /* space in buf */ + size_t *pnread) /* amount bytes read */ { - ssize_t nread = 0; - ssize_t allread = 0; - int result; - CURLcode err = CURLE_OK; + size_t nread = 0; + CURLcode err; - *n = 0; + *pnread = 0; for(;;) { timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE); if(timeout_ms < 0) { /* we already got the timeout */ - result = CURLE_OPERATION_TIMEDOUT; - break; + return CURLE_OPERATION_TIMEDOUT; } if(!timeout_ms) timeout_ms = TIMEDIFF_T_MAX; if(SOCKET_READABLE(cf->conn->sock[cf->sockindex], timeout_ms) <= 0) { - result = ~CURLE_OK; - break; - } - nread = Curl_conn_cf_recv(cf->next, data, buf, buffersize, &err); - if(nread <= 0) { - result = (int)err; - if(CURLE_AGAIN == err) - continue; - if(err) { - break; - } + return ~CURLE_OK; } + err = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread); + if(CURLE_AGAIN == err) + continue; + else if(err) + return (int)err; - if(buffersize == nread) { - allread += nread; - *n = allread; - result = CURLE_OK; - break; - } - if(!nread) { - result = ~CURLE_OK; - break; + if(blen == nread) { + *pnread += nread; + return CURLE_OK; } + if(!nread) /* EOF */ + return ~CURLE_OK; - buffersize -= nread; buf += nread; - allread += nread; + blen -= nread; + *pnread += nread; } - return result; } #endif @@ -213,24 +200,25 @@ static CURLproxycode socks_state_send(struct Curl_cfilter *cf, CURLproxycode failcode, const char *description) { - ssize_t nwritten; + size_t nwritten; CURLcode result; - nwritten = Curl_conn_cf_send(cf->next, data, (char *)sx->outp, - sx->outstanding, FALSE, &result); - if(nwritten <= 0) { - if(CURLE_AGAIN == result) { + result = Curl_conn_cf_send(cf->next, data, (char *)sx->outp, + sx->outstanding, FALSE, &nwritten); + if(result) { + if(CURLE_AGAIN == result) return CURLPX_OK; - } - else if(CURLE_OK == result) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; - } + failf(data, "Failed to send %s: %s", description, curl_easy_strerror(result)); return failcode; } + else if(!nwritten) { + /* connection closed */ + failf(data, "connection to proxy closed"); + return CURLPX_CLOSED; + } + DEBUGASSERT(sx->outstanding >= nwritten); /* not done, remain in state */ sx->outstanding -= nwritten; @@ -244,24 +232,24 @@ static CURLproxycode socks_state_recv(struct Curl_cfilter *cf, CURLproxycode failcode, const char *description) { - ssize_t nread; + size_t nread; CURLcode result; - nread = Curl_conn_cf_recv(cf->next, data, (char *)sx->outp, - sx->outstanding, &result); - if(nread <= 0) { - if(CURLE_AGAIN == result) { + result = Curl_conn_cf_recv(cf->next, data, (char *)sx->outp, + sx->outstanding, &nread); + if(result) { + if(CURLE_AGAIN == result) return CURLPX_OK; - } - else if(CURLE_OK == result) { - /* connection closed */ - failf(data, "connection to proxy closed"); - return CURLPX_CLOSED; - } + failf(data, "SOCKS: Failed receiving %s: %s", description, curl_easy_strerror(result)); return failcode; } + else if(!nread) { + /* connection closed */ + failf(data, "connection to proxy closed"); + return CURLPX_CLOSED; + } /* remain in reading state */ DEBUGASSERT(sx->outstanding >= nread); sx->outstanding -= nread; @@ -1158,8 +1146,8 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf, } static void socks_cf_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) + struct Curl_easy *data, + struct easy_pollset *ps) { struct socks_state *sx = cf->ctx; @@ -1199,21 +1187,25 @@ static void socks_proxy_cf_destroy(struct Curl_cfilter *cf, socks_proxy_cf_free(cf); } -static void socks_cf_get_host(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char **phost, - const char **pdisplay_host, - int *pport) +static CURLcode socks_cf_query(struct Curl_cfilter *cf, + struct Curl_easy *data, + int query, int *pres1, void *pres2) { - (void)data; - if(!cf->connected) { - *phost = cf->conn->socks_proxy.host.name; - *pdisplay_host = cf->conn->http_proxy.host.dispname; - *pport = (int)cf->conn->socks_proxy.port; - } - else { - cf->next->cft->get_host(cf->next, data, phost, pdisplay_host, pport); + struct socks_state *sx = cf->ctx; + + if(sx) { + switch(query) { + case CF_QUERY_HOST_PORT: + *pres1 = sx->remote_port; + *((const char **)pres2) = sx->hostname; + 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_socks_proxy = { @@ -1224,7 +1216,6 @@ struct Curl_cftype Curl_cft_socks_proxy = { socks_proxy_cf_connect, socks_proxy_cf_close, Curl_cf_def_shutdown, - socks_cf_get_host, socks_cf_adjust_pollset, Curl_cf_def_data_pending, Curl_cf_def_send, @@ -1232,7 +1223,7 @@ struct Curl_cftype Curl_cft_socks_proxy = { Curl_cf_def_cntrl, Curl_cf_def_conn_is_alive, Curl_cf_def_conn_keep_alive, - Curl_cf_def_query, + socks_cf_query, }; CURLcode Curl_cf_socks_proxy_insert_after(struct Curl_cfilter *cf_at, diff --git a/lib/socks.h b/lib/socks.h index a3adcc6e91..d60796316f 100644 --- a/lib/socks.h +++ b/lib/socks.h @@ -40,8 +40,8 @@ int Curl_blockread_all(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, - ssize_t buffersize, - ssize_t *n); + size_t blen, + size_t *pnread); #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) /* diff --git a/lib/socks_gssapi.c b/lib/socks_gssapi.c index 697e301b77..f23a1ae6ed 100644 --- a/lib/socks_gssapi.c +++ b/lib/socks_gssapi.c @@ -115,8 +115,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, struct connectdata *conn = cf->conn; curl_socket_t sock = conn->sock[cf->sockindex]; CURLcode code; - ssize_t actualread; - ssize_t nwritten; + size_t actualread; + size_t nwritten; int result; OM_uint32 gss_major_status, gss_minor_status, gss_status; OM_uint32 gss_ret_flags; @@ -199,7 +199,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to initial GSS-API token."); return CURLE_COULDNT_CONNECT; } @@ -210,26 +210,26 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, us_length = htons((unsigned short)gss_send_token.length); memcpy(socksreq + 2, &us_length, sizeof(short)); - nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, - FALSE, &code); + code = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, + FALSE, &nwritten); if(code || (4 != nwritten)) { failf(data, "Failed to send GSS-API authentication request."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } - nwritten = Curl_conn_cf_send(cf->next, data, - (char *)gss_send_token.value, - gss_send_token.length, FALSE, &code); - if(code || ((ssize_t)gss_send_token.length != nwritten)) { + code = Curl_conn_cf_send(cf->next, data, + (char *)gss_send_token.value, + gss_send_token.length, FALSE, &nwritten); + if(code || (gss_send_token.length != nwritten)) { failf(data, "Failed to send GSS-API authentication token."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_send_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -254,7 +254,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(result || (actualread != 4)) { failf(data, "Failed to receive GSS-API authentication response."); gss_release_name(&gss_status, &server); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -263,7 +263,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); gss_release_name(&gss_status, &server); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -271,7 +271,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "Invalid GSS-API authentication response type (%d %d).", socksreq[0], socksreq[1]); gss_release_name(&gss_status, &server); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -285,7 +285,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, "Could not allocate memory for GSS-API authentication " "response token."); gss_release_name(&gss_status, &server); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } @@ -296,7 +296,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "Failed to receive GSS-API authentication token."); gss_release_name(&gss_status, &server); gss_release_buffer(&gss_status, &gss_recv_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -311,7 +311,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, NULL, NULL, NULL); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_inquire_context")) { - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); failf(data, "Failed to determine username."); return CURLE_COULDNT_CONNECT; @@ -320,7 +320,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, &gss_send_token, NULL); if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_display_name")) { - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); failf(data, "Failed to determine username."); @@ -328,7 +328,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, } user = malloc(gss_send_token.length + 1); if(!user) { - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); gss_release_name(&gss_status, &gss_client_name); gss_release_buffer(&gss_status, &gss_send_token); return CURLE_OUT_OF_MEMORY; @@ -397,7 +397,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_send_token.length = 1; gss_send_token.value = Curl_memdup(&gss_enc, 1); if(!gss_send_token.value) { - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } @@ -408,7 +408,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_wrap")) { gss_release_buffer(&gss_status, &gss_send_token); gss_release_buffer(&gss_status, &gss_w_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to wrap GSS-API encryption value into token."); return CURLE_COULDNT_CONNECT; } @@ -418,33 +418,32 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, memcpy(socksreq + 2, &us_length, sizeof(short)); } - nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, FALSE, - &code); - if(code || (4 != nwritten)) { + code = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, FALSE, + &nwritten); + if(code || (4 != nwritten)) { failf(data, "Failed to send GSS-API encryption request."); gss_release_buffer(&gss_status, &gss_w_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); - nwritten = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, FALSE, - &code); - if(code || ( 1 != nwritten)) { + code = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, FALSE, + &nwritten); + if(code || (1 != nwritten)) { failf(data, "Failed to send GSS-API encryption type."); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } } else { - nwritten = Curl_conn_cf_send(cf->next, data, - (char *)gss_w_token.value, - gss_w_token.length, FALSE, &code); - if(code || ((ssize_t)gss_w_token.length != nwritten)) { + code = Curl_conn_cf_send(cf->next, data, (char *)gss_w_token.value, + gss_w_token.length, FALSE, &nwritten); + if(code || (gss_w_token.length != nwritten)) { failf(data, "Failed to send GSS-API encryption type."); gss_release_buffer(&gss_status, &gss_w_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } gss_release_buffer(&gss_status, &gss_w_token); @@ -453,7 +452,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, result = Curl_blockread_all(cf, data, (char *)socksreq, 4, &actualread); if(result || (actualread != 4)) { failf(data, "Failed to receive GSS-API encryption response."); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -461,14 +460,14 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(socksreq[1] == 255) { /* status / message type */ failf(data, "User was rejected by the SOCKS5 server (%d %d).", socksreq[0], socksreq[1]); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } if(socksreq[1] != 2) { /* status / message type */ failf(data, "Invalid GSS-API encryption response type (%d %d).", socksreq[0], socksreq[1]); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -478,7 +477,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, gss_recv_token.length = us_length; gss_recv_token.value = malloc(gss_recv_token.length); if(!gss_recv_token.value) { - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OUT_OF_MEMORY; } result = Curl_blockread_all(cf, data, (char *)gss_recv_token.value, @@ -487,7 +486,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(result || (actualread != us_length)) { failf(data, "Failed to receive GSS-API encryption type."); gss_release_buffer(&gss_status, &gss_recv_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -499,7 +498,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(check_gss_err(data, gss_major_status, gss_minor_status, "gss_unwrap")) { gss_release_buffer(&gss_status, &gss_recv_token); gss_release_buffer(&gss_status, &gss_w_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); failf(data, "Failed to unwrap GSS-API encryption value into token."); return CURLE_COULDNT_CONNECT; } @@ -509,7 +508,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "Invalid GSS-API encryption response length (%zu).", gss_w_token.length); gss_release_buffer(&gss_status, &gss_w_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -521,7 +520,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, failf(data, "Invalid GSS-API encryption response length (%zu).", gss_recv_token.length); gss_release_buffer(&gss_status, &gss_recv_token); - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_COULDNT_CONNECT; } @@ -538,7 +537,7 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, conn->socks5_gssapi_enctype = socksreq[0]; if(socksreq[0] == 0) - gss_delete_sec_context(&gss_status, &gss_context, NULL); + Curl_gss_delete_sec_context(&gss_status, &gss_context, NULL); return CURLE_OK; } diff --git a/lib/socks_sspi.c b/lib/socks_sspi.c index 1f0846dda4..82147562b7 100644 --- a/lib/socks_sspi.c +++ b/lib/socks_sspi.c @@ -69,8 +69,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, struct connectdata *conn = cf->conn; curl_socket_t sock = conn->sock[cf->sockindex]; CURLcode code; - ssize_t actualread; - ssize_t written; + size_t actualread; + size_t written; int result; /* Needs GSS-API authentication */ SECURITY_STATUS status; @@ -207,8 +207,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, us_length = htons((unsigned short)sspi_send_token.cbBuffer); memcpy(socksreq + 2, &us_length, sizeof(short)); - written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, FALSE, - &code); + code = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, FALSE, + &written); if(code || (4 != written)) { failf(data, "Failed to send SSPI authentication request."); free(service_name); @@ -221,10 +221,10 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, return CURLE_COULDNT_CONNECT; } - written = Curl_conn_cf_send(cf->next, data, - (char *)sspi_send_token.pvBuffer, - sspi_send_token.cbBuffer, FALSE, &code); - if(code || (sspi_send_token.cbBuffer != (size_t)written)) { + code = Curl_conn_cf_send(cf->next, data, + (char *)sspi_send_token.pvBuffer, + sspi_send_token.cbBuffer, FALSE, &written); + if(code || (sspi_send_token.cbBuffer != written)) { failf(data, "Failed to send SSPI authentication token."); free(service_name); if(sspi_send_token.pvBuffer) @@ -478,8 +478,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, memcpy(socksreq + 2, &us_length, sizeof(short)); } - written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, FALSE, - &code); + code = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 4, FALSE, + &written); if(code || (4 != written)) { failf(data, "Failed to send SSPI encryption request."); if(sspi_send_token.pvBuffer) @@ -490,8 +490,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, if(data->set.socks5_gssapi_nec) { memcpy(socksreq, &gss_enc, 1); - written = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, FALSE, - &code); + code = Curl_conn_cf_send(cf->next, data, (char *)socksreq, 1, FALSE, + &written); if(code || (1 != written)) { failf(data, "Failed to send SSPI encryption type."); Curl_pSecFn->DeleteSecurityContext(&sspi_context); @@ -499,9 +499,9 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf, } } else { - written = Curl_conn_cf_send(cf->next, data, - (char *)sspi_send_token.pvBuffer, - sspi_send_token.cbBuffer, FALSE, &code); + code = Curl_conn_cf_send(cf->next, data, + (char *)sspi_send_token.pvBuffer, + sspi_send_token.cbBuffer, FALSE, &written); if(code || (sspi_send_token.cbBuffer != (size_t)written)) { failf(data, "Failed to send SSPI encryption type."); if(sspi_send_token.pvBuffer) diff --git a/lib/speedcheck.c b/lib/speedcheck.c index 16d7d72562..b063e5d4f2 100644 --- a/lib/speedcheck.c +++ b/lib/speedcheck.c @@ -27,6 +27,7 @@ #include #include "urldata.h" #include "sendf.h" +#include "transfer.h" #include "multiif.h" #include "speedcheck.h" @@ -41,7 +42,7 @@ void Curl_speedinit(struct Curl_easy *data) CURLcode Curl_speedcheck(struct Curl_easy *data, struct curltime now) { - if(data->req.keepon & KEEP_RECV_PAUSE) + if(Curl_xfer_recv_is_paused(data)) /* A paused transfer is not qualified for speed checks */ return CURLE_OK; diff --git a/lib/strcase.h b/lib/strcase.h index 8c50bbcba6..915c996926 100644 --- a/lib/strcase.h +++ b/lib/strcase.h @@ -26,18 +26,6 @@ #include -/* - * Only "raw" case insensitive strings. This is meant to be locale independent - * and only compare strings we know are safe for this. - * - * The function is capable of comparing a-z case insensitively. - * - * Result is 1 if text matches and 0 if not. - */ - -#define strcasecompare(a,b) curl_strequal(a,b) -#define strncasecompare(a,b,c) curl_strnequal(a,b,c) - char Curl_raw_toupper(char in); char Curl_raw_tolower(char in); diff --git a/lib/strequal.c b/lib/strequal.c index d1ba91501b..fb6be51dd5 100644 --- a/lib/strequal.c +++ b/lib/strequal.c @@ -65,6 +65,15 @@ static int ncasecompare(const char *first, const char *second, size_t max) return Curl_raw_toupper(*first) == Curl_raw_toupper(*second); } +/* + * Only "raw" case insensitive strings. This is meant to be locale independent + * and only compare strings we know are safe for this. + * + * The function is capable of comparing a-z case insensitively. + * + * Result is 1 if text matches and 0 if not. + */ + /* --- public function --- */ int curl_strequal(const char *first, const char *second) { diff --git a/lib/strerror.c b/lib/strerror.c index 9cf93dd66e..90694882c3 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -317,36 +317,7 @@ curl_easy_strerror(CURLcode error) return "ECH attempted but failed"; /* error codes not used by current libcurl */ - case CURLE_OBSOLETE20: - case CURLE_OBSOLETE24: - case CURLE_OBSOLETE29: - case CURLE_OBSOLETE32: - case CURLE_OBSOLETE34: - case CURLE_OBSOLETE40: - case CURLE_OBSOLETE41: - case CURLE_OBSOLETE44: - case CURLE_OBSOLETE46: - case CURLE_OBSOLETE50: - case CURLE_OBSOLETE51: - case CURLE_OBSOLETE57: - case CURLE_OBSOLETE62: - case CURLE_OBSOLETE75: - case CURLE_OBSOLETE76: - - /* error codes used by curl tests */ - case CURLE_RESERVED115: - case CURLE_RESERVED116: - case CURLE_RESERVED117: - case CURLE_RESERVED118: - case CURLE_RESERVED119: - case CURLE_RESERVED120: - case CURLE_RESERVED121: - case CURLE_RESERVED122: - case CURLE_RESERVED123: - case CURLE_RESERVED124: - case CURLE_RESERVED125: - case CURLE_RESERVED126: - case CURL_LAST: + default: break; } /* diff --git a/lib/system_win32.c b/lib/system_win32.c index 5212887580..a890a0ea98 100644 --- a/lib/system_win32.c +++ b/lib/system_win32.c @@ -36,12 +36,17 @@ #include "curl_memory.h" #include "memdebug.h" +#ifndef HAVE_IF_NAMETOINDEX /* Handle of iphlpapp.dll */ static HMODULE s_hIpHlpApiDll = NULL; /* Pointer to the if_nametoindex function */ IF_NAMETOINDEX_FN Curl_if_nametoindex = NULL; +/* This is used to dynamically load DLLs */ +static HMODULE curl_load_library(LPCTSTR filename); +#endif + /* Curl_win32_init() performs Win32 global initialization */ CURLcode Curl_win32_init(long flags) { @@ -90,7 +95,8 @@ CURLcode Curl_win32_init(long flags) } #endif - s_hIpHlpApiDll = Curl_load_library(TEXT("iphlpapi.dll")); +#ifndef HAVE_IF_NAMETOINDEX + s_hIpHlpApiDll = curl_load_library(TEXT("iphlpapi.dll")); if(s_hIpHlpApiDll) { /* Get the address of the if_nametoindex function */ #ifdef UNDER_CE @@ -106,6 +112,7 @@ CURLcode Curl_win32_init(long flags) if(pIfNameToIndex) Curl_if_nametoindex = pIfNameToIndex; } +#endif /* curlx_verify_windows_version must be called during init at least once because it has its own initialization routine. */ @@ -123,11 +130,13 @@ CURLcode Curl_win32_init(long flags) /* Curl_win32_cleanup() is the opposite of Curl_win32_init() */ void Curl_win32_cleanup(long init_flags) { +#ifndef HAVE_IF_NAMETOINDEX if(s_hIpHlpApiDll) { FreeLibrary(s_hIpHlpApiDll); s_hIpHlpApiDll = NULL; Curl_if_nametoindex = NULL; } +#endif #ifdef USE_WINDOWS_SSPI Curl_sspi_global_cleanup(); @@ -140,11 +149,13 @@ void Curl_win32_cleanup(long init_flags) } } -#if !defined(LOAD_WITH_ALTERED_SEARCH_PATH) +#ifndef HAVE_IF_NAMETOINDEX + +#ifndef LOAD_WITH_ALTERED_SEARCH_PATH #define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008 #endif -#if !defined(LOAD_LIBRARY_SEARCH_SYSTEM32) +#ifndef LOAD_LIBRARY_SEARCH_SYSTEM32 #define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 #endif @@ -163,7 +174,7 @@ typedef HMODULE (APIENTRY *LOADLIBRARYEX_FN)(LPCTSTR, HANDLE, DWORD); #endif /* - * Curl_load_library() + * curl_load_library() * * This is used to dynamically load DLLs using the most secure method available * for the version of Windows that we are running on. @@ -176,7 +187,7 @@ typedef HMODULE (APIENTRY *LOADLIBRARYEX_FN)(LPCTSTR, HANDLE, DWORD); * * Returns the handle of the module on success; otherwise NULL. */ -HMODULE Curl_load_library(LPCTSTR filename) +static HMODULE curl_load_library(LPCTSTR filename) { #if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE) HMODULE hModule = NULL; @@ -228,7 +239,6 @@ HMODULE Curl_load_library(LPCTSTR filename) hModule = pLoadLibraryEx ? pLoadLibraryEx(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) : LoadLibrary(path); - } free(path); } @@ -240,5 +250,6 @@ HMODULE Curl_load_library(LPCTSTR filename) return NULL; #endif } +#endif /* !HAVE_IF_NAMETOINDEX */ #endif /* _WIN32 */ diff --git a/lib/system_win32.h b/lib/system_win32.h index b8333c647e..6bf89d3d9d 100644 --- a/lib/system_win32.h +++ b/lib/system_win32.h @@ -36,14 +36,13 @@ extern bool Curl_isVistaOrGreater; CURLcode Curl_win32_init(long flags); void Curl_win32_cleanup(long init_flags); +#ifndef HAVE_IF_NAMETOINDEX /* We use our own typedef here since some headers might lack this */ typedef unsigned int(WINAPI *IF_NAMETOINDEX_FN)(const char *); /* This is used instead of if_nametoindex if available on Windows */ extern IF_NAMETOINDEX_FN Curl_if_nametoindex; - -/* This is used to dynamically load DLLs */ -HMODULE Curl_load_library(LPCTSTR filename); +#endif #else /* _WIN32 */ #define Curl_win32_init(x) CURLE_OK #endif /* !_WIN32 */ diff --git a/lib/telnet.c b/lib/telnet.c index 737db36d72..bd599dece2 100644 --- a/lib/telnet.c +++ b/lib/telnet.c @@ -57,7 +57,6 @@ #include "system_win32.h" #include "arpa_telnet.h" #include "select.h" -#include "strcase.h" #include "curlx/warnless.h" #include "curlx/strparse.h" @@ -171,7 +170,7 @@ static void sendsuboption(struct Curl_easy *data, static CURLcode telnet_do(struct Curl_easy *data, bool *done); static CURLcode telnet_done(struct Curl_easy *data, - CURLcode, bool premature); + CURLcode, bool premature); static CURLcode send_telnet_data(struct Curl_easy *data, struct TELNET *tn, char *buffer, ssize_t nread); @@ -839,7 +838,7 @@ static CURLcode check_telnet_options(struct Curl_easy *data, switch(olen) { case 5: /* Terminal type */ - if(strncasecompare(option, "TTYPE", 5)) { + if(curl_strnequal(option, "TTYPE", 5)) { tn->subopt_ttype = arg; tn->us_preferred[CURL_TELOPT_TTYPE] = CURL_YES; break; @@ -849,7 +848,7 @@ static CURLcode check_telnet_options(struct Curl_easy *data, case 8: /* Display variable */ - if(strncasecompare(option, "XDISPLOC", 8)) { + if(curl_strnequal(option, "XDISPLOC", 8)) { tn->subopt_xdisploc = arg; tn->us_preferred[CURL_TELOPT_XDISPLOC] = CURL_YES; break; @@ -859,7 +858,7 @@ static CURLcode check_telnet_options(struct Curl_easy *data, case 7: /* Environment variable */ - if(strncasecompare(option, "NEW_ENV", 7)) { + if(curl_strnequal(option, "NEW_ENV", 7)) { beg = curl_slist_append(tn->telnet_vars, arg); if(!beg) { result = CURLE_OUT_OF_MEMORY; @@ -874,7 +873,7 @@ static CURLcode check_telnet_options(struct Curl_easy *data, case 2: /* Window Size */ - if(strncasecompare(option, "WS", 2)) { + if(curl_strnequal(option, "WS", 2)) { const char *p = arg; curl_off_t x = 0; curl_off_t y = 0; @@ -896,7 +895,7 @@ static CURLcode check_telnet_options(struct Curl_easy *data, case 6: /* To take care or not of the 8th bit in data exchange */ - if(strncasecompare(option, "BINARY", 6)) { + if(curl_strnequal(option, "BINARY", 6)) { int binary_option = atoi(arg); if(binary_option != 1) { tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO; @@ -1314,8 +1313,8 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) int poll_cnt; curl_off_t total_dl = 0; curl_off_t total_ul = 0; + ssize_t snread; #endif - ssize_t nread; struct curltime now; bool keepon = TRUE; char buffer[4*1024]; @@ -1464,6 +1463,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) } if(events.lNetworkEvents & FD_READ) { /* read data from network */ + size_t nread; result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread); /* read would have blocked. Loop again */ if(result == CURLE_AGAIN) @@ -1475,7 +1475,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) } /* returned zero but actually received 0 or less here, the server closed the connection and we bail out */ - else if(nread <= 0) { + else if(!nread) { keepon = FALSE; break; } @@ -1550,6 +1550,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) default: /* read! */ if(pfd[0].revents & POLLIN) { /* read data from network */ + size_t nread; result = Curl_xfer_recv(data, buffer, sizeof(buffer), &nread); /* read would have blocked. Loop again */ if(result == CURLE_AGAIN) @@ -1567,7 +1568,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) } /* returned zero but actually received 0 or less here, the server closed the connection and we bail out */ - else if(nread <= 0) { + else if(!nread) { keepon = FALSE; break; } @@ -1590,34 +1591,34 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done) } } - nread = 0; + snread = 0; if(poll_cnt == 2) { if(pfd[1].revents & POLLIN) { /* read from in file */ - nread = read(pfd[1].fd, buffer, sizeof(buffer)); + snread = read(pfd[1].fd, buffer, sizeof(buffer)); } } else { /* read from user-supplied method */ - nread = (int)data->state.fread_func(buffer, 1, sizeof(buffer), + snread = (int)data->state.fread_func(buffer, 1, sizeof(buffer), data->state.in); - if(nread == CURL_READFUNC_ABORT) { + if(snread == CURL_READFUNC_ABORT) { keepon = FALSE; break; } - if(nread == CURL_READFUNC_PAUSE) + if(snread == CURL_READFUNC_PAUSE) break; } - if(nread > 0) { - result = send_telnet_data(data, tn, buffer, nread); + if(snread > 0) { + result = send_telnet_data(data, tn, buffer, snread); if(result) { keepon = FALSE; break; } - total_ul += nread; + total_ul += snread; Curl_pgrsSetUploadCounter(data, total_ul); } - else if(nread < 0) + else if(snread < 0) keepon = FALSE; break; diff --git a/lib/tftp.c b/lib/tftp.c index e9f4e68ecc..5ea986dbb4 100644 --- a/lib/tftp.c +++ b/lib/tftp.c @@ -48,6 +48,7 @@ #include "urldata.h" #include +#include "cfilters.h" #include "cf-socket.h" #include "transfer.h" #include "sendf.h" @@ -429,6 +430,7 @@ static CURLcode tftp_send_first(struct tftp_conn *state, const char *mode = "octet"; char *filename; struct Curl_easy *data = state->data; + const struct Curl_sockaddr_ex *remote_addr = NULL; CURLcode result = CURLE_OK; /* Set ASCII mode if -B flag was used */ @@ -525,10 +527,14 @@ static CURLcode tftp_send_first(struct tftp_conn *state, #else #define CURL_SENDTO_ARG5(x) (x) #endif + remote_addr = Curl_conn_get_remote_addr(data, FIRSTSOCKET); + if(!remote_addr) + return CURLE_FAILED_INIT; + senddata = sendto(state->sockfd, (void *)state->spacket.data, (SEND_TYPE_ARG3)sbytes, 0, - CURL_SENDTO_ARG5(&data->conn->remote_addr->curl_sa_addr), - (curl_socklen_t)data->conn->remote_addr->addrlen); + CURL_SENDTO_ARG5(&remote_addr->curl_sa_addr), + (curl_socklen_t)remote_addr->addrlen); if(senddata != (ssize_t)sbytes) { char buffer[STRERROR_LEN]; failf(data, "%s", Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); @@ -955,6 +961,7 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) int blksize; int need_blksize; struct connectdata *conn = data->conn; + const struct Curl_sockaddr_ex *remote_addr = NULL; blksize = TFTP_BLKSIZE_DEFAULT; @@ -998,8 +1005,13 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) state->blksize = TFTP_BLKSIZE_DEFAULT; /* Unless updated by OACK response */ state->requested_blksize = blksize; + remote_addr = Curl_conn_get_remote_addr(data, FIRSTSOCKET); + DEBUGASSERT(remote_addr); + if(!remote_addr) + return CURLE_FAILED_INIT; + ((struct sockaddr *)&state->local_addr)->sa_family = - (CURL_SA_FAMILY_T)(conn->remote_addr->family); + (CURL_SA_FAMILY_T)(remote_addr->family); tftp_set_timeouts(state); @@ -1018,7 +1030,7 @@ static CURLcode tftp_connect(struct Curl_easy *data, bool *done) * IPv4 and IPv6... */ int rc = bind(state->sockfd, (struct sockaddr *)&state->local_addr, - (curl_socklen_t)conn->remote_addr->addrlen); + (curl_socklen_t)remote_addr->addrlen); if(rc) { char buffer[STRERROR_LEN]; failf(data, "bind() failed; %s", @@ -1357,7 +1369,7 @@ static CURLcode tftp_setup_connection(struct Curl_easy *data, { char *type; - conn->transport = TRNSPRT_UDP; + conn->transport_wanted = TRNSPRT_UDP; /* TFTP URLs support an extension like ";mode=" that * we will try to get now! */ diff --git a/lib/transfer.c b/lib/transfer.c index ef7d7f19af..15a61f6997 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -79,7 +79,6 @@ #include "connect.h" #include "http2.h" #include "mime.h" -#include "strcase.h" #include "hsts.h" #include "setopt.h" #include "headers.h" @@ -106,7 +105,7 @@ char *Curl_checkheaders(const struct Curl_easy *data, DEBUGASSERT(thisheader[thislen-1] != ':'); for(head = data->set.headers; head; head = head->next) { - if(strncasecompare(head->data, thisheader, thislen) && + if(curl_strnequal(head->data, thisheader, thislen) && Curl_headersep(head->data[thislen]) ) return head->data; } @@ -115,7 +114,7 @@ char *Curl_checkheaders(const struct Curl_easy *data, } #endif -static int data_pending(struct Curl_easy *data) +static int data_pending(struct Curl_easy *data, bool rcvd_eagain) { struct connectdata *conn = data->conn; @@ -124,8 +123,9 @@ static int data_pending(struct Curl_easy *data) /* in the case of libssh2, we can never be really sure that we have emptied its internal buffers so we MUST always try until we get EAGAIN back */ - return conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP) || - Curl_conn_data_pending(data, FIRSTSOCKET); + return (!rcvd_eagain && + conn->handler->protocol&(CURLPROTO_SCP|CURLPROTO_SFTP)) || + Curl_conn_data_pending(data, FIRSTSOCKET); } /* @@ -210,7 +210,7 @@ static ssize_t xfer_recv_resp(struct Curl_easy *data, bool eos_reliable, CURLcode *err) { - ssize_t nread; + size_t nread; DEBUGASSERT(blen > 0); /* If we are reading BODY data and the connection does NOT handle EOF @@ -251,8 +251,7 @@ static ssize_t xfer_recv_resp(struct Curl_easy *data, } DEBUGF(infof(data, "sendrecv_dl: we are done")); } - DEBUGASSERT(nread >= 0); - return nread; + return (ssize_t)nread; } /* @@ -271,6 +270,7 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, int maxloops = 10; curl_off_t total_received = 0; bool is_multiplex = FALSE; + bool rcvd_eagain = FALSE; result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen); if(result) @@ -303,23 +303,25 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, bytestoread = (size_t)data->set.max_recv_speed; } + rcvd_eagain = FALSE; nread = xfer_recv_resp(data, buf, bytestoread, is_multiplex, &result); if(nread < 0) { if(CURLE_AGAIN != result) goto out; /* real error */ + rcvd_eagain = TRUE; result = CURLE_OK; if(data->req.download_done && data->req.no_body && !data->req.resp_trailer) { DEBUGF(infof(data, "EAGAIN, download done, no trailer announced, " "not waiting for EOS")); nread = 0; - /* continue as if we read the EOS */ + /* continue as if we received the EOS */ } else break; /* get out of loop */ } - /* We only get a 0-length read on EndOfStream */ + /* We only get a 0-length receive at the end of the response */ blen = (size_t)nread; is_eos = (blen == 0); *didwhat |= KEEP_RECV; @@ -355,12 +357,12 @@ static CURLcode sendrecv_dl(struct Curl_easy *data, } while(maxloops--); - if((maxloops <= 0) || data_pending(data)) { - /* did not read until EAGAIN or there is still pending data, mark as - read-again-please */ - data->state.select_bits = CURL_CSELECT_IN; - if((k->keepon & KEEP_SENDBITS) == KEEP_SEND) - data->state.select_bits |= CURL_CSELECT_OUT; + if(!Curl_xfer_is_blocked(data) && + (!rcvd_eagain || data_pending(data, rcvd_eagain))) { + /* Did not read until EAGAIN or there is still data pending + * in buffers. Mark as read-again via simulated SELECT results. */ + Curl_multi_mark_dirty(data); + CURL_TRC_M(data, "sendrecv_dl() no EAGAIN/pending data, mark as dirty"); } if(((k->keepon & (KEEP_RECV|KEEP_SEND)) == KEEP_SEND) && @@ -396,24 +398,6 @@ static CURLcode sendrecv_ul(struct Curl_easy *data, int *didwhat) return CURLE_OK; } -static int select_bits_paused(struct Curl_easy *data, int select_bits) -{ - /* See issue #11982: we really need to be careful not to progress - * a transfer direction when that direction is paused. Not all parts - * of our state machine are handling PAUSED transfers correctly. So, we - * do not want to go there. - * NOTE: we are only interested in PAUSE, not HOLD. */ - - /* if there is data in a direction not paused, return false */ - if(((select_bits & CURL_CSELECT_IN) && - !(data->req.keepon & KEEP_RECV_PAUSE)) || - ((select_bits & CURL_CSELECT_OUT) && - !(data->req.keepon & KEEP_SEND_PAUSE))) - return FALSE; - - return (data->req.keepon & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)); -} - /* * Curl_sendrecv() is the low-level function to be called when data is to * be read and written to/from the connection. @@ -425,14 +409,9 @@ CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp) int didwhat = 0; DEBUGASSERT(nowp); - if(data->state.select_bits) { - if(select_bits_paused(data, data->state.select_bits)) { - /* leave the bits unchanged, so they'll tell us what to do when - * this transfer gets unpaused. */ - result = CURLE_OK; - goto out; - } - data->state.select_bits = 0; + if(Curl_xfer_is_blocked(data)) { + result = CURLE_OK; + goto out; } /* We go ahead and do a read if we have a readable socket or if the stream @@ -948,7 +927,7 @@ CURLcode Curl_xfer_send(struct Curl_easy *data, CURLcode Curl_xfer_recv(struct Curl_easy *data, char *buf, size_t blen, - ssize_t *pnrcvd) + size_t *pnrcvd) { int sockindex; @@ -974,9 +953,48 @@ bool Curl_xfer_is_blocked(struct Curl_easy *data) bool want_send = ((data)->req.keepon & KEEP_SEND); bool want_recv = ((data)->req.keepon & KEEP_RECV); if(!want_send) - return want_recv && Curl_cwriter_is_paused(data); + return want_recv && Curl_xfer_recv_is_paused(data); else if(!want_recv) - return want_send && Curl_creader_is_paused(data); + return want_send && Curl_xfer_send_is_paused(data); else - return Curl_creader_is_paused(data) && Curl_cwriter_is_paused(data); + return Curl_xfer_recv_is_paused(data) && Curl_xfer_send_is_paused(data); +} + +bool Curl_xfer_send_is_paused(struct Curl_easy *data) +{ + return (data->req.keepon & KEEP_SEND_PAUSE); +} + +bool Curl_xfer_recv_is_paused(struct Curl_easy *data) +{ + return (data->req.keepon & KEEP_RECV_PAUSE); +} + +CURLcode Curl_xfer_pause_send(struct Curl_easy *data, bool enable) +{ + CURLcode result = CURLE_OK; + if(enable) { + data->req.keepon |= KEEP_SEND_PAUSE; + } + else { + data->req.keepon &= ~KEEP_SEND_PAUSE; + if(Curl_creader_is_paused(data)) + result = Curl_creader_unpause(data); + } + return result; +} + +CURLcode Curl_xfer_pause_recv(struct Curl_easy *data, bool enable) +{ + CURLcode result = CURLE_OK; + if(enable) { + data->req.keepon |= KEEP_RECV_PAUSE; + } + else { + data->req.keepon &= ~KEEP_RECV_PAUSE; + if(Curl_cwriter_is_paused(data)) + result = Curl_cwriter_unpause(data); + } + Curl_conn_ev_data_pause(data, enable); + return result; } diff --git a/lib/transfer.h b/lib/transfer.h index 2c355e0e9d..92278226be 100644 --- a/lib/transfer.h +++ b/lib/transfer.h @@ -132,16 +132,23 @@ CURLcode Curl_xfer_send(struct Curl_easy *data, */ CURLcode Curl_xfer_recv(struct Curl_easy *data, char *buf, size_t blen, - ssize_t *pnrcvd); + size_t *pnrcvd); CURLcode Curl_xfer_send_close(struct Curl_easy *data); CURLcode Curl_xfer_send_shutdown(struct Curl_easy *data, bool *done); -/** - * Return TRUE iff the transfer is not done, but further progress +/* Return TRUE if the transfer is not done, but further progress * is blocked. For example when it is only receiving and its writer - * is PAUSED. - */ + * is PAUSED. */ bool Curl_xfer_is_blocked(struct Curl_easy *data); +/* Query if send/recv for transfer is paused. */ +bool Curl_xfer_send_is_paused(struct Curl_easy *data); +bool Curl_xfer_recv_is_paused(struct Curl_easy *data); + +/* Enable/Disable pausing of send/recv for the transfer. */ +CURLcode Curl_xfer_pause_send(struct Curl_easy *data, bool enable); +CURLcode Curl_xfer_pause_recv(struct Curl_easy *data, bool enable); + + #endif /* HEADER_CURL_TRANSFER_H */ diff --git a/lib/uint-bset.c b/lib/uint-bset.c index 2560b37363..e612c390a6 100644 --- a/lib/uint-bset.c +++ b/lib/uint-bset.c @@ -45,7 +45,8 @@ void Curl_uint_bset_init(struct uint_bset *bset) CURLcode Curl_uint_bset_resize(struct uint_bset *bset, unsigned int nmax) { - unsigned int nslots = (nmax + 63) / 64; + unsigned int nslots = (nmax < (UINT_MAX-63)) ? + ((nmax + 63) / 64) : (UINT_MAX / 64); DEBUGASSERT(bset->init == CURL_UINT_BSET_MAGIC); if(nslots != bset->nslots) { @@ -60,6 +61,7 @@ CURLcode Curl_uint_bset_resize(struct uint_bset *bset, unsigned int nmax) } bset->slots = slots; bset->nslots = nslots; + bset->first_slot_used = 0; } return CURLE_OK; } @@ -72,14 +74,13 @@ void Curl_uint_bset_destroy(struct uint_bset *bset) memset(bset, 0, sizeof(*bset)); } - -unsigned int Curl_uint_bset_capacity(struct uint_bset *bset) +#ifdef UNITTESTS +UNITTEST unsigned int Curl_uint_bset_capacity(struct uint_bset *bset) { return bset->nslots * 64; } - -unsigned int Curl_uint_bset_count(struct uint_bset *bset) +UNITTEST unsigned int Curl_uint_bset_count(struct uint_bset *bset) { unsigned int i; unsigned int n = 0; @@ -89,12 +90,12 @@ unsigned int Curl_uint_bset_count(struct uint_bset *bset) } return n; } - +#endif bool Curl_uint_bset_empty(struct uint_bset *bset) { unsigned int i; - for(i = 0; i < bset->nslots; ++i) { + for(i = bset->first_slot_used; i < bset->nslots; ++i) { if(bset->slots[i]) return FALSE; } @@ -104,8 +105,10 @@ bool Curl_uint_bset_empty(struct uint_bset *bset) void Curl_uint_bset_clear(struct uint_bset *bset) { - if(bset->nslots) + if(bset->nslots) { memset(bset->slots, 0, bset->nslots * sizeof(curl_uint64_t)); + bset->first_slot_used = UINT_MAX; + } } @@ -115,6 +118,8 @@ bool Curl_uint_bset_add(struct uint_bset *bset, unsigned int i) if(islot >= bset->nslots) return FALSE; bset->slots[islot] |= ((curl_uint64_t)1 << (i % 64)); + if(islot < bset->first_slot_used) + bset->first_slot_used = islot; return TRUE; } @@ -139,13 +144,14 @@ bool Curl_uint_bset_contains(struct uint_bset *bset, unsigned int i) bool Curl_uint_bset_first(struct uint_bset *bset, unsigned int *pfirst) { unsigned int i; - for(i = 0; i < bset->nslots; ++i) { + for(i = bset->first_slot_used; i < bset->nslots; ++i) { if(bset->slots[i]) { *pfirst = (i * 64) + CURL_CTZ64(bset->slots[i]); + bset->first_slot_used = i; return TRUE; } } - *pfirst = UINT_MAX; /* a value we cannot store */ + bset->first_slot_used = *pfirst = UINT_MAX; return FALSE; } @@ -183,11 +189,11 @@ unsigned int Curl_popcount64(curl_uint64_t x) /* Compute the "Hamming Distance" between 'x' and 0, * which is the number of set bits in 'x'. * See: https://en.wikipedia.org/wiki/Hamming_weight */ - const curl_uint64_t m1 = CURL_OFF_TU_C(0x5555555555555555); /* 0101+ */ - const curl_uint64_t m2 = CURL_OFF_TU_C(0x3333333333333333); /* 00110011+ */ - const curl_uint64_t m4 = CURL_OFF_TU_C(0x0f0f0f0f0f0f0f0f); /* 00001111+ */ + const curl_uint64_t m1 = 0x5555555555555555LL; /* 0101+ */ + const curl_uint64_t m2 = 0x3333333333333333LL; /* 00110011+ */ + const curl_uint64_t m4 = 0x0f0f0f0f0f0f0f0fLL; /* 00001111+ */ /* 1 + 256^1 + 256^2 + 256^3 + ... + 256^7 */ - const curl_uint64_t h01 = CURL_OFF_TU_C(0x0101010101010101); + const curl_uint64_t h01 = 0x0101010101010101LL; x -= (x >> 1) & m1; /* replace every 2 bits with bits present */ x = (x & m2) + ((x >> 2) & m2); /* replace every nibble with bits present */ x = (x + (x >> 4)) & m4; /* replace every byte with bits present */ @@ -203,11 +209,11 @@ unsigned int Curl_ctz64(curl_uint64_t x) { /* count trailing zeros in a curl_uint64_t. * divide and conquer to find the number of lower 0 bits */ - const curl_uint64_t ml32 = CURL_OFF_TU_C(0xFFFFFFFF); /* lower 32 bits */ - const curl_uint64_t ml16 = CURL_OFF_TU_C(0x0000FFFF); /* lower 16 bits */ - const curl_uint64_t ml8 = CURL_OFF_TU_C(0x000000FF); /* lower 8 bits */ - const curl_uint64_t ml4 = CURL_OFF_TU_C(0x0000000F); /* lower 4 bits */ - const curl_uint64_t ml2 = CURL_OFF_TU_C(0x00000003); /* lower 2 bits */ + const curl_uint64_t ml32 = 0xFFFFFFFF; /* lower 32 bits */ + const curl_uint64_t ml16 = 0x0000FFFF; /* lower 16 bits */ + const curl_uint64_t ml8 = 0x000000FF; /* lower 8 bits */ + const curl_uint64_t ml4 = 0x0000000F; /* lower 4 bits */ + const curl_uint64_t ml2 = 0x00000003; /* lower 2 bits */ unsigned int n; if(!x) diff --git a/lib/uint-bset.h b/lib/uint-bset.h index d998dccdfe..cc405cc656 100644 --- a/lib/uint-bset.h +++ b/lib/uint-bset.h @@ -42,6 +42,7 @@ struct uint_bset { curl_uint64_t *slots; unsigned int nslots; + unsigned int first_slot_used; #ifdef DEBUGBUILD int init; #endif diff --git a/lib/uint-hash.c b/lib/uint-hash.c index afeb684d0c..6c66507887 100644 --- a/lib/uint-hash.c +++ b/lib/uint-hash.c @@ -107,8 +107,8 @@ static void uint_hash_entry_unlink(struct uint_hash *h, } static void uint_hash_elem_link(struct uint_hash *h, - struct uint_hash_entry **he_anchor, - struct uint_hash_entry *he) + struct uint_hash_entry **he_anchor, + struct uint_hash_entry *he) { he->next = *he_anchor; *he_anchor = he; @@ -206,10 +206,12 @@ static void uint_hash_clear(struct uint_hash *h) } } -void Curl_uint_hash_clear(struct uint_hash *h) +#ifdef UNITTESTS +UNITTEST void Curl_uint_hash_clear(struct uint_hash *h) { uint_hash_clear(h); } +#endif void Curl_uint_hash_destroy(struct uint_hash *h) { diff --git a/lib/uint-spbset.c b/lib/uint-spbset.c index 578b9bd07c..d12cdcb4aa 100644 --- a/lib/uint-spbset.c +++ b/lib/uint-spbset.c @@ -35,6 +35,9 @@ #define CURL_UINT_SPBSET_MAGIC 0x70737362 #endif +/* Clear the bitset, making it empty. */ +UNITTEST void Curl_uint_spbset_clear(struct uint_spbset *bset); + void Curl_uint_spbset_init(struct uint_spbset *bset) { memset(bset, 0, sizeof(*bset)); @@ -77,7 +80,7 @@ bool Curl_uint_spbset_empty(struct uint_spbset *bset) return TRUE; } -void Curl_uint_spbset_clear(struct uint_spbset *bset) +UNITTEST void Curl_uint_spbset_clear(struct uint_spbset *bset) { struct uint_spbset_chunk *next, *chunk; diff --git a/lib/uint-spbset.h b/lib/uint-spbset.h index 571d56753c..bd2347902c 100644 --- a/lib/uint-spbset.h +++ b/lib/uint-spbset.h @@ -64,9 +64,6 @@ unsigned int Curl_uint_spbset_count(struct uint_spbset *bset); /* TRUE of bitset is empty */ bool Curl_uint_spbset_empty(struct uint_spbset *bset); -/* Clear the bitset, making it empty. */ -void Curl_uint_spbset_clear(struct uint_spbset *bset); - /* Add the number `i` to the bitset. * Numbers can be added more than once, without making a difference. * Returns FALSE if allocations failed. */ diff --git a/lib/uint-table.c b/lib/uint-table.c index d8de1b128a..21bcb6e1cf 100644 --- a/lib/uint-table.c +++ b/lib/uint-table.c @@ -34,6 +34,9 @@ #define CURL_UINT_TBL_MAGIC 0x62757473 #endif +/* Clear the table, making it empty. */ +UNITTEST void Curl_uint_tbl_clear(struct uint_tbl *tbl); + void Curl_uint_tbl_init(struct uint_tbl *tbl, Curl_uint_tbl_entry_dtor *entry_dtor) { @@ -68,7 +71,7 @@ CURLcode Curl_uint_tbl_resize(struct uint_tbl *tbl, unsigned int nrows) { /* we use `tbl->nrows + 1` during iteration, want that to work */ DEBUGASSERT(tbl->init == CURL_UINT_TBL_MAGIC); - if(!nrows || (nrows == UINT_MAX)) + if(!nrows) return CURLE_BAD_FUNCTION_ARGUMENT; if(nrows != tbl->nrows) { void **rows = calloc(nrows, sizeof(void *)); @@ -95,8 +98,7 @@ void Curl_uint_tbl_destroy(struct uint_tbl *tbl) memset(tbl, 0, sizeof(*tbl)); } - -void Curl_uint_tbl_clear(struct uint_tbl *tbl) +UNITTEST void Curl_uint_tbl_clear(struct uint_tbl *tbl) { DEBUGASSERT(tbl->init == CURL_UINT_TBL_MAGIC); uint_tbl_clear_rows(tbl, 0, tbl->nrows); @@ -130,7 +132,7 @@ bool Curl_uint_tbl_add(struct uint_tbl *tbl, void *entry, unsigned int *pkey) DEBUGASSERT(tbl->init == CURL_UINT_TBL_MAGIC); if(!entry || !pkey) return FALSE; - *pkey = UINT_MAX; /* always invalid */ + *pkey = UINT_MAX; if(tbl->nentries == tbl->nrows) /* full */ return FALSE; diff --git a/lib/uint-table.h b/lib/uint-table.h index 2c05b1de1a..c74ec7ad63 100644 --- a/lib/uint-table.h +++ b/lib/uint-table.h @@ -60,9 +60,6 @@ unsigned int Curl_uint_tbl_capacity(struct uint_tbl *tbl); /* Get the number of entries in the table. */ unsigned int Curl_uint_tbl_count(struct uint_tbl *tbl); -/* Clear the table, making it empty. */ -void Curl_uint_tbl_clear(struct uint_tbl *tbl); - /* Get the entry for key or NULL if not present */ void *Curl_uint_tbl_get(struct uint_tbl *tbl, unsigned int key); diff --git a/lib/url.c b/lib/url.c index 10e37ec67f..e4f250f54a 100644 --- a/lib/url.c +++ b/lib/url.c @@ -59,6 +59,10 @@ #error "We cannot compile without socket() support!" #endif +#if defined(HAVE_IF_NAMETOINDEX) && defined(_WIN32) +#include +#endif + #include #include "doh.h" @@ -107,7 +111,6 @@ #include "imap.h" #include "url.h" #include "connect.h" -#include "inet_ntop.h" #include "http_ntlm.h" #include "curl_rtmp.h" #include "gopher.h" @@ -231,22 +234,24 @@ CURLcode Curl_close(struct Curl_easy **datap) data = *datap; *datap = NULL; - /* Detach connection if any is left. This should not be normal, but can be - the case for example with CONNECT_ONLY + recv/send (test 556) */ - Curl_detach_connection(data); - if(!data->state.internal) { - if(data->multi) - /* This handle is still part of a multi handle, take care of this first - and detach this handle from there. */ - curl_multi_remove_handle(data->multi, data); - - if(data->multi_easy) { + if(!data->state.internal && data->multi) { + /* This handle is still part of a multi handle, take care of this first + and detach this handle from there. + This detaches the connection. */ + curl_multi_remove_handle(data->multi, data); + } + else { + /* Detach connection if any is left. This should not be normal, but can be + the case for example with CONNECT_ONLY + recv/send (test 556) */ + Curl_detach_connection(data); + if(!data->state.internal && data->multi_easy) { /* when curl_easy_perform() is used, it creates its own multi handle to use and this is the one */ curl_multi_cleanup(data->multi_easy); data->multi_easy = NULL; } } + DEBUGASSERT(!data->conn || data->state.internal); Curl_expire_clear(data); /* shut off any timers left */ @@ -422,13 +427,11 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) /* Set the default CA cert bundle/path detected/specified at build time. * - * If Schannel or Secure Transport is the selected SSL backend then these - * locations are ignored. We allow setting CA location for Schannel and - * Secure Transport when explicitly specified by the user via - * CURLOPT_CAINFO / --cacert. + * If Schannel is the selected SSL backend then these locations are ignored. + * We allow setting CA location for Schannel when explicitly specified by + * the user via CURLOPT_CAINFO / --cacert. */ - if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL && - Curl_ssl_backend() != CURLSSLBACKEND_SECURETRANSPORT) { + if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL) { #ifdef CURL_CA_BUNDLE result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE); if(result) @@ -472,8 +475,8 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->happy_eyeballs_timeout = CURL_HET_DEFAULT; set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT; set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */ - set->maxage_conn = 118; - set->maxlifetime_conn = 0; + set->conn_max_idle_ms = 118 * 1000; + set->conn_max_age_ms = 0; set->http09_allowed = FALSE; set->httpwant = CURL_HTTP_VERSION_NONE ; @@ -640,7 +643,7 @@ proxy_info_matches(const struct proxy_info *data, { if((data->proxytype == needle->proxytype) && (data->port == needle->port) && - strcasecompare(data->host.name, needle->host.name)) + curl_strequal(data->host.name, needle->host.name)) return TRUE; return FALSE; @@ -670,36 +673,36 @@ socks_proxy_info_matches(const struct proxy_info *data, #define socks_proxy_info_matches(x,y) FALSE #endif -/* A connection has to have been idle for a shorter time than 'maxage_conn' +/* A connection has to have been idle for less than 'conn_max_idle_ms' (the success rate is just too low after this), or created less than - 'maxlifetime_conn' ago, to be subject for reuse. */ - + 'conn_max_age_ms' ago, to be subject for reuse. */ static bool conn_maxage(struct Curl_easy *data, struct connectdata *conn, struct curltime now) { - timediff_t idletime, lifetime; + timediff_t age_ms; - idletime = curlx_timediff(now, conn->lastused); - idletime /= 1000; /* integer seconds is fine */ - - if(idletime > data->set.maxage_conn) { - infof(data, "Too old connection (%" FMT_TIMEDIFF_T - " seconds idle), disconnect it", idletime); - return TRUE; + if(data->set.conn_max_idle_ms) { + age_ms = curlx_timediff(now, conn->lastused); + if(age_ms > data->set.conn_max_idle_ms) { + infof(data, "Too old connection (%" FMT_TIMEDIFF_T + " ms idle, max idle is %" FMT_TIMEDIFF_T " ms), disconnect it", + age_ms, data->set.conn_max_idle_ms); + return TRUE; + } } - lifetime = curlx_timediff(now, conn->created); - lifetime /= 1000; /* integer seconds is fine */ - - if(data->set.maxlifetime_conn && lifetime > data->set.maxlifetime_conn) { - infof(data, - "Too old connection (%" FMT_TIMEDIFF_T - " seconds since creation), disconnect it", lifetime); - return TRUE; + if(data->set.conn_max_age_ms) { + age_ms = curlx_timediff(now, conn->created); + if(age_ms > data->set.conn_max_age_ms) { + infof(data, + "Too old connection (created %" FMT_TIMEDIFF_T + " ms ago, max lifetime is %" FMT_TIMEDIFF_T " ms), disconnect it", + age_ms, data->set.conn_max_age_ms); + return TRUE; + } } - return FALSE; } @@ -1125,7 +1128,7 @@ static bool url_match_destination(struct connectdata *conn, || !m->needle->bits.httpproxy || m->needle->bits.tunnel_proxy #endif ) { - if(!strcasecompare(m->needle->handler->scheme, conn->handler->scheme)) { + if(!curl_strequal(m->needle->handler->scheme, conn->handler->scheme)) { /* `needle` and `conn` do not have the same scheme... */ if(get_protocol_family(conn->handler) != m->needle->handler->protocol) { /* and `conn`s protocol family is not the protocol `needle` wants. @@ -1142,14 +1145,14 @@ static bool url_match_destination(struct connectdata *conn, } /* If needle has "conn_to_*" set, conn must match this */ - if((m->needle->bits.conn_to_host && !strcasecompare( + if((m->needle->bits.conn_to_host && !curl_strequal( m->needle->conn_to_host.name, conn->conn_to_host.name)) || (m->needle->bits.conn_to_port && m->needle->conn_to_port != conn->conn_to_port)) return FALSE; /* hostname and port must match */ - if(!strcasecompare(m->needle->host.name, conn->host.name) || + if(!curl_strequal(m->needle->host.name, conn->host.name) || m->needle->remote_port != conn->remote_port) return FALSE; } @@ -1461,7 +1464,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) #endif conn->ip_version = data->set.ipver; conn->connect_only = data->set.connect_only; - conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */ + conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */ /* Initialize the attached xfers bitset */ Curl_uint_spbset_init(&conn->xfers_attached); @@ -1711,7 +1714,7 @@ const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme, } h = protocols[c % 67]; - if(h && strncasecompare(scheme, h->scheme, len) && !h->scheme[len]) + if(h && curl_strnequal(scheme, h->scheme, len) && !h->scheme[len]) return h; } return NULL; @@ -1795,10 +1798,10 @@ static void zonefrom_url(CURLU *uh, struct Curl_easy *data, #if defined(HAVE_IF_NAMETOINDEX) || defined(_WIN32) /* Zone identifier is not numeric */ unsigned int scopeidx = 0; -#ifdef _WIN32 - scopeidx = Curl_if_nametoindex(zoneid); -#else +#ifdef HAVE_IF_NAMETOINDEX scopeidx = if_nametoindex(zoneid); +#else + scopeidx = Curl_if_nametoindex(zoneid); #endif if(!scopeidx) { #ifndef CURL_DISABLE_VERBOSE_STRINGS @@ -1885,7 +1888,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, uc = curl_url_get(uh, CURLUPART_HOST, &data->state.up.hostname, 0); if(uc) { - if(!strcasecompare("file", data->state.up.scheme)) + if(!curl_strequal("file", data->state.up.scheme)) return CURLE_OUT_OF_MEMORY; } else if(strlen(data->state.up.hostname) > MAX_URL_LEN) { @@ -1922,7 +1925,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, #ifndef CURL_DISABLE_HSTS /* HSTS upgrade */ - if(data->hsts && strcasecompare("http", data->state.up.scheme)) { + if(data->hsts && curl_strequal("http", data->state.up.scheme)) { /* This MUST use the IDN decoded name */ if(Curl_hsts(data->hsts, conn->host.name, strlen(conn->host.name), TRUE)) { char *url; @@ -2016,7 +2019,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, uc = curl_url_get(uh, CURLUPART_PORT, &data->state.up.port, CURLU_DEFAULT_PORT); if(uc) { - if(!strcasecompare("file", data->state.up.scheme)) + if(!curl_strequal("file", data->state.up.scheme)) return CURLE_OUT_OF_MEMORY; } else { @@ -2195,7 +2198,7 @@ static char *detect_proxy(struct Curl_easy *data, * This can cause 'internal' http/ftp requests to be * arbitrarily redirected by any external attacker. */ - if(!proxy && !strcasecompare("http_proxy", proxy_env)) { + if(!proxy && !curl_strequal("http_proxy", proxy_env)) { /* There was no lowercase variable, try the uppercase version: */ Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env)); proxy = curl_getenv(proxy_env); @@ -2204,10 +2207,10 @@ static char *detect_proxy(struct Curl_easy *data, if(!proxy) { #ifndef CURL_DISABLE_WEBSOCKETS /* websocket proxy fallbacks */ - if(strcasecompare("ws_proxy", proxy_env)) { + if(curl_strequal("ws_proxy", proxy_env)) { proxy = curl_getenv("http_proxy"); } - else if(strcasecompare("wss_proxy", proxy_env)) { + else if(curl_strequal("wss_proxy", proxy_env)) { proxy = curl_getenv("https_proxy"); if(!proxy) proxy = curl_getenv("HTTPS_PROXY"); @@ -2274,22 +2277,22 @@ static CURLcode parse_proxy(struct Curl_easy *data, goto error; } - if(strcasecompare("https", scheme)) { + if(curl_strequal("https", scheme)) { if(proxytype != CURLPROXY_HTTPS2) proxytype = CURLPROXY_HTTPS; else proxytype = CURLPROXY_HTTPS2; } - else if(strcasecompare("socks5h", scheme)) + else if(curl_strequal("socks5h", scheme)) proxytype = CURLPROXY_SOCKS5_HOSTNAME; - else if(strcasecompare("socks5", scheme)) + else if(curl_strequal("socks5", scheme)) proxytype = CURLPROXY_SOCKS5; - else if(strcasecompare("socks4a", scheme)) + else if(curl_strequal("socks4a", scheme)) proxytype = CURLPROXY_SOCKS4A; - else if(strcasecompare("socks4", scheme) || - strcasecompare("socks", scheme)) + else if(curl_strequal("socks4", scheme) || + curl_strequal("socks", scheme)) proxytype = CURLPROXY_SOCKS4; - else if(strcasecompare("http", scheme)) + else if(curl_strequal("http", scheme)) ; /* leave it as HTTP or HTTP/1.0 */ else { /* Any other xxx:// reject! */ @@ -2390,7 +2393,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, goto error; } #ifdef USE_UNIX_SOCKETS - if(sockstype && strcasecompare(UNIX_SOCKET_PREFIX, host)) { + if(sockstype && curl_strequal(UNIX_SOCKET_PREFIX, host)) { uc = curl_url_get(uhp, CURLUPART_PATH, &path, CURLU_URLDECODE); if(uc) { result = CURLE_OUT_OF_MEMORY; @@ -2757,16 +2760,20 @@ static CURLcode parse_remote_port(struct Curl_easy *data, return CURLE_OK; } +#ifndef CURL_DISABLE_NETRC static bool str_has_ctrl(const char *input) { - const unsigned char *str = (const unsigned char *)input; - while(*str) { - if(*str < 0x20) - return TRUE; - str++; + if(input) { + const unsigned char *str = (const unsigned char *)input; + while(*str) { + if(*str < 0x20) + return TRUE; + str++; + } } return FALSE; } +#endif /* * Override the login details from the URL with that in the CURLOPT_USERPWD @@ -3062,8 +3069,8 @@ static CURLcode parse_connect_to_string(struct Curl_easy *data, if(!hostname_to_match) return CURLE_OUT_OF_MEMORY; hostname_to_match_len = strlen(hostname_to_match); - host_match = strncasecompare(ptr, hostname_to_match, - hostname_to_match_len); + host_match = curl_strnequal(ptr, hostname_to_match, + hostname_to_match_len); free(hostname_to_match); ptr += hostname_to_match_len; @@ -3228,7 +3235,7 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, neg->wanted = neg->allowed = CURL_HTTP_V2x; break; case ALPN_h3: - conn->transport = TRNSPRT_QUIC; + conn->transport_wanted = TRNSPRT_QUIC; neg->wanted = neg->allowed = CURL_HTTP_V3x; break; default: /* should not be possible */ @@ -3305,7 +3312,7 @@ static CURLcode resolve_server(struct Curl_easy *data, if(unix_path) { /* This only works if previous transport is TRNSPRT_TCP. Check it? */ - conn->transport = TRNSPRT_UNIX; + conn->transport_wanted = TRNSPRT_UNIX; return resolve_unix(data, conn, unix_path, pdns); } } @@ -3596,7 +3603,7 @@ static CURLcode create_conn(struct Curl_easy *data, * Do this after the hostnames have been IDN-converted. *************************************************************/ if(conn->bits.conn_to_host && - strcasecompare(conn->conn_to_host.name, conn->host.name)) { + curl_strequal(conn->conn_to_host.name, conn->host.name)) { conn->bits.conn_to_host = FALSE; } @@ -4094,3 +4101,17 @@ void *Curl_conn_meta_get(struct connectdata *conn, const char *key) { return Curl_hash_pick(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1); } + +CURLcode Curl_1st_err(CURLcode r1, CURLcode r2) +{ + return r1 ? r1 : r2; +} + +CURLcode Curl_1st_fatal(CURLcode r1, CURLcode r2) +{ + if(r1 && (r1 != CURLE_AGAIN)) + return r1; + if(r2 && (r2 != CURLE_AGAIN)) + return r2; + return r1; +} diff --git a/lib/url.h b/lib/url.h index 7aba98dbb9..bd4060c884 100644 --- a/lib/url.h +++ b/lib/url.h @@ -101,6 +101,19 @@ CURLcode Curl_conn_upkeep(struct Curl_easy *data, struct connectdata *conn, struct curltime *now); +/** + * Always eval all arguments, return the first result != CURLE_OK. + * A non-short-circuit evaluation. + */ +CURLcode Curl_1st_err(CURLcode r1, CURLcode r2); + +/** + * Always eval all arguments, return the first + * result != (CURLE_OK|CURLE_AGAIN) or `r1`. + * A non-short-circuit evaluation. + */ +CURLcode Curl_1st_fatal(CURLcode r1, CURLcode r2); + #if defined(USE_HTTP2) || defined(USE_HTTP3) void Curl_data_priority_clear_state(struct Curl_easy *data); #else diff --git a/lib/urlapi.c b/lib/urlapi.c index 6a7ab1a575..c266fd3ebc 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -31,7 +31,7 @@ #include "escape.h" #include "curl_ctype.h" #include "curlx/inet_pton.h" -#include "inet_ntop.h" +#include "curlx/inet_ntop.h" #include "strdup.h" #include "idn.h" #include "curlx/strparse.h" @@ -164,7 +164,7 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url, } else if((*iptr < ' ') || (*iptr >= 0x7f)) { unsigned char out[3]={'%'}; - Curl_hexbyte(&out[1], *iptr, TRUE); + Curl_hexbyte(&out[1], *iptr); result = curlx_dyn_addn(o, out, 3); } else { @@ -513,7 +513,7 @@ static CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname, hostname[hlen] = 0; /* end the address there */ if(1 != curlx_inet_pton(AF_INET6, hostname, dest)) return CURLUE_BAD_IPV6; - if(Curl_inet_ntop(AF_INET6, dest, hostname, hlen)) { + if(curlx_inet_ntop(AF_INET6, dest, hostname, hlen)) { hlen = strlen(hostname); /* might be shorter now */ hostname[hlen + 1] = 0; } @@ -1320,18 +1320,221 @@ fail: return NULL; } +#ifndef USE_IDN +#define host_decode(x,y) CURLUE_LACKS_IDN +#define host_encode(x,y) CURLUE_LACKS_IDN +#else +static CURLUcode host_decode(const char *host, char **allochost) +{ + CURLcode result = Curl_idn_decode(host, allochost); + if(result) + return (result == CURLE_OUT_OF_MEMORY) ? + CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; + return CURLUE_OK; +} + +static CURLUcode host_encode(const char *host, char **allochost) +{ + CURLcode result = Curl_idn_encode(host, allochost); + if(result) + return (result == CURLE_OUT_OF_MEMORY) ? + CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; + return CURLUE_OK; +} +#endif + +static CURLUcode urlget_format(const CURLU *u, CURLUPart what, + const char *ptr, char **part, + bool plusdecode, unsigned int flags) +{ + size_t partlen = strlen(ptr); + bool urldecode = (flags & CURLU_URLDECODE) ? 1 : 0; + bool urlencode = (flags & CURLU_URLENCODE) ? 1 : 0; + bool punycode = (flags & CURLU_PUNYCODE) && (what == CURLUPART_HOST); + bool depunyfy = (flags & CURLU_PUNY2IDN) && (what == CURLUPART_HOST); + *part = Curl_memdup0(ptr, partlen); + if(!*part) + return CURLUE_OUT_OF_MEMORY; + if(plusdecode) { + /* convert + to space */ + char *plus = *part; + size_t i = 0; + for(i = 0; i < partlen; ++plus, i++) { + if(*plus == '+') + *plus = ' '; + } + } + if(urldecode) { + char *decoded; + size_t dlen; + /* this unconditional rejection of control bytes is documented + API behavior */ + CURLcode res = Curl_urldecode(*part, 0, &decoded, &dlen, REJECT_CTRL); + free(*part); + if(res) { + *part = NULL; + return CURLUE_URLDECODE; + } + *part = decoded; + partlen = dlen; + } + if(urlencode) { + struct dynbuf enc; + CURLUcode uc; + curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); + uc = urlencode_str(&enc, *part, partlen, TRUE, what == CURLUPART_QUERY); + if(uc) + return uc; + free(*part); + *part = curlx_dyn_ptr(&enc); + } + else if(punycode) { + if(!Curl_is_ASCII_name(u->host)) { + char *allochost = NULL; + CURLUcode ret = host_decode(*part, &allochost); + if(ret) + return ret; + free(*part); + *part = allochost; + } + } + else if(depunyfy) { + if(Curl_is_ASCII_name(u->host)) { + char *allochost = NULL; + CURLUcode ret = host_encode(*part, &allochost); + if(ret) + return ret; + free(*part); + *part = allochost; + } + } + + return CURLUE_OK; +} + +static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) +{ + char *url; + const char *scheme; + char *options = u->options; + char *port = u->port; + char *allochost = NULL; + bool show_fragment = + u->fragment || (u->fragment_present && flags & CURLU_GET_EMPTY); + bool show_query = (u->query && u->query[0]) || + (u->query_present && flags & CURLU_GET_EMPTY); + bool punycode = (flags & CURLU_PUNYCODE) ? 1 : 0; + bool depunyfy = (flags & CURLU_PUNY2IDN) ? 1 : 0; + bool urlencode = (flags & CURLU_URLENCODE) ? 1 : 0; + char portbuf[7]; + if(u->scheme && curl_strequal("file", u->scheme)) { + url = aprintf("file://%s%s%s%s%s", + u->path, + show_query ? "?": "", + u->query ? u->query : "", + show_fragment ? "#": "", + u->fragment ? u->fragment : ""); + } + else if(!u->host) + return CURLUE_NO_HOST; + else { + const struct Curl_handler *h = NULL; + char schemebuf[MAX_SCHEME_LEN + 5]; + if(u->scheme) + scheme = u->scheme; + else if(flags & CURLU_DEFAULT_SCHEME) + scheme = DEFAULT_SCHEME; + else + return CURLUE_NO_SCHEME; + + h = Curl_get_scheme_handler(scheme); + if(!port && (flags & CURLU_DEFAULT_PORT)) { + /* there is no stored port number, but asked to deliver + a default one for the scheme */ + if(h) { + msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); + port = portbuf; + } + } + else if(port) { + /* there is a stored port number, but asked to inhibit if it matches + the default one for the scheme */ + if(h && (h->defport == u->portnum) && + (flags & CURLU_NO_DEFAULT_PORT)) + port = NULL; + } + + if(h && !(h->flags & PROTOPT_URLOPTIONS)) + options = NULL; + + if(u->host[0] == '[') { + if(u->zoneid) { + /* make it '[ host %25 zoneid ]' */ + struct dynbuf enc; + size_t hostlen = strlen(u->host); + curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); + if(curlx_dyn_addf(&enc, "%.*s%%25%s]", (int)hostlen - 1, u->host, + u->zoneid)) + return CURLUE_OUT_OF_MEMORY; + allochost = curlx_dyn_ptr(&enc); + } + } + else if(urlencode) { + allochost = curl_easy_escape(NULL, u->host, 0); + if(!allochost) + return CURLUE_OUT_OF_MEMORY; + } + else if(punycode) { + if(!Curl_is_ASCII_name(u->host)) { + CURLUcode ret = host_decode(u->host, &allochost); + if(ret) + return ret; + } + } + else if(depunyfy) { + if(Curl_is_ASCII_name(u->host)) { + CURLUcode ret = host_encode(u->host, &allochost); + if(ret) + return ret; + } + } + + if(!(flags & CURLU_NO_GUESS_SCHEME) || !u->guessed_scheme) + msnprintf(schemebuf, sizeof(schemebuf), "%s://", scheme); + else + schemebuf[0] = 0; + + url = aprintf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", + schemebuf, + u->user ? u->user : "", + u->password ? ":": "", + u->password ? u->password : "", + options ? ";" : "", + options ? options : "", + (u->user || u->password || options) ? "@": "", + allochost ? allochost : u->host, + port ? ":": "", + port ? port : "", + u->path ? u->path : "/", + show_query ? "?": "", + u->query ? u->query : "", + show_fragment ? "#": "", + u->fragment ? u->fragment : ""); + free(allochost); + } + if(!url) + return CURLUE_OUT_OF_MEMORY; + *part = url; + return CURLUE_OK; +} + CURLUcode curl_url_get(const CURLU *u, CURLUPart what, char **part, unsigned int flags) { const char *ptr; CURLUcode ifmissing = CURLUE_UNKNOWN_PART; char portbuf[7]; - bool urldecode = (flags & CURLU_URLDECODE) ? 1 : 0; - bool urlencode = (flags & CURLU_URLENCODE) ? 1 : 0; - bool punycode = FALSE; - bool depunyfy = FALSE; bool plusdecode = FALSE; - (void)flags; if(!u) return CURLUE_BAD_HANDLE; if(!part) @@ -1342,7 +1545,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, case CURLUPART_SCHEME: ptr = u->scheme; ifmissing = CURLUE_NO_SCHEME; - urldecode = FALSE; /* never for schemes */ + flags &= ~CURLU_URLDECODE; /* never for schemes */ if((flags & CURLU_NO_GUESS_SCHEME) && u->guessed_scheme) return CURLUE_NO_SCHEME; break; @@ -1361,8 +1564,6 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, case CURLUPART_HOST: ptr = u->host; ifmissing = CURLUE_NO_HOST; - punycode = (flags & CURLU_PUNYCODE) ? 1 : 0; - depunyfy = (flags & CURLU_PUNY2IDN) ? 1 : 0; break; case CURLUPART_ZONEID: ptr = u->zoneid; @@ -1371,7 +1572,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, case CURLUPART_PORT: ptr = u->port; ifmissing = CURLUE_NO_PORT; - urldecode = FALSE; /* never for port */ + flags &= ~CURLU_URLDECODE; /* never for port */ if(!ptr && (flags & CURLU_DEFAULT_PORT) && u->scheme) { /* there is no stored port number, but asked to deliver a default one for the scheme */ @@ -1398,7 +1599,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, case CURLUPART_QUERY: ptr = u->query; ifmissing = CURLUE_NO_QUERY; - plusdecode = urldecode; + plusdecode = flags & CURLU_URLDECODE; if(ptr && !ptr[0] && !(flags & CURLU_GET_EMPTY)) /* there was a blank query and the user do not ask for it */ ptr = NULL; @@ -1410,219 +1611,31 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, /* there was a blank fragment and the user asks for it */ ptr = ""; break; - case CURLUPART_URL: { - char *url; - const char *scheme; - char *options = u->options; - char *port = u->port; - char *allochost = NULL; - bool show_fragment = - u->fragment || (u->fragment_present && flags & CURLU_GET_EMPTY); - bool show_query = - (u->query && u->query[0]) || - (u->query_present && flags & CURLU_GET_EMPTY); - punycode = (flags & CURLU_PUNYCODE) ? 1 : 0; - depunyfy = (flags & CURLU_PUNY2IDN) ? 1 : 0; - if(u->scheme && strcasecompare("file", u->scheme)) { - url = aprintf("file://%s%s%s%s%s", - u->path, - show_query ? "?": "", - u->query ? u->query : "", - show_fragment ? "#": "", - u->fragment ? u->fragment : ""); - } - else if(!u->host) - return CURLUE_NO_HOST; - else { - const struct Curl_handler *h = NULL; - char schemebuf[MAX_SCHEME_LEN + 5]; - if(u->scheme) - scheme = u->scheme; - else if(flags & CURLU_DEFAULT_SCHEME) - scheme = DEFAULT_SCHEME; - else - return CURLUE_NO_SCHEME; - - h = Curl_get_scheme_handler(scheme); - if(!port && (flags & CURLU_DEFAULT_PORT)) { - /* there is no stored port number, but asked to deliver - a default one for the scheme */ - if(h) { - msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); - port = portbuf; - } - } - else if(port) { - /* there is a stored port number, but asked to inhibit if it matches - the default one for the scheme */ - if(h && (h->defport == u->portnum) && - (flags & CURLU_NO_DEFAULT_PORT)) - port = NULL; - } - - if(h && !(h->flags & PROTOPT_URLOPTIONS)) - options = NULL; - - if(u->host[0] == '[') { - if(u->zoneid) { - /* make it '[ host %25 zoneid ]' */ - struct dynbuf enc; - size_t hostlen = strlen(u->host); - curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - if(curlx_dyn_addf(&enc, "%.*s%%25%s]", (int)hostlen - 1, u->host, - u->zoneid)) - return CURLUE_OUT_OF_MEMORY; - allochost = curlx_dyn_ptr(&enc); - } - } - else if(urlencode) { - allochost = curl_easy_escape(NULL, u->host, 0); - if(!allochost) - return CURLUE_OUT_OF_MEMORY; - } - else if(punycode) { - if(!Curl_is_ASCII_name(u->host)) { -#ifndef USE_IDN - return CURLUE_LACKS_IDN; -#else - CURLcode result = Curl_idn_decode(u->host, &allochost); - if(result) - return (result == CURLE_OUT_OF_MEMORY) ? - CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; -#endif - } - } - else if(depunyfy) { - if(Curl_is_ASCII_name(u->host)) { -#ifndef USE_IDN - return CURLUE_LACKS_IDN; -#else - CURLcode result = Curl_idn_encode(u->host, &allochost); - if(result) - /* this is the most likely error */ - return (result == CURLE_OUT_OF_MEMORY) ? - CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; -#endif - } - } - - if(!(flags & CURLU_NO_GUESS_SCHEME) || !u->guessed_scheme) - msnprintf(schemebuf, sizeof(schemebuf), "%s://", scheme); - else - schemebuf[0] = 0; - - url = aprintf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s", - schemebuf, - u->user ? u->user : "", - u->password ? ":": "", - u->password ? u->password : "", - options ? ";" : "", - options ? options : "", - (u->user || u->password || options) ? "@": "", - allochost ? allochost : u->host, - port ? ":": "", - port ? port : "", - u->path ? u->path : "/", - show_query ? "?": "", - u->query ? u->query : "", - show_fragment ? "#": "", - u->fragment ? u->fragment : ""); - free(allochost); - } - if(!url) - return CURLUE_OUT_OF_MEMORY; - *part = url; - return CURLUE_OK; - } + case CURLUPART_URL: + return urlget_url(u, part, flags); default: ptr = NULL; break; } - if(ptr) { - size_t partlen = strlen(ptr); - size_t i = 0; - *part = Curl_memdup0(ptr, partlen); - if(!*part) - return CURLUE_OUT_OF_MEMORY; - if(plusdecode) { - /* convert + to space */ - char *plus = *part; - for(i = 0; i < partlen; ++plus, i++) { - if(*plus == '+') - *plus = ' '; - } - } - if(urldecode) { - char *decoded; - size_t dlen; - /* this unconditional rejection of control bytes is documented - API behavior */ - CURLcode res = Curl_urldecode(*part, 0, &decoded, &dlen, REJECT_CTRL); - free(*part); - if(res) { - *part = NULL; - return CURLUE_URLDECODE; - } - *part = decoded; - partlen = dlen; - } - if(urlencode) { - struct dynbuf enc; - CURLUcode uc; - curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - uc = urlencode_str(&enc, *part, partlen, TRUE, what == CURLUPART_QUERY); - if(uc) - return uc; - free(*part); - *part = curlx_dyn_ptr(&enc); - } - else if(punycode) { - if(!Curl_is_ASCII_name(u->host)) { -#ifndef USE_IDN - return CURLUE_LACKS_IDN; -#else - char *allochost; - CURLcode result = Curl_idn_decode(*part, &allochost); - if(result) - return (result == CURLE_OUT_OF_MEMORY) ? - CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; - free(*part); - *part = allochost; -#endif - } - } - else if(depunyfy) { - if(Curl_is_ASCII_name(u->host)) { -#ifndef USE_IDN - return CURLUE_LACKS_IDN; -#else - char *allochost; - CURLcode result = Curl_idn_encode(*part, &allochost); - if(result) - return (result == CURLE_OUT_OF_MEMORY) ? - CURLUE_OUT_OF_MEMORY : CURLUE_BAD_HOSTNAME; - free(*part); - *part = allochost; -#endif - } - } + if(ptr) + return urlget_format(u, what, ptr, part, plusdecode, flags); - return CURLUE_OK; - } - else - return ifmissing; + return ifmissing; } static CURLUcode set_url_scheme(CURLU *u, const char *scheme, - unsigned int flags) + unsigned int flags) { - size_t plen = strlen(scheme); + size_t plen = strlen(scheme); + const struct Curl_handler *h = NULL; + if((plen > MAX_SCHEME_LEN) || (plen < 1)) + /* too long or too short */ + return CURLUE_BAD_SCHEME; + /* verify that it is a fine scheme */ + h = Curl_get_scheme_handler(scheme); + if(!h) { const char *s = scheme; - if((plen > MAX_SCHEME_LEN) || (plen < 1)) - /* too long or too short */ - return CURLUE_BAD_SCHEME; - /* verify that it is a fine scheme */ - if(!(flags & CURLU_NON_SUPPORT_SCHEME) && !Curl_get_scheme_handler(scheme)) + if(!(flags & CURLU_NON_SUPPORT_SCHEME)) return CURLUE_UNSUPPORTED_SCHEME; if(ISALPHA(*s)) { /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */ @@ -1635,8 +1648,9 @@ static CURLUcode set_url_scheme(CURLU *u, const char *scheme, } else return CURLUE_BAD_SCHEME; - u->guessed_scheme = FALSE; - return CURLUE_OK; + } + u->guessed_scheme = FALSE; + return CURLUE_OK; } static CURLUcode set_url_port(CURLU *u, const char *provided_port) @@ -1659,7 +1673,7 @@ static CURLUcode set_url_port(CURLU *u, const char *provided_port) } static CURLUcode set_url(CURLU *u, const char *url, size_t part_size, - unsigned int flags) + unsigned int flags) { /* * Allow a new URL to replace the existing (if any) contents. @@ -1696,6 +1710,53 @@ static CURLUcode set_url(CURLU *u, const char *url, size_t part_size, return uc; } +static CURLUcode urlset_clear(CURLU *u, CURLUPart what) +{ + switch(what) { + case CURLUPART_URL: + free_urlhandle(u); + memset(u, 0, sizeof(struct Curl_URL)); + break; + case CURLUPART_SCHEME: + Curl_safefree(u->scheme); + u->guessed_scheme = FALSE; + break; + case CURLUPART_USER: + Curl_safefree(u->user); + break; + case CURLUPART_PASSWORD: + Curl_safefree(u->password); + break; + case CURLUPART_OPTIONS: + Curl_safefree(u->options); + break; + case CURLUPART_HOST: + Curl_safefree(u->host); + break; + case CURLUPART_ZONEID: + Curl_safefree(u->zoneid); + break; + case CURLUPART_PORT: + u->portnum = 0; + Curl_safefree(u->port); + break; + case CURLUPART_PATH: + Curl_safefree(u->path); + break; + case CURLUPART_QUERY: + Curl_safefree(u->query); + u->query_present = FALSE; + break; + case CURLUPART_FRAGMENT: + Curl_safefree(u->fragment); + u->fragment_present = FALSE; + break; + default: + return CURLUE_UNKNOWN_PART; + } + return CURLUE_OK; +} + CURLUcode curl_url_set(CURLU *u, CURLUPart what, const char *part, unsigned int flags) { @@ -1710,57 +1771,9 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, if(!u) return CURLUE_BAD_HANDLE; - if(!part) { + if(!part) /* setting a part to NULL clears it */ - switch(what) { - case CURLUPART_URL: - break; - case CURLUPART_SCHEME: - storep = &u->scheme; - u->guessed_scheme = FALSE; - break; - case CURLUPART_USER: - storep = &u->user; - break; - case CURLUPART_PASSWORD: - storep = &u->password; - break; - case CURLUPART_OPTIONS: - storep = &u->options; - break; - case CURLUPART_HOST: - storep = &u->host; - break; - case CURLUPART_ZONEID: - storep = &u->zoneid; - break; - case CURLUPART_PORT: - u->portnum = 0; - storep = &u->port; - break; - case CURLUPART_PATH: - storep = &u->path; - break; - case CURLUPART_QUERY: - storep = &u->query; - u->query_present = FALSE; - break; - case CURLUPART_FRAGMENT: - storep = &u->fragment; - u->fragment_present = FALSE; - break; - default: - return CURLUE_UNKNOWN_PART; - } - if(storep && *storep) { - Curl_safefree(*storep); - } - else if(!storep) { - free_urlhandle(u); - memset(u, 0, sizeof(struct Curl_URL)); - } - return CURLUE_OK; - } + return urlset_clear(u, what); nalloc = strlen(part); if(nalloc > CURL_MAX_INPUT_LENGTH) @@ -1810,9 +1823,8 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, storep = &u->fragment; u->fragment_present = TRUE; break; - case CURLUPART_URL: { + case CURLUPART_URL: return set_url(u, part, nalloc, flags); - } default: return CURLUE_UNKNOWN_PART; } @@ -1849,7 +1861,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, } else { unsigned char out[3]={'%'}; - Curl_hexbyte(&out[1], *i, TRUE); + Curl_hexbyte(&out[1], *i); result = curlx_dyn_addn(&enc, out, 3); if(result) return cc2cu(result); diff --git a/lib/urldata.h b/lib/urldata.h index 45052e84b1..e54721dd98 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -158,20 +158,20 @@ typedef unsigned int curl_prot_t; #include "request.h" #include "netrc.h" -/* return the count of bytes sent, or -1 on error */ -typedef ssize_t (Curl_send)(struct Curl_easy *data, /* transfer */ - int sockindex, /* socketindex */ - const void *buf, /* data to write */ - size_t len, /* max amount to write */ - bool eos, /* last chunk */ - CURLcode *err); /* error to return */ +/* On error return, the value of `pnwritten` has no meaning */ +typedef CURLcode (Curl_send)(struct Curl_easy *data, /* transfer */ + int sockindex, /* socketindex */ + const void *buf, /* data to write */ + size_t len, /* amount to send */ + bool eos, /* last chunk */ + size_t *pnwritten); /* how much sent */ -/* return the count of bytes read, or -1 on error */ -typedef ssize_t (Curl_recv)(struct Curl_easy *data, /* transfer */ - int sockindex, /* socketindex */ - char *buf, /* store data here */ - size_t len, /* max amount to read */ - CURLcode *err); /* error to return */ +/* On error return, the value of `pnread` has no meaning */ +typedef CURLcode (Curl_recv)(struct Curl_easy *data, /* transfer */ + int sockindex, /* socketindex */ + char *buf, /* store data here */ + size_t len, /* max amount to read */ + size_t *pnread); /* how much received */ #include "mime.h" #include "imap.h" @@ -295,7 +295,6 @@ struct ssl_config_data { char *key_type; /* format for private key (default: PEM) */ char *key_passwd; /* plain text private key password */ BIT(certinfo); /* gather lots of certificate info */ - BIT(falsestart); BIT(earlydata); /* use tls1.3 early data */ BIT(enable_beast); /* allow this flaw for interoperability's sake */ BIT(no_revoke); /* disable SSL certificate revocation checks */ @@ -357,93 +356,6 @@ typedef enum { GSS_AUTHSUCC } curlnegotiate; -/* Struct used for GSSAPI (Kerberos V5) authentication */ -#if defined(USE_KERBEROS5) -struct kerberos5data { -#if defined(USE_WINDOWS_SSPI) - CredHandle *credentials; - CtxtHandle *context; - TCHAR *spn; - SEC_WINNT_AUTH_IDENTITY identity; - SEC_WINNT_AUTH_IDENTITY *p_identity; - size_t token_max; - BYTE *output_token; -#else - gss_ctx_id_t context; - gss_name_t spn; -#endif -}; -#endif - -/* Struct used for SCRAM-SHA-1 authentication */ -#ifdef USE_GSASL -#include -struct gsasldata { - Gsasl *ctx; - Gsasl_session *client; -}; -#endif - -/* Struct used for NTLM challenge-response authentication */ -#if defined(USE_NTLM) -struct ntlmdata { -#ifdef USE_WINDOWS_SSPI -/* The sslContext is used for the Schannel bindings. The - * api is available on the Windows 7 SDK and later. - */ -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS - CtxtHandle *sslContext; -#endif - CredHandle *credentials; - CtxtHandle *context; - SEC_WINNT_AUTH_IDENTITY identity; - SEC_WINNT_AUTH_IDENTITY *p_identity; - size_t token_max; - BYTE *output_token; - BYTE *input_token; - size_t input_token_len; - TCHAR *spn; -#else - unsigned int flags; - unsigned char nonce[8]; - unsigned int target_info_len; - void *target_info; /* TargetInfo received in the NTLM type-2 message */ -#endif -}; -#endif - -/* Struct used for Negotiate (SPNEGO) authentication */ -#ifdef USE_SPNEGO -struct negotiatedata { -#ifdef HAVE_GSSAPI - OM_uint32 status; - gss_ctx_id_t context; - gss_name_t spn; - gss_buffer_desc output_token; - struct dynbuf channel_binding_data; -#else -#ifdef USE_WINDOWS_SSPI -#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS - CtxtHandle *sslContext; -#endif - DWORD status; - CredHandle *credentials; - CtxtHandle *context; - SEC_WINNT_AUTH_IDENTITY identity; - SEC_WINNT_AUTH_IDENTITY *p_identity; - TCHAR *spn; - size_t token_max; - BYTE *output_token; - size_t output_token_length; -#endif -#endif - BIT(noauthpersist); - BIT(havenoauthpersist); - BIT(havenegdata); - BIT(havemultiplerequests); -}; -#endif - #ifdef CURL_DISABLE_PROXY #define CONN_IS_PROXIED(x) 0 #else @@ -753,10 +665,6 @@ struct connectdata { * the connection is cleaned up (see Curl_hash_add2()).*/ struct Curl_hash meta_hash; - /* 'remote_addr' is the particular IP we connected to. it is owned, set - * and NULLed by the connected socket filter (if there is one). */ - const struct Curl_sockaddr_ex *remote_addr; - struct hostname host; char *hostname_resolve; /* hostname to resolve to address, allocated */ char *secondaryhostname; /* secondary socket hostname (ftp) */ @@ -788,7 +696,7 @@ struct connectdata { struct Curl_cfilter *cfilter[2]; /* connection filters */ struct { struct curltime start[2]; /* when filter shutdown started */ - unsigned int timeout_ms; /* 0 means no timeout */ + timediff_t timeout_ms; /* 0 means no timeout */ } shutdown; struct ssl_primary_config ssl_config; @@ -823,10 +731,6 @@ struct connectdata { struct sockaddr_in local_addr; #endif -#if defined(USE_KERBEROS5) /* Consider moving some of the above GSS-API */ - struct kerberos5data krb5; /* variables into the structure definition, */ -#endif /* however, some of them are ftp specific. */ - struct uint_spbset xfers_attached; /* mids of attached transfers */ /* A connection cache from a SHARE might be used in several multi handles. * We MUST not reuse connections that are running in another multi, @@ -841,26 +745,14 @@ struct connectdata { CtxtHandle *sslContext; #endif -#ifdef USE_GSASL - struct gsasldata gsasl; -#endif - #if defined(USE_NTLM) curlntlm http_ntlm_state; curlntlm proxy_ntlm_state; - - struct ntlmdata ntlm; /* NTLM differs from other authentication schemes - because it authenticates connections, not - single requests! */ - struct ntlmdata proxyntlm; /* NTLM data for proxy */ #endif #ifdef USE_SPNEGO curlnegotiate http_negotiate_state; curlnegotiate proxy_negotiate_state; - - struct negotiatedata negotiate; /* state data for host Negotiate auth */ - struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */ #endif #ifdef USE_UNIX_SOCKETS @@ -893,7 +785,10 @@ struct connectdata { #ifndef CURL_DISABLE_PROXY unsigned char proxy_alpn; /* APLN of proxy tunnel, CURL_HTTP_VERSION* */ #endif - unsigned char transport; /* one of the TRNSPRT_* defines */ + unsigned char transport_wanted; /* one of the TRNSPRT_* defines. Not + necessarily the transport the connection ends using due to Alt-Svc + and happy eyeballing. Use `Curl_conn_get_transport() for actual value + once the connection is set up. */ unsigned char ip_version; /* copied from the Curl_easy at creation time */ /* HTTP version last responded with by the server. * 0 at start, then one of 09, 10, 11, etc. */ @@ -1263,9 +1158,6 @@ struct UrlState { #endif unsigned char httpreq; /* Curl_HttpReq; what kind of HTTP request (if any) is this */ - unsigned char select_bits; /* != 0 -> bitmask of socket events for this - transfer overriding anything the socket may - report */ unsigned int creds_from:2; /* where is the server credentials originating from, see the CREDS_* defines above */ @@ -1515,9 +1407,9 @@ struct UserDefined { #endif void *progress_client; /* pointer to pass to the progress callback */ void *ioctl_client; /* pointer to pass to the ioctl callback */ - long maxage_conn; /* in seconds, max idle time to allow a connection that + timediff_t conn_max_idle_ms; /* max idle time to allow a connection that is to be reused */ - long maxlifetime_conn; /* in seconds, max time since creation to allow a + timediff_t conn_max_age_ms; /* max time since creation to allow a connection that is to be reused */ #ifndef CURL_DISABLE_TFTP long tftp_blksize; /* in bytes, 0 means use default */ @@ -1563,7 +1455,7 @@ struct UserDefined { #endif curl_off_t max_filesize; /* Maximum file size to download */ #ifndef CURL_DISABLE_FTP - unsigned int accepttimeout; /* in milliseconds, 0 means no timeout */ + timediff_t accepttimeout; /* in milliseconds, 0 means no timeout */ unsigned char ftp_filemethod; /* how to get to a file: curl_ftpfile */ unsigned char ftpsslauth; /* what AUTH XXX to try: curl_ftpauth */ unsigned char ftp_ccc; /* FTP CCC options: curl_ftpccc */ @@ -1607,11 +1499,11 @@ struct UserDefined { void *wildcardptr; #endif - unsigned int timeout; /* ms, 0 means no timeout */ - unsigned int connecttimeout; /* ms, 0 means default timeout */ - unsigned int happy_eyeballs_timeout; /* ms, 0 is a valid value */ - unsigned int server_response_timeout; /* ms, 0 means no timeout */ - unsigned int shutdowntimeout; /* ms, 0 means default timeout */ + timediff_t timeout; /* ms, 0 means no timeout */ + timediff_t connecttimeout; /* ms, 0 means default timeout */ + timediff_t happy_eyeballs_timeout; /* ms, 0 is a valid value */ + timediff_t server_response_timeout; /* ms, 0 means no timeout */ + timediff_t shutdowntimeout; /* ms, 0 means default timeout */ int tcp_keepidle; /* seconds in idle before sending keepalive probe */ int tcp_keepintvl; /* seconds between TCP keepalive probes */ int tcp_keepcnt; /* maximum number of keepalive probes */ diff --git a/lib/vauth/cleartext.c b/lib/vauth/cleartext.c index 719abd1960..8fc5d41b9a 100644 --- a/lib/vauth/cleartext.c +++ b/lib/vauth/cleartext.c @@ -127,7 +127,7 @@ void Curl_auth_create_login_message(const char *valuep, struct bufref *out) * Returns void. */ void Curl_auth_create_external_message(const char *user, - struct bufref *out) + struct bufref *out) { /* This is the same formatting as the login message */ Curl_auth_create_login_message(user, out); diff --git a/lib/vauth/digest.c b/lib/vauth/digest.c index ec4e82256c..898629a958 100644 --- a/lib/vauth/digest.c +++ b/lib/vauth/digest.c @@ -42,7 +42,6 @@ #include "../vtls/vtls.h" #include "../curlx/warnless.h" #include "../curlx/strparse.h" -#include "../strcase.h" #include "../curl_printf.h" #include "../rand.h" @@ -153,7 +152,7 @@ static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */ /* Convert sha256 or SHA-512/256 chunk to RFC7616 -suitable ASCII string */ static void auth_digest_sha256_to_ascii(unsigned char *source, /* 32 bytes */ - unsigned char *dest) /* 65 bytes */ + unsigned char *dest) /* 65 bytes */ { int i; for(i = 0; i < 32; i++) @@ -510,31 +509,31 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, /* Extract a value=content pair */ if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) { - if(strcasecompare(value, "nonce")) { + if(curl_strequal(value, "nonce")) { free(digest->nonce); digest->nonce = strdup(content); if(!digest->nonce) return CURLE_OUT_OF_MEMORY; } - else if(strcasecompare(value, "stale")) { - if(strcasecompare(content, "true")) { + else if(curl_strequal(value, "stale")) { + if(curl_strequal(content, "true")) { digest->stale = TRUE; digest->nc = 1; /* we make a new nonce now */ } } - else if(strcasecompare(value, "realm")) { + else if(curl_strequal(value, "realm")) { free(digest->realm); digest->realm = strdup(content); if(!digest->realm) return CURLE_OUT_OF_MEMORY; } - else if(strcasecompare(value, "opaque")) { + else if(curl_strequal(value, "opaque")) { free(digest->opaque); digest->opaque = strdup(content); if(!digest->opaque) return CURLE_OUT_OF_MEMORY; } - else if(strcasecompare(value, "qop")) { + else if(curl_strequal(value, "qop")) { const char *token = content; struct Curl_str out; bool foundAuth = FALSE; @@ -568,28 +567,28 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, return CURLE_OUT_OF_MEMORY; } } - else if(strcasecompare(value, "algorithm")) { + else if(curl_strequal(value, "algorithm")) { free(digest->algorithm); digest->algorithm = strdup(content); if(!digest->algorithm) return CURLE_OUT_OF_MEMORY; - if(strcasecompare(content, "MD5-sess")) + if(curl_strequal(content, "MD5-sess")) digest->algo = ALGO_MD5SESS; - else if(strcasecompare(content, "MD5")) + else if(curl_strequal(content, "MD5")) digest->algo = ALGO_MD5; - else if(strcasecompare(content, "SHA-256")) + else if(curl_strequal(content, "SHA-256")) digest->algo = ALGO_SHA256; - else if(strcasecompare(content, "SHA-256-SESS")) + else if(curl_strequal(content, "SHA-256-SESS")) digest->algo = ALGO_SHA256SESS; - else if(strcasecompare(content, "SHA-512-256")) { + else if(curl_strequal(content, "SHA-512-256")) { #ifdef CURL_HAVE_SHA512_256 digest->algo = ALGO_SHA512_256; #else /* ! CURL_HAVE_SHA512_256 */ return CURLE_NOT_BUILT_IN; #endif /* ! CURL_HAVE_SHA512_256 */ } - else if(strcasecompare(content, "SHA-512-256-SESS")) { + else if(curl_strequal(content, "SHA-512-256-SESS")) { #ifdef CURL_HAVE_SHA512_256 digest->algo = ALGO_SHA512_256SESS; #else /* ! CURL_HAVE_SHA512_256 */ @@ -599,8 +598,8 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, else return CURLE_BAD_CONTENT_ENCODING; } - else if(strcasecompare(value, "userhash")) { - if(strcasecompare(content, "true")) { + else if(curl_strequal(value, "userhash")) { + if(curl_strequal(content, "true")) { digest->userhash = TRUE; } } @@ -771,7 +770,7 @@ static CURLcode auth_create_digest_http_message( if(!hashthis) return CURLE_OUT_OF_MEMORY; - if(digest->qop && strcasecompare(digest->qop, "auth-int")) { + if(digest->qop && curl_strequal(digest->qop, "auth-int")) { /* We do not support auth-int for PUT or POST */ char hashed[65]; char *hashthis2; diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c index 2761c59965..28d0b3599c 100644 --- a/lib/vauth/digest_sspi.c +++ b/lib/vauth/digest_sspi.c @@ -270,7 +270,7 @@ CURLcode Curl_override_sspi_http_realm(const char *chlg, /* Extract a value=content pair */ if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) { - if(strcasecompare(value, "realm")) { + if(curl_strequal(value, "realm")) { /* Setup identity's domain and length */ domain.tchar_ptr = curlx_convert_UTF8_to_tchar(content); @@ -345,8 +345,8 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg, if(!Curl_auth_digest_get_pair(p, value, content, &p)) break; - if(strcasecompare(value, "stale") && - strcasecompare(content, "true")) { + if(curl_strequal(value, "stale") && + curl_strequal(content, "true")) { stale = TRUE; break; } diff --git a/lib/vauth/krb5_gssapi.c b/lib/vauth/krb5_gssapi.c index b559040617..78f4be3dc9 100644 --- a/lib/vauth/krb5_gssapi.c +++ b/lib/vauth/krb5_gssapi.c @@ -96,7 +96,6 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, OM_uint32 major_status; OM_uint32 minor_status; OM_uint32 unused_status; - gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; @@ -104,6 +103,8 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, (void) passwdp; if(!krb5->spn) { + gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; + /* Generate our SPN */ char *spn = Curl_auth_build_spn(service, NULL, host); if(!spn) @@ -315,7 +316,8 @@ void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) /* Free our security context */ if(krb5->context != GSS_C_NO_CONTEXT) { - gss_delete_sec_context(&minor_status, &krb5->context, GSS_C_NO_BUFFER); + Curl_gss_delete_sec_context(&minor_status, &krb5->context, + GSS_C_NO_BUFFER); krb5->context = GSS_C_NO_CONTEXT; } diff --git a/lib/vauth/oauth2.c b/lib/vauth/oauth2.c index 76e77c6a2f..a84dc60daf 100644 --- a/lib/vauth/oauth2.c +++ b/lib/vauth/oauth2.c @@ -94,8 +94,8 @@ CURLcode Curl_auth_create_oauth_bearer_message(const char *user, * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_xoauth_bearer_message(const char *user, - const char *bearer, - struct bufref *out) + const char *bearer, + struct bufref *out) { /* Generate the message */ char *xoauth = aprintf("user=%s\1auth=Bearer %s\1\1", user, bearer); diff --git a/lib/vauth/spnego_gssapi.c b/lib/vauth/spnego_gssapi.c index b17ee46d17..1e576c7134 100644 --- a/lib/vauth/spnego_gssapi.c +++ b/lib/vauth/spnego_gssapi.c @@ -93,7 +93,6 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, OM_uint32 major_status; OM_uint32 minor_status; OM_uint32 unused_status; - gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; gss_channel_bindings_t chan_bindings = GSS_C_NO_CHANNEL_BINDINGS; @@ -111,6 +110,8 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, } if(!nego->spn) { + gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; + /* Generate our SPN */ char *spn = Curl_auth_build_spn(service, NULL, host); if(!spn) @@ -267,7 +268,8 @@ void Curl_auth_cleanup_spnego(struct negotiatedata *nego) /* Free our security context */ if(nego->context != GSS_C_NO_CONTEXT) { - gss_delete_sec_context(&minor_status, &nego->context, GSS_C_NO_BUFFER); + Curl_gss_delete_sec_context(&minor_status, &nego->context, + GSS_C_NO_BUFFER); nego->context = GSS_C_NO_CONTEXT; } @@ -276,7 +278,6 @@ void Curl_auth_cleanup_spnego(struct negotiatedata *nego) gss_release_buffer(&minor_status, &nego->output_token); nego->output_token.value = NULL; nego->output_token.length = 0; - } /* Free the SPN */ diff --git a/lib/vauth/vauth.c b/lib/vauth/vauth.c index e2872b2522..7a85b7041f 100644 --- a/lib/vauth/vauth.c +++ b/lib/vauth/vauth.c @@ -27,10 +27,11 @@ #include #include "vauth.h" +#include "../strdup.h" #include "../urldata.h" -#include "../strcase.h" #include "../curlx/multibyte.h" #include "../curl_printf.h" +#include "../url.h" /* The last #include files should be: */ #include "../curl_memory.h" @@ -156,7 +157,121 @@ bool Curl_auth_allowed_to_host(struct Curl_easy *data) return !data->state.this_is_a_follow || data->set.allow_auth_to_other_hosts || (data->state.first_host && - strcasecompare(data->state.first_host, conn->host.name) && + curl_strequal(data->state.first_host, conn->host.name) && (data->state.first_remote_port == conn->remote_port) && (data->state.first_remote_protocol == conn->handler->protocol)); } + +#ifdef USE_NTLM + +static void ntlm_conn_dtor(void *key, size_t klen, void *entry) +{ + struct ntlmdata *ntlm = entry; + (void)key; + (void)klen; + DEBUGASSERT(ntlm); + Curl_auth_cleanup_ntlm(ntlm); + free(ntlm); +} + +struct ntlmdata *Curl_auth_ntlm_get(struct connectdata *conn, bool proxy) +{ + const char *key = proxy ? CURL_META_NTLM_PROXY_CONN : + CURL_META_NTLM_CONN; + struct ntlmdata *ntlm = Curl_conn_meta_get(conn, key); + if(!ntlm) { + ntlm = calloc(1, sizeof(*ntlm)); + if(!ntlm || + Curl_conn_meta_set(conn, key, ntlm, ntlm_conn_dtor)) + return NULL; + } + return ntlm; +} + +void Curl_auth_ntlm_remove(struct connectdata *conn, bool proxy) +{ + Curl_conn_meta_remove(conn, proxy ? + CURL_META_NTLM_PROXY_CONN : CURL_META_NTLM_CONN); +} + +#endif /* USE_NTLM */ + +#ifdef USE_KERBEROS5 + +static void krb5_conn_dtor(void *key, size_t klen, void *entry) +{ + struct kerberos5data *krb5 = entry; + (void)key; + (void)klen; + DEBUGASSERT(krb5); + Curl_auth_cleanup_gssapi(krb5); + free(krb5); +} + +struct kerberos5data *Curl_auth_krb5_get(struct connectdata *conn) +{ + struct kerberos5data *krb5 = Curl_conn_meta_get(conn, CURL_META_KRB5_CONN); + if(!krb5) { + krb5 = calloc(1, sizeof(*krb5)); + if(!krb5 || + Curl_conn_meta_set(conn, CURL_META_KRB5_CONN, krb5, krb5_conn_dtor)) + return NULL; + } + return krb5; +} + +#endif /* USE_KERBEROS5 */ + +#ifdef USE_GSASL + +static void gsasl_conn_dtor(void *key, size_t klen, void *entry) +{ + struct gsasldata *gsasl = entry; + (void)key; + (void)klen; + DEBUGASSERT(gsasl); + Curl_auth_gsasl_cleanup(gsasl); + free(gsasl); +} + +struct gsasldata *Curl_auth_gsasl_get(struct connectdata *conn) +{ + struct gsasldata *gsasl = Curl_conn_meta_get(conn, CURL_META_GSASL_CONN); + if(!gsasl) { + gsasl = calloc(1, sizeof(*gsasl)); + if(!gsasl || + Curl_conn_meta_set(conn, CURL_META_GSASL_CONN, gsasl, gsasl_conn_dtor)) + return NULL; + } + return gsasl; +} + +#endif /* USE_GSASL */ + +#ifdef USE_SPNEGO + +static void nego_conn_dtor(void *key, size_t klen, void *entry) +{ + struct negotiatedata *nego = entry; + (void)key; + (void)klen; + DEBUGASSERT(nego); + Curl_auth_cleanup_spnego(nego); + free(nego); +} + +struct negotiatedata *Curl_auth_nego_get(struct connectdata *conn, bool proxy) +{ + const char *key = proxy ? CURL_META_NEGO_PROXY_CONN : + CURL_META_NEGO_CONN; + struct negotiatedata *nego = Curl_conn_meta_get(conn, key); + if(!nego) { + nego = calloc(1, sizeof(*nego)); + if(!nego || + Curl_conn_meta_set(conn, key, nego, nego_conn_dtor)) + return NULL; + } + return nego; +} + +#endif /* USE_SPNEGO */ diff --git a/lib/vauth/vauth.h b/lib/vauth/vauth.h index 58b13f582c..7d085e33fd 100644 --- a/lib/vauth/vauth.h +++ b/lib/vauth/vauth.h @@ -27,8 +27,10 @@ #include #include "../bufref.h" +#include "../curlx/dynbuf.h" struct Curl_easy; +struct connectdata; #if !defined(CURL_DISABLE_DIGEST_AUTH) struct digestdata; @@ -38,10 +40,6 @@ struct digestdata; struct ntlmdata; #endif -#if defined(USE_KERBEROS5) -struct kerberos5data; -#endif - #if (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) && defined(USE_SPNEGO) struct negotiatedata; #endif @@ -50,7 +48,8 @@ struct negotiatedata; struct gsasldata; #endif -#if defined(USE_WINDOWS_SSPI) +#ifdef USE_WINDOWS_SSPI +#include "../curl_sspi.h" #define GSS_ERROR(status) ((status) & 0x80000000) #endif @@ -117,9 +116,23 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, /* This is used to clean up the digest specific data */ void Curl_auth_digest_cleanup(struct digestdata *digest); +#else +#define Curl_auth_is_digest_supported() FALSE #endif /* !CURL_DISABLE_DIGEST_AUTH */ #ifdef USE_GSASL + +/* meta key for storing GSASL meta at connection */ +#define CURL_META_GSASL_CONN "meta:auth:gsasl:conn" + +#include +struct gsasldata { + Gsasl *ctx; + Gsasl_session *client; +}; + +struct gsasldata *Curl_auth_gsasl_get(struct connectdata *conn); + /* This is used to evaluate if MECH is supported by gsasl */ bool Curl_auth_gsasl_is_supported(struct Curl_easy *data, const char *mech, @@ -141,9 +154,46 @@ void Curl_auth_gsasl_cleanup(struct gsasldata *digest); #endif #if defined(USE_NTLM) + +/* meta key for storing NTML meta at connection */ +#define CURL_META_NTLM_CONN "meta:auth:ntml:conn" +/* meta key for storing NTML-PROXY meta at connection */ +#define CURL_META_NTLM_PROXY_CONN "meta:auth:ntml-proxy:conn" + +struct ntlmdata { +#ifdef USE_WINDOWS_SSPI +/* The sslContext is used for the Schannel bindings. The + * api is available on the Windows 7 SDK and later. + */ +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + CtxtHandle *sslContext; +#endif + CredHandle *credentials; + CtxtHandle *context; + SEC_WINNT_AUTH_IDENTITY identity; + SEC_WINNT_AUTH_IDENTITY *p_identity; + size_t token_max; + BYTE *output_token; + BYTE *input_token; + size_t input_token_len; + TCHAR *spn; +#else + unsigned int flags; + unsigned char nonce[8]; + unsigned int target_info_len; + void *target_info; /* TargetInfo received in the NTLM type-2 message */ +#endif +}; + /* This is used to evaluate if NTLM is supported */ bool Curl_auth_is_ntlm_supported(void); +struct ntlmdata *Curl_auth_ntlm_get(struct connectdata *conn, bool proxy); +void Curl_auth_ntlm_remove(struct connectdata *conn, bool proxy); + +/* This is used to clean up the NTLM specific data */ +void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm); + /* This is used to generate a base64 encoded NTLM type-1 message */ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, const char *userp, @@ -165,8 +215,6 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, struct ntlmdata *ntlm, struct bufref *out); -/* This is used to clean up the NTLM specific data */ -void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm); #else #define Curl_auth_is_ntlm_supported() FALSE #endif /* USE_NTLM */ @@ -184,6 +232,40 @@ CURLcode Curl_auth_create_xoauth_bearer_message(const char *user, struct bufref *out); #if defined(USE_KERBEROS5) + +#ifdef HAVE_GSSAPI +# ifdef HAVE_GSSGNU +# include +# elif defined HAVE_GSSAPI_GSSAPI_H +# include +# else +# include +# endif +# ifdef HAVE_GSSAPI_GSSAPI_GENERIC_H +# include +# endif +#endif + +/* meta key for storing KRB5 meta at connection */ +#define CURL_META_KRB5_CONN "meta:auth:krb5:conn" + +struct kerberos5data { +#if defined(USE_WINDOWS_SSPI) + CredHandle *credentials; + CtxtHandle *context; + TCHAR *spn; + SEC_WINNT_AUTH_IDENTITY identity; + SEC_WINNT_AUTH_IDENTITY *p_identity; + size_t token_max; + BYTE *output_token; +#else + gss_ctx_id_t context; + gss_name_t spn; +#endif +}; + +struct kerberos5data *Curl_auth_krb5_get(struct connectdata *conn); + /* This is used to evaluate if GSSAPI (Kerberos V5) is supported */ bool Curl_auth_is_gssapi_supported(void); @@ -213,10 +295,48 @@ void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5); #define Curl_auth_is_gssapi_supported() FALSE #endif /* USE_KERBEROS5 */ -#if defined(USE_SPNEGO) -/* This is used to evaluate if SPNEGO (Negotiate) is supported */ +#ifdef USE_SPNEGO + bool Curl_auth_is_spnego_supported(void); +/* meta key for storing NEGO meta at connection */ +#define CURL_META_NEGO_CONN "meta:auth:nego:conn" +/* meta key for storing NEGO PROXY meta at connection */ +#define CURL_META_NEGO_PROXY_CONN "meta:auth:nego-proxy:conn" + +/* Struct used for Negotiate (SPNEGO) authentication */ +struct negotiatedata { +#ifdef HAVE_GSSAPI + OM_uint32 status; + gss_ctx_id_t context; + gss_name_t spn; + gss_buffer_desc output_token; + struct dynbuf channel_binding_data; +#else +#ifdef USE_WINDOWS_SSPI +#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS + CtxtHandle *sslContext; +#endif + DWORD status; + CredHandle *credentials; + CtxtHandle *context; + SEC_WINNT_AUTH_IDENTITY identity; + SEC_WINNT_AUTH_IDENTITY *p_identity; + TCHAR *spn; + size_t token_max; + BYTE *output_token; + size_t output_token_length; +#endif +#endif + BIT(noauthpersist); + BIT(havenoauthpersist); + BIT(havenegdata); + BIT(havemultiplerequests); +}; + +struct negotiatedata * +Curl_auth_nego_get(struct connectdata *conn, bool proxy); + /* This is used to decode a base64 encoded SPNEGO (Negotiate) challenge message */ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data, diff --git a/lib/vquic/curl_msh3.c b/lib/vquic/curl_msh3.c index cbeb650516..6566447ba1 100644 --- a/lib/vquic/curl_msh3.c +++ b/lib/vquic/curl_msh3.c @@ -83,16 +83,16 @@ static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection, void *IfContext); static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection, - void *IfContext); + void *IfContext); static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection, - void *IfContext, - MSH3_REQUEST *Request); + void *IfContext, + MSH3_REQUEST *Request); static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, void *IfContext, const MSH3_HEADER *Header); static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, - void *IfContext, uint32_t *Length, - const uint8_t *Data); + void *IfContext, uint32_t *Length, + const uint8_t *Data); static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, bool Aborted, uint64_t AbortError); static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request, @@ -244,33 +244,17 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) static void drain_stream_from_other_thread(struct Curl_easy *data, struct h3_stream_ctx *stream) { - unsigned char bits; - - /* risky */ - bits = CURL_CSELECT_IN; - if(stream && !stream->upload_done) - bits |= CURL_CSELECT_OUT; - if(data->state.select_bits != bits) { - data->state.select_bits = bits; - /* cannot expire from other thread */ - } + (void)data; + (void)stream; + /* cannot expire from other thread. + here is the disconnect between msh3 and curl */ } static void h3_drain_stream(struct Curl_cfilter *cf, struct Curl_easy *data) { - struct cf_msh3_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - unsigned char bits; - (void)cf; - bits = CURL_CSELECT_IN; - if(stream && !stream->upload_done) - bits |= CURL_CSELECT_OUT; - if(data->state.select_bits != bits) { - data->state.select_bits = bits; - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } + Curl_multi_mark_dirty(data); } static const MSH3_CONNECTION_IF msh3_conn_if = { @@ -294,7 +278,7 @@ static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection, } static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection, - void *IfContext) + void *IfContext) { struct Curl_cfilter *cf = IfContext; struct cf_msh3_ctx *ctx = cf->ctx; @@ -307,8 +291,8 @@ static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection, } static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection, - void *IfContext, - MSH3_REQUEST *Request) + void *IfContext, + MSH3_REQUEST *Request) { (void)Connection; (void)IfContext; @@ -361,17 +345,16 @@ static CURLcode write_resp_raw(struct Curl_easy *data, struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); CURLcode result = CURLE_OK; - ssize_t nwritten; + size_t nwritten; if(!stream) return CURLE_RECV_ERROR; - nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result); - if(nwritten < 0) { + result = Curl_bufq_write(&stream->recvbuf, mem, memlen, &nwritten); + if(result) return result; - } - if((size_t)nwritten < memlen) { + if(nwritten < memlen) { /* This MUST not happen. Our recbuf is dimensioned to hold the * full max_stream_window and then some for this very reason. */ DEBUGASSERT(0); @@ -518,40 +501,33 @@ static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request, (void)SendContext; } -static ssize_t recv_closed_stream(struct Curl_cfilter *cf, - struct Curl_easy *data, - CURLcode *err) +static CURLcode recv_closed_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + size_t *pnread) { struct cf_msh3_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - ssize_t nread = -1; - if(!stream) { - *err = CURLE_RECV_ERROR; - return -1; - } (void)cf; + *pnread = 0; + if(!stream) + return CURLE_RECV_ERROR; + if(stream->reset) { failf(data, "HTTP/3 stream reset by server"); - *err = CURLE_PARTIAL_FILE; - CURL_TRC_CF(data, cf, "cf_recv, was reset -> %d", *err); - goto out; + CURL_TRC_CF(data, cf, "cf_recv, was reset"); + return CURLE_PARTIAL_FILE; } else if(stream->error3) { failf(data, "HTTP/3 stream was not closed cleanly: (error %zd)", (ssize_t)stream->error3); - *err = CURLE_HTTP3; - CURL_TRC_CF(data, cf, "cf_recv, closed uncleanly -> %d", *err); - goto out; + CURL_TRC_CF(data, cf, "cf_recv, closed uncleanly"); + return CURLE_HTTP3; } else { - CURL_TRC_CF(data, cf, "cf_recv, closed ok -> %d", *err); + CURL_TRC_CF(data, cf, "cf_recv, closed ok"); } - *err = CURLE_OK; - nread = 0; - -out: - return nread; + return CURLE_OK; } static void set_quic_expire(struct Curl_cfilter *cf, struct Curl_easy *data) @@ -571,60 +547,56 @@ static void set_quic_expire(struct Curl_cfilter *cf, struct Curl_easy *data) } } -static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) +static CURLcode cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, size_t *pnread) { struct cf_msh3_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - ssize_t nread = -1; struct cf_call_data save; + CURLcode result = CURLE_OK; + *pnread = 0; CURL_TRC_CF(data, cf, "cf_recv(len=%zu), stream=%d", len, !!stream); - if(!stream) { - *err = CURLE_RECV_ERROR; - return -1; - } + if(!stream) + return CURLE_RECV_ERROR; CF_DATA_SAVE(save, cf, data); msh3_lock_acquire(&stream->recv_lock); if(stream->recv_error) { failf(data, "request aborted"); - *err = stream->recv_error; + result = stream->recv_error; goto out; } - *err = CURLE_OK; - if(!Curl_bufq_is_empty(&stream->recvbuf)) { - nread = Curl_bufq_read(&stream->recvbuf, - (unsigned char *)buf, len, err); - CURL_TRC_CF(data, cf, "read recvbuf(len=%zu) -> %zd, %d", - len, nread, *err); - if(nread < 0) + result = Curl_bufq_cread(&stream->recvbuf, buf, len, pnread); + CURL_TRC_CF(data, cf, "read recvbuf(len=%zu) -> %d, %zu", + len, result, *pnread); + if(result) goto out; if(stream->closed) h3_drain_stream(cf, data); } else if(stream->closed) { - nread = recv_closed_stream(cf, data, err); + result = recv_closed_stream(cf, data, pnread); goto out; } else { CURL_TRC_CF(data, cf, "req: nothing here, call again"); - *err = CURLE_AGAIN; + result = CURLE_AGAIN; } out: msh3_lock_release(&stream->recv_lock); set_quic_expire(cf, data); CF_DATA_RESTORE(cf, save); - return nread; + return result; } -static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, - CURLcode *err) +static CURLcode cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, bool eos, + size_t *pnwritten) { struct cf_msh3_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); @@ -634,7 +606,9 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, size_t nheader, i; ssize_t nwritten = -1; struct cf_call_data save; + CURLcode result = CURLE_OK; + *pnwritten = 0; CF_DATA_SAVE(save, cf, data); Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); @@ -648,23 +622,21 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* The first send on the request contains the headers and possibly some data. Parse out the headers and create the request, then if there is any data left over go ahead and send it too. */ - nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err); + nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, &result); if(nwritten < 0) goto out; DEBUGASSERT(h1.done); DEBUGASSERT(h1.req); + *pnwritten = (size_t)nwritten; - *err = Curl_http_req_to_h2(&h2_headers, h1.req, data); - if(*err) { - nwritten = -1; + result = Curl_http_req_to_h2(&h2_headers, h1.req, data); + if(result) goto out; - } nheader = Curl_dynhds_count(&h2_headers); nva = malloc(sizeof(MSH3_HEADER) * nheader); if(!nva) { - *err = CURLE_OUT_OF_MEMORY; - nwritten = -1; + result = CURLE_OUT_OF_MEMORY; goto out; } @@ -683,11 +655,10 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, MSH3_REQUEST_FLAG_NONE); if(!stream->req) { failf(data, "request open failed"); - *err = CURLE_SEND_ERROR; - goto out; + result = CURLE_SEND_ERROR; } - *err = CURLE_OK; - nwritten = len; + result = CURLE_OK; + *pnwritten = len; goto out; } else { @@ -699,14 +670,14 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_NONE, buf, (uint32_t)len, stream)) { - *err = CURLE_SEND_ERROR; + result = CURLE_SEND_ERROR; goto out; } /* msh3/msquic will hold onto this memory until the send complete event. How do we make sure curl does not free it until then? */ - *err = CURLE_OK; - nwritten = len; + result = CURLE_OK; + *pnwritten = len; } out: @@ -715,7 +686,7 @@ out: Curl_h1_req_parse_free(&h1); Curl_dynhds_free(&h2_headers); CF_DATA_RESTORE(cf, save); - return nwritten; + return result; } static void cf_msh3_adjust_pollset(struct Curl_cfilter *cf, @@ -964,8 +935,6 @@ static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data) "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]); ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; } - if(cf->sockindex == FIRSTSOCKET) - cf->conn->remote_addr = NULL; } if(ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) { sclose(ctx->sock[SP_LOCAL]); @@ -1049,7 +1018,6 @@ struct Curl_cftype Curl_cft_http3 = { cf_msh3_connect, cf_msh3_close, Curl_cf_def_shutdown, - Curl_cf_def_get_host, cf_msh3_adjust_pollset, cf_msh3_data_pending, cf_msh3_send, diff --git a/lib/vquic/curl_ngtcp2.c b/lib/vquic/curl_ngtcp2.c index f529f7e4fa..69d54ecce2 100644 --- a/lib/vquic/curl_ngtcp2.c +++ b/lib/vquic/curl_ngtcp2.c @@ -47,12 +47,12 @@ #endif #include "../urldata.h" +#include "../url.h" #include "../uint-hash.h" #include "../sendf.h" #include "../strdup.h" #include "../rand.h" #include "../multiif.h" -#include "../strcase.h" #include "../cfilters.h" #include "../cf-socket.h" #include "../connect.h" @@ -342,23 +342,6 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) } } -static void h3_drain_stream(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_ngtcp2_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - unsigned char bits; - - (void)cf; - bits = CURL_CSELECT_IN; - if(stream && stream->upload_left && !stream->send_closed) - bits |= CURL_CSELECT_OUT; - if(data->state.select_bits != bits) { - data->state.select_bits = bits; - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } -} - /* ngtcp2 default congestion controller does not perform pacing. Limit the maximum packet burst to MAX_PKT_BURST packets. */ #define MAX_PKT_BURST 10 @@ -391,8 +374,8 @@ static void pktx_init(struct pkt_io_ctx *pktx, } static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, - uint64_t datalen, void *user_data, - void *stream_user_data); + uint64_t datalen, void *user_data, + void *stream_user_data); static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) { @@ -485,6 +468,7 @@ static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data) ctx->handshake_at = curlx_now(); ctx->tls_handshake_complete = TRUE; cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ + Curl_vquic_report_handshake(&ctx->tls, cf, data); ctx->tls_vrfy_result = Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer); @@ -744,7 +728,7 @@ static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, CURL_TRC_CF(s_data, cf, "[%" FMT_PRId64 "] unblock quic flow", (curl_int64_t)stream_id); stream->quic_flow_blocked = FALSE; - h3_drain_stream(cf, s_data); + Curl_multi_mark_dirty(s_data); } return 0; } @@ -912,8 +896,8 @@ static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, } static void cf_ngtcp2_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) + struct Curl_easy *data, + struct easy_pollset *ps) { struct cf_ngtcp2_ctx *ctx = cf->ctx; bool want_recv, want_send; @@ -971,7 +955,7 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t sid, else { CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] CLOSED", stream->id); } - h3_drain_stream(cf, data); + Curl_multi_mark_dirty(data); return 0; } @@ -1072,7 +1056,7 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid, if(stream->status_code / 100 != 1) { stream->resp_hds_complete = TRUE; } - h3_drain_stream(cf, data); + Curl_multi_mark_dirty(data); return 0; } @@ -1293,14 +1277,14 @@ out: } /* incoming data frames on the h3 stream */ -static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t blen, CURLcode *err) +static CURLcode cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t blen, size_t *pnread) { struct cf_ngtcp2_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - ssize_t nread = -1; struct cf_call_data save; struct pkt_io_ctx pktx; + CURLcode result = CURLE_OK; (void)ctx; (void)buf; @@ -1310,7 +1294,7 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, DEBUGASSERT(ctx); DEBUGASSERT(ctx->qconn); DEBUGASSERT(ctx->h3conn); - *err = CURLE_OK; + *pnread = 0; /* handshake verification failed in callback, do not recv anything */ if(ctx->tls_vrfy_result) @@ -1319,46 +1303,38 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, pktx_init(&pktx, cf, data); if(!stream || ctx->shutdown_started) { - *err = CURLE_RECV_ERROR; + result = CURLE_RECV_ERROR; goto out; } if(cf_progress_ingress(cf, data, &pktx)) { - *err = CURLE_RECV_ERROR; - nread = -1; + result = CURLE_RECV_ERROR; goto out; } if(stream->xfer_result) { CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] xfer write failed", stream->id); cf_ngtcp2_stream_close(cf, data, stream); - *err = stream->xfer_result; - nread = -1; + result = stream->xfer_result; goto out; } else if(stream->closed) { - nread = recv_closed_stream(cf, data, stream, err); + ssize_t nread = recv_closed_stream(cf, data, stream, &result); + if(nread > 0) + *pnread = (size_t)nread; goto out; } - *err = CURLE_AGAIN; - nread = -1; + result = CURLE_AGAIN; out: - if(cf_progress_egress(cf, data, &pktx)) { - *err = CURLE_SEND_ERROR; - nread = -1; - } - else { - CURLcode result2 = check_and_set_expiry(cf, data, &pktx); - if(result2) { - *err = result2; - nread = -1; - } - } - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_recv(blen=%zu) -> %zd, %d", - stream ? stream->id : -1, blen, nread, *err); + result = Curl_1st_err(result, cf_progress_egress(cf, data, &pktx)); + result = Curl_1st_err(result, check_and_set_expiry(cf, data, &pktx)); + + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_recv(blen=%zu) -> %dm, %zu", + + stream ? stream->id : -1, blen, result, *pnread); CF_DATA_RESTORE(cf, save); - return nread; + return result; } static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, @@ -1464,10 +1440,10 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, field list. */ #define AUTHORITY_DST_IDX 3 -static ssize_t h3_stream_open(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *buf, size_t len, - CURLcode *err) +static CURLcode h3_stream_open(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *buf, size_t len, + size_t *pnwritten) { struct cf_ngtcp2_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = NULL; @@ -1480,41 +1456,43 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, ssize_t nwritten = -1; nghttp3_data_reader reader; nghttp3_data_reader *preader = NULL; + CURLcode result; + *pnwritten = 0; Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); - *err = h3_data_setup(cf, data); - if(*err) + result = h3_data_setup(cf, data); + if(result) goto out; stream = H3_STREAM_CTX(ctx, data); DEBUGASSERT(stream); if(!stream) { - *err = CURLE_FAILED_INIT; + result = CURLE_FAILED_INIT; goto out; } - nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); + nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, &result); if(nwritten < 0) goto out; + *pnwritten = (size_t)nwritten; + if(!stream->h1.done) { /* need more data */ goto out; } DEBUGASSERT(stream->h1.req); - *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); - if(*err) { - nwritten = -1; + result = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); + if(result) goto out; - } + /* no longer needed */ Curl_h1_req_parse_free(&stream->h1); nheader = Curl_dynhds_count(&h2_headers); nva = malloc(sizeof(nghttp3_nv) * nheader); if(!nva) { - *err = CURLE_OUT_OF_MEMORY; - nwritten = -1; + result = CURLE_OUT_OF_MEMORY; goto out; } @@ -1530,8 +1508,7 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &sid, data); if(rc) { failf(data, "can get bidi streams"); - *err = CURLE_SEND_ERROR; - nwritten = -1; + result = CURLE_SEND_ERROR; goto out; } stream->id = (curl_int64_t)sid; @@ -1574,8 +1551,7 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, "%d (%s)", stream->id, rc, nghttp3_strerror(rc)); break; } - *err = CURLE_SEND_ERROR; - nwritten = -1; + result = CURLE_SEND_ERROR; goto out; } @@ -1592,26 +1568,25 @@ static ssize_t h3_stream_open(struct Curl_cfilter *cf, out: free(nva); Curl_dynhds_free(&h2_headers); - return nwritten; + return result; } -static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, - CURLcode *err) +static CURLcode cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, bool eos, + size_t *pnwritten) { struct cf_ngtcp2_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - ssize_t sent = -1; struct cf_call_data save; struct pkt_io_ctx pktx; - CURLcode result; + CURLcode result = CURLE_OK; CF_DATA_SAVE(save, cf, data); DEBUGASSERT(cf->connected); DEBUGASSERT(ctx->qconn); DEBUGASSERT(ctx->h3conn); pktx_init(&pktx, cf, data); - *err = CURLE_OK; + *pnwritten = 0; /* handshake verification failed in callback, do not send anything */ if(ctx->tls_vrfy_result) @@ -1619,19 +1594,18 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, (void)eos; /* use for stream EOF and block handling */ result = cf_progress_ingress(cf, data, &pktx); - if(result) { - *err = result; - } + if(result) + goto out; if(!stream || stream->id < 0) { if(ctx->shutdown_started) { CURL_TRC_CF(data, cf, "cannot open stream on closed connection"); - *err = CURLE_SEND_ERROR; + result = CURLE_SEND_ERROR; goto out; } - sent = h3_stream_open(cf, data, buf, len, err); - if(sent < 0) { - CURL_TRC_CF(data, cf, "failed to open stream -> %d", *err); + result = h3_stream_open(cf, data, buf, len, pnwritten); + if(result) { + CURL_TRC_CF(data, cf, "failed to open stream -> %d", result); goto out; } stream = H3_STREAM_CTX(ctx, data); @@ -1639,7 +1613,7 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, else if(stream->xfer_result) { CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] xfer write failed", stream->id); cf_ngtcp2_stream_close(cf, data, stream); - *err = stream->xfer_result; + result = stream->xfer_result; goto out; } else if(stream->closed) { @@ -1651,52 +1625,43 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, * error situation. */ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] discarding data" "on closed stream with response", stream->id); - *err = CURLE_OK; - sent = (ssize_t)len; + result = CURLE_OK; + *pnwritten = len; goto out; } CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] send_body(len=%zu) " "-> stream closed", stream->id, len); - *err = CURLE_HTTP3; - sent = -1; + result = CURLE_HTTP3; goto out; } else if(ctx->shutdown_started) { CURL_TRC_CF(data, cf, "cannot send on closed connection"); - *err = CURLE_SEND_ERROR; + result = CURLE_SEND_ERROR; goto out; } else { - sent = Curl_bufq_write(&stream->sendbuf, buf, len, err); + result = Curl_bufq_write(&stream->sendbuf, buf, len, pnwritten); CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send, add to " - "sendbuf(len=%zu) -> %zd, %d", - stream->id, len, sent, *err); - if(sent < 0) { + "sendbuf(len=%zu) -> %d, %zu", + stream->id, len, result, *pnwritten); + if(result) goto out; - } - (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); } - if(sent > 0 && !ctx->tls_handshake_complete && ctx->use_earlydata) - ctx->earlydata_skip += sent; + if(*pnwritten > 0 && !ctx->tls_handshake_complete && ctx->use_earlydata) + ctx->earlydata_skip += *pnwritten; + DEBUGASSERT(!result); result = cf_progress_egress(cf, data, &pktx); - if(result) { - *err = result; - sent = -1; - } out: - result = check_and_set_expiry(cf, data, &pktx); - if(result) { - *err = result; - sent = -1; - } - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send(len=%zu) -> %zd, %d", - stream ? stream->id : -1, len, sent, *err); + result = Curl_1st_err(result, check_and_set_expiry(cf, data, &pktx)); + + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send(len=%zu) -> %d, %zu", + stream ? stream->id : -1, len, result, *pnwritten); CF_DATA_RESTORE(cf, save); - return sent; + return result; } static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, @@ -1756,9 +1721,9 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, * Read a network packet to send from ngtcp2 into `buf`. * Return number of bytes written or -1 with *err set. */ -static ssize_t read_pkt_to_send(void *userp, - unsigned char *buf, size_t buflen, - CURLcode *err) +static CURLcode read_pkt_to_send(void *userp, + unsigned char *buf, size_t buflen, + size_t *pnread) { struct pkt_io_ctx *x = userp; struct cf_ngtcp2_ctx *ctx = x->cf->ctx; @@ -1768,7 +1733,9 @@ static ssize_t read_pkt_to_send(void *userp, uint32_t flags; int64_t stream_id; int fin; - ssize_t nwritten = 0, n; + ssize_t n; + + *pnread = 0; veccnt = 0; stream_id = -1; fin = 0; @@ -1780,7 +1747,6 @@ static ssize_t read_pkt_to_send(void *userp, * When ngtcp2 is happy (because it has no other frame that would fit * or it has nothing more to send), it returns the total length * of the assembled packet. This may be 0 if there was nothing to send. */ - *err = CURLE_OK; for(;;) { if(ctx->h3conn && ngtcp2_conn_get_max_data_left(ctx->qconn)) { @@ -1790,8 +1756,7 @@ static ssize_t read_pkt_to_send(void *userp, failf(x->data, "nghttp3_conn_writev_stream returned error: %s", nghttp3_strerror((int)veccnt)); cf_ngtcp2_h3_err_set(x->cf, x->data, (int)veccnt); - *err = CURLE_SEND_ERROR; - return -1; + return CURLE_SEND_ERROR; } } @@ -1803,9 +1768,7 @@ static ssize_t read_pkt_to_send(void *userp, (const ngtcp2_vec *)vec, veccnt, x->ts); if(n == 0) { /* nothing to send */ - *err = CURLE_AGAIN; - nwritten = -1; - goto out; + return CURLE_AGAIN; } else if(n < 0) { switch(n) { @@ -1837,9 +1800,7 @@ static ssize_t read_pkt_to_send(void *userp, failf(x->data, "ngtcp2_conn_writev_stream returned error: %s", ngtcp2_strerror((int)n)); cf_ngtcp2_err_set(x->cf, x->data, (int)n); - *err = CURLE_SEND_ERROR; - nwritten = -1; - goto out; + return CURLE_SEND_ERROR; } } @@ -1855,12 +1816,10 @@ static ssize_t read_pkt_to_send(void *userp, if(n > 0) { /* packet assembled, leave */ - nwritten = n; - goto out; + *pnread = (size_t)n; + return CURLE_OK; } } -out: - return nwritten; } static CURLcode cf_progress_egress(struct Curl_cfilter *cf, @@ -1868,7 +1827,7 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, struct pkt_io_ctx *pktx) { struct cf_ngtcp2_ctx *ctx = cf->ctx; - ssize_t nread; + size_t nread; size_t max_payload_size, path_max_payload_size, max_pktcnt; size_t pktcnt = 0; size_t gsolen = 0; /* this disables gso until we have a clue */ @@ -1893,7 +1852,7 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, return curlcode; } - /* In UDP, there is a maximum theoretical packet paload length and + /* In UDP, there is a maximum theoretical packet payload length and * a minimum payload length that is "guaranteed" to work. * To detect if this minimum payload can be increased, ngtcp2 sends * now and then a packet payload larger than the minimum. It that @@ -1913,9 +1872,9 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, for(;;) { /* add the next packet to send, if any, to our buffer */ - nread = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, - read_pkt_to_send, pktx, &curlcode); - if(nread < 0) { + curlcode = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, + read_pkt_to_send, pktx, &nread); + if(curlcode) { if(curlcode != CURLE_AGAIN) return curlcode; /* Nothing more to add, flush and leave */ @@ -1934,10 +1893,10 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, if(pktcnt == 0) { /* first packet in buffer. This is either of a known, "good" * payload size or it is a PMTUD. We will see. */ - gsolen = (size_t)nread; + gsolen = nread; } - else if((size_t)nread > gsolen || - (gsolen > path_max_payload_size && (size_t)nread != gsolen)) { + else if(nread > gsolen || + (gsolen > path_max_payload_size && nread != gsolen)) { /* The just added packet is a PMTUD *or* the one(s) before the * just added were PMTUD and the last one is smaller. * Flush the buffer before the last add. */ @@ -1954,7 +1913,7 @@ static CURLcode cf_progress_egress(struct Curl_cfilter *cf, continue; } - if(++pktcnt >= max_pktcnt || (size_t)nread < gsolen) { + if(++pktcnt >= max_pktcnt || nread < gsolen) { /* Reached MAX_PKT_BURST *or* * the capacity of our buffer *or* * last add was shorter than the previous ones, flush */ @@ -1975,28 +1934,15 @@ out: return CURLE_OK; } -/* - * Called from transfer.c:data_pending to know if we should keep looping - * to receive more data from the connection. - */ -static bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - (void)cf; - (void)data; - return FALSE; -} - static CURLcode h3_data_pause(struct Curl_cfilter *cf, struct Curl_easy *data, bool pause) { /* There seems to exist no API in ngtcp2 to shrink/enlarge the streams * windows. As we do in HTTP/2. */ - if(!pause) { - h3_drain_stream(cf, data); - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } + (void)cf; + if(!pause) + Curl_multi_mark_dirty(data); return CURLE_OK; } @@ -2129,8 +2075,9 @@ static CURLcode cf_ngtcp2_shutdown(struct Curl_cfilter *cf, /* Ignore amount written. sendbuf was empty and has always room for * NGTCP2_MAX_UDP_PAYLOAD_SIZE. It can only completely fail, in which * case `result` is set non zero. */ - (void)Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer, - (size_t)nwritten, &result); + size_t n; + result = Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer, + (size_t)nwritten, &n); if(result) { CURL_TRC_CF(data, cf, "error %d adding shutdown packets to sendbuf, " "aborting shutdown", result); @@ -2702,6 +2649,14 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, case CF_QUERY_HTTP_VERSION: *pres1 = 30; return CURLE_OK; + case CF_QUERY_SSL_INFO: + case CF_QUERY_SSL_CTX_INFO: { + struct curl_tlssessioninfo *info = pres2; + if(Curl_vquic_tls_get_ssl_info(&ctx->tls, + (query == CF_QUERY_SSL_INFO), info)) + return CURLE_OK; + break; + } default: break; } @@ -2761,9 +2716,8 @@ struct Curl_cftype Curl_cft_http3 = { cf_ngtcp2_connect, cf_ngtcp2_close, cf_ngtcp2_shutdown, - Curl_cf_def_get_host, cf_ngtcp2_adjust_pollset, - cf_ngtcp2_data_pending, + Curl_cf_def_data_pending, cf_ngtcp2_send, cf_ngtcp2_recv, cf_ngtcp2_data_event, diff --git a/lib/vquic/curl_ngtcp2.h b/lib/vquic/curl_ngtcp2.h index 884662523f..a4ccef49fe 100644 --- a/lib/vquic/curl_ngtcp2.h +++ b/lib/vquic/curl_ngtcp2.h @@ -52,9 +52,9 @@ struct Curl_cfilter; void Curl_ngtcp2_ver(char *p, size_t len); CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, - struct Curl_easy *data, - struct connectdata *conn, - const struct Curl_addrinfo *ai); + struct Curl_easy *data, + struct connectdata *conn, + const struct Curl_addrinfo *ai); bool Curl_conn_is_ngtcp2(const struct Curl_easy *data, const struct connectdata *conn, diff --git a/lib/vquic/curl_osslq.c b/lib/vquic/curl_osslq.c index d5af10b06b..90fed6c44f 100644 --- a/lib/vquic/curl_osslq.c +++ b/lib/vquic/curl_osslq.c @@ -37,7 +37,6 @@ #include "../strdup.h" #include "../rand.h" #include "../multiif.h" -#include "../strcase.h" #include "../cfilters.h" #include "../cf-socket.h" #include "../connect.h" @@ -55,6 +54,7 @@ #include "../vtls/vtls.h" #include "../vtls/openssl.h" #include "curl_osslq.h" +#include "../url.h" #include "../curlx/warnless.h" /* The last 3 #include files should be in this order */ @@ -560,7 +560,7 @@ static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf, } static CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf, - struct Curl_easy *data) + struct Curl_easy *data) { struct cf_osslq_ctx *ctx = cf->ctx; @@ -711,31 +711,14 @@ static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf, return NULL; } -static void h3_drain_stream(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_osslq_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - unsigned char bits; - - (void)cf; - bits = CURL_CSELECT_IN; - if(stream && stream->upload_left && !stream->send_closed) - bits |= CURL_CSELECT_OUT; - if(data->state.select_bits != bits) { - data->state.select_bits = bits; - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } -} - static CURLcode h3_data_pause(struct Curl_cfilter *cf, struct Curl_easy *data, bool pause) { + (void)cf; if(!pause) { /* unpaused. make it run again right away */ - h3_drain_stream(cf, data); - Curl_expire(data, 0, EXPIRE_RUN_NOW); + Curl_multi_mark_dirty(data); } return CURLE_OK; } @@ -766,7 +749,7 @@ static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, else { CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] CLOSED", stream->s.id); } - h3_drain_stream(cf, data); + Curl_multi_mark_dirty(data); return 0; } @@ -783,21 +766,20 @@ static CURLcode write_resp_raw(struct Curl_cfilter *cf, struct cf_osslq_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); CURLcode result = CURLE_OK; - ssize_t nwritten; + size_t nwritten; (void)cf; if(!stream) { return CURLE_RECV_ERROR; } - nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result); - if(nwritten < 0) { + result = Curl_bufq_write(&stream->recvbuf, mem, memlen, &nwritten); + if(result) return result; - } if(!flow) stream->recv_buf_nonflow += (size_t)nwritten; - if((size_t)nwritten < memlen) { + if(nwritten < memlen) { /* This MUST not happen. Our recbuf is dimensioned to hold the * full max_stream_window and then some for this very reason. */ DEBUGASSERT(0); @@ -831,7 +813,7 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, stream->download_recvd += (curl_off_t)buflen; CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu, total=%" FMT_OFF_T, stream->s.id, buflen, stream->download_recvd); - h3_drain_stream(cf, data); + Curl_multi_mark_dirty(data); return 0; } @@ -943,7 +925,7 @@ static int cb_h3_end_headers(nghttp3_conn *conn, int64_t sid, if(stream->status_code / 100 != 1) { stream->resp_hds_complete = TRUE; } - h3_drain_stream(cf, data); + Curl_multi_mark_dirty(data); return 0; } @@ -1259,27 +1241,24 @@ struct h3_quic_recv_ctx { struct cf_osslq_stream *s; }; -static ssize_t h3_quic_recv(void *reader_ctx, - unsigned char *buf, size_t len, - CURLcode *err) +static CURLcode h3_quic_recv(void *reader_ctx, + unsigned char *buf, size_t len, + size_t *pnread) { struct h3_quic_recv_ctx *x = reader_ctx; - size_t nread; int rv; - *err = CURLE_OK; - rv = SSL_read_ex(x->s->ssl, buf, len, &nread); + rv = SSL_read_ex(x->s->ssl, buf, len, pnread); if(rv <= 0) { int detail = SSL_get_error(x->s->ssl, rv); if(detail == SSL_ERROR_WANT_READ || detail == SSL_ERROR_WANT_WRITE) { - *err = CURLE_AGAIN; - return -1; + return CURLE_AGAIN; } else if(detail == SSL_ERROR_ZERO_RETURN) { CURL_TRC_CF(x->data, x->cf, "[%" FMT_PRId64 "] h3_quic_recv -> EOS", x->s->id); x->s->recvd_eos = TRUE; - return 0; + return CURLE_OK; } else if(SSL_get_stream_read_state(x->s->ssl) == SSL_STREAM_STATE_RESET_REMOTE) { @@ -1292,14 +1271,13 @@ static ssize_t h3_quic_recv(void *reader_ctx, x->s->reset = TRUE; } x->s->recvd_eos = TRUE; - return 0; + return CURLE_OK; } else { - *err = cf_osslq_ssl_err(x->cf, x->data, detail, CURLE_RECV_ERROR); - return -1; + return cf_osslq_ssl_err(x->cf, x->data, detail, CURLE_RECV_ERROR); } } - return (ssize_t)nread; + return CURLE_OK; } static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s, @@ -1309,6 +1287,7 @@ static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s, struct cf_osslq_ctx *ctx = cf->ctx; CURLcode result = CURLE_OK; ssize_t nread; + size_t n; struct h3_quic_recv_ctx x; bool eagain = FALSE; size_t total_recv_len = 0; @@ -1324,8 +1303,8 @@ static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s, (total_recv_len < H3_STREAM_CHUNK_SIZE)) { if(Curl_bufq_is_empty(&s->recvbuf) && !s->recvd_eos) { while(!eagain && !s->recvd_eos && !Curl_bufq_is_full(&s->recvbuf)) { - nread = Curl_bufq_sipn(&s->recvbuf, 0, h3_quic_recv, &x, &result); - if(nread < 0) { + result = Curl_bufq_sipn(&s->recvbuf, 0, h3_quic_recv, &x, &n); + if(result) { if(result != CURLE_AGAIN) goto out; result = CURLE_OK; @@ -1566,7 +1545,7 @@ static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf, if(stream) { nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id); stream->s.send_blocked = FALSE; - h3_drain_stream(cf, ctx->curl_items[idx_count]); + Curl_multi_mark_dirty(ctx->curl_items[idx_count]); CURL_TRC_CF(ctx->curl_items[idx_count], cf, "unblocked"); } result_count--; @@ -2009,44 +1988,39 @@ out: return nwritten; } -static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, - CURLcode *err) +static CURLcode cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, bool eos, + size_t *pnwritten) { struct cf_osslq_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); struct cf_call_data save; ssize_t nwritten; - CURLcode result; + CURLcode result = CURLE_OK; (void)eos; /* use to end stream */ CF_DATA_SAVE(save, cf, data); DEBUGASSERT(cf->connected); DEBUGASSERT(ctx->tls.ossl.ssl); DEBUGASSERT(ctx->h3.conn); - *err = CURLE_OK; + *pnwritten = 0; result = cf_progress_ingress(cf, data); - if(result) { - *err = result; - nwritten = -1; + if(result) goto out; - } result = cf_progress_egress(cf, data); - if(result) { - *err = result; - nwritten = -1; + if(result) goto out; - } if(!stream || stream->s.id < 0) { - nwritten = h3_stream_open(cf, data, buf, len, err); + nwritten = h3_stream_open(cf, data, buf, len, &result); if(nwritten < 0) { - CURL_TRC_CF(data, cf, "failed to open stream -> %d", *err); + CURL_TRC_CF(data, cf, "failed to open stream -> %d", result); goto out; } stream = H3_STREAM_CTX(ctx, data); + *pnwritten = (size_t)nwritten; } else if(stream->closed) { if(stream->resp_hds_complete) { @@ -2057,56 +2031,48 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data, * error situation. */ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] discarding data" "on closed stream with response", stream->s.id); - *err = CURLE_OK; - nwritten = (ssize_t)len; + result = CURLE_OK; + *pnwritten = len; goto out; } CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] send_body(len=%zu) " "-> stream closed", stream->s.id, len); - *err = CURLE_HTTP3; - nwritten = -1; + result = CURLE_HTTP3; goto out; } else { - nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err); + result = Curl_bufq_write(&stream->sendbuf, buf, len, pnwritten); CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send, add to " - "sendbuf(len=%zu) -> %zd, %d", - stream->s.id, len, nwritten, *err); - if(nwritten < 0) { + "sendbuf(len=%zu) -> %d, %zu", + stream->s.id, len, result, *pnwritten); + if(result) goto out; - } - (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id); } - result = cf_progress_egress(cf, data); - if(result) { - *err = result; - nwritten = -1; - } + result = Curl_1st_err(result, cf_progress_egress(cf, data)); out: - result = check_and_set_expiry(cf, data); - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send(len=%zu) -> %zd, %d", - stream ? stream->s.id : -1, len, nwritten, *err); + result = Curl_1st_err(result, check_and_set_expiry(cf, data)); + + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_send(len=%zu) -> %d, %zu", + stream ? stream->s.id : -1, len, result, *pnwritten); CF_DATA_RESTORE(cf, save); - return nwritten; + return result; } -static ssize_t recv_closed_stream(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream, - CURLcode *err) +static CURLcode recv_closed_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream, + size_t *pnread) { - ssize_t nread = -1; - (void)cf; + *pnread = 0; if(stream->reset) { failf(data, "HTTP/3 stream %" FMT_PRId64 " reset by server", stream->s.id); - *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; - goto out; + return data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; } else if(!stream->resp_hds_complete) { failf(data, @@ -2114,24 +2080,18 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, " was closed cleanly, but before getting" " all response header fields, treated as error", stream->s.id); - *err = CURLE_HTTP3; - goto out; + return CURLE_HTTP3; } - *err = CURLE_OK; - nread = 0; - -out: - return nread; + return CURLE_OK; } -static ssize_t cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) +static CURLcode cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, size_t *pnread) { struct cf_osslq_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - ssize_t nread = -1; struct cf_call_data save; - CURLcode result; + CURLcode result = CURLE_OK; (void)ctx; CF_DATA_SAVE(save, cf, data); @@ -2139,69 +2099,55 @@ static ssize_t cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data, DEBUGASSERT(ctx); DEBUGASSERT(ctx->tls.ossl.ssl); DEBUGASSERT(ctx->h3.conn); - *err = CURLE_OK; + *pnread = 0; if(!stream) { - *err = CURLE_RECV_ERROR; + result = CURLE_RECV_ERROR; goto out; } if(!Curl_bufq_is_empty(&stream->recvbuf)) { - nread = Curl_bufq_read(&stream->recvbuf, - (unsigned char *)buf, len, err); - if(nread < 0) { + result = Curl_bufq_cread(&stream->recvbuf, buf, len, pnread); + if(result) { CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->s.id, len, nread, *err); + "-> %d, %zu", stream->s.id, len, result, *pnread); goto out; } } - result = cf_progress_ingress(cf, data); - if(result) { - *err = result; - nread = -1; + result = Curl_1st_err(result, cf_progress_ingress(cf, data)); + if(result) goto out; - } /* recvbuf had nothing before, maybe after progressing ingress? */ - if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) { - nread = Curl_bufq_read(&stream->recvbuf, - (unsigned char *)buf, len, err); - if(nread < 0) { + if(!*pnread && !Curl_bufq_is_empty(&stream->recvbuf)) { + result = Curl_bufq_cread(&stream->recvbuf, buf, len, pnread); + if(result) { CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->s.id, len, nread, *err); + "-> %d, %zu", stream->s.id, len, result, *pnread); goto out; } } - if(nread > 0) { - h3_drain_stream(cf, data); + if(*pnread) { + Curl_multi_mark_dirty(data); } else { if(stream->closed) { - nread = recv_closed_stream(cf, data, stream, err); + result = recv_closed_stream(cf, data, stream, pnread); goto out; } - *err = CURLE_AGAIN; - nread = -1; + result = CURLE_AGAIN; } out: - if(cf_progress_egress(cf, data)) { - *err = CURLE_SEND_ERROR; - nread = -1; - } - else { - CURLcode result2 = check_and_set_expiry(cf, data); - if(result2) { - *err = result2; - nread = -1; - } - } - CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_recv(len=%zu) -> %zd, %d", - stream ? stream->s.id : -1, len, nread, *err); + result = Curl_1st_err(result, cf_progress_egress(cf, data)); + result = Curl_1st_err(result, check_and_set_expiry(cf, data)); + + CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] cf_recv(len=%zu) -> %d, %zu", + stream ? stream->s.id : -1, len, result, *pnread); CF_DATA_RESTORE(cf, save); - return nread; + return result; } /* @@ -2358,8 +2304,9 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf, case CF_QUERY_MAX_CONCURRENT: { #ifdef SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL /* Added in OpenSSL v3.3.x */ - uint64_t v; - if(!SSL_get_value_uint(ctx->tls.ossl.ssl, SSL_VALUE_CLASS_GENERIC, + uint64_t v = 0; + if(ctx->tls.ossl.ssl && + !SSL_get_value_uint(ctx->tls.ossl.ssl, SSL_VALUE_CLASS_GENERIC, SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL, &v)) { CURL_TRC_CF(data, cf, "error getting available local bidi streams"); return CURLE_HTTP3; @@ -2396,6 +2343,14 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf, case CF_QUERY_HTTP_VERSION: *pres1 = 30; return CURLE_OK; + case CF_QUERY_SSL_INFO: + case CF_QUERY_SSL_CTX_INFO: { + struct curl_tlssessioninfo *info = pres2; + if(Curl_vquic_tls_get_ssl_info(&ctx->tls, + (query == CF_QUERY_SSL_INFO), info)) + return CURLE_OK; + break; + } default: break; } @@ -2412,7 +2367,6 @@ struct Curl_cftype Curl_cft_http3 = { cf_osslq_connect, cf_osslq_close, cf_osslq_shutdown, - Curl_cf_def_get_host, cf_osslq_adjust_pollset, cf_osslq_data_pending, cf_osslq_send, diff --git a/lib/vquic/curl_quiche.c b/lib/vquic/curl_quiche.c index 8806532889..920913d288 100644 --- a/lib/vquic/curl_quiche.c +++ b/lib/vquic/curl_quiche.c @@ -36,7 +36,6 @@ #include "../sendf.h" #include "../strdup.h" #include "../rand.h" -#include "../strcase.h" #include "../multiif.h" #include "../connect.h" #include "../progress.h" @@ -47,6 +46,7 @@ #include "vquic-tls.h" #include "curl_quiche.h" #include "../transfer.h" +#include "../url.h" #include "../curlx/inet_pton.h" #include "../vtls/openssl.h" #include "../vtls/keylog.h" @@ -237,7 +237,7 @@ static bool cf_quiche_do_resume(struct Curl_cfilter *cf, (void)user_data; if(stream->quic_flow_blocked) { stream->quic_flow_blocked = FALSE; - Curl_expire(sdata, 0, EXPIRE_RUN_NOW); + Curl_multi_mark_dirty(sdata); CURL_TRC_CF(sdata, cf, "[%"FMT_PRIu64"] unblock", stream->id); } return TRUE; @@ -250,8 +250,8 @@ static bool cf_quiche_do_expire(struct Curl_cfilter *cf, { (void)stream; (void)user_data; - CURL_TRC_CF(sdata, cf, "conn closed, expire transfer"); - Curl_expire(sdata, 0, EXPIRE_RUN_NOW); + CURL_TRC_CF(sdata, cf, "conn closed, mark as dirty"); + Curl_multi_mark_dirty(sdata); return TRUE; } @@ -307,23 +307,6 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) } } -static void h3_drain_stream(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct cf_quiche_ctx *ctx = cf->ctx; - struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - unsigned char bits; - - (void)cf; - bits = CURL_CSELECT_IN; - if(stream && !stream->send_closed) - bits |= CURL_CSELECT_OUT; - if(data->state.select_bits != bits) { - data->state.select_bits = bits; - Curl_expire(data, 0, EXPIRE_RUN_NOW); - } -} - static void cf_quiche_expire_conn_closed(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -344,16 +327,16 @@ static CURLcode write_resp_raw(struct Curl_cfilter *cf, struct cf_quiche_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); CURLcode result = CURLE_OK; - ssize_t nwritten; + size_t nwritten; (void)cf; if(!stream) return CURLE_RECV_ERROR; - nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result); - if(nwritten < 0) + result = Curl_bufq_write(&stream->recvbuf, mem, memlen, &nwritten); + if(result) return result; - if((size_t)nwritten < memlen) { + if(nwritten < memlen) { /* This MUST not happen. Our recbuf is dimensioned to hold the * full max_stream_window and then some for this very reason. */ DEBUGASSERT(0); @@ -407,30 +390,26 @@ static int cb_each_header(uint8_t *name, size_t name_len, return result; } -static ssize_t stream_resp_read(void *reader_ctx, - unsigned char *buf, size_t len, - CURLcode *err) +static CURLcode stream_resp_read(void *reader_ctx, + unsigned char *buf, size_t len, + size_t *pnread) { struct cb_ctx *x = reader_ctx; struct cf_quiche_ctx *ctx = x->cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, x->data); ssize_t nread; - if(!stream) { - *err = CURLE_RECV_ERROR; - return -1; - } + *pnread = 0; + if(!stream) + return CURLE_RECV_ERROR; - nread = quiche_h3_recv_body(ctx->h3c, ctx->qconn, stream->id, - buf, len); + nread = quiche_h3_recv_body(ctx->h3c, ctx->qconn, stream->id, buf, len); if(nread >= 0) { - *err = CURLE_OK; - return nread; - } - else { - *err = CURLE_AGAIN; - return -1; + *pnread = (size_t)nread; + return CURLE_OK; } + else + return CURLE_AGAIN; } static CURLcode cf_recv_body(struct Curl_cfilter *cf, @@ -438,7 +417,7 @@ static CURLcode cf_recv_body(struct Curl_cfilter *cf, { struct cf_quiche_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - ssize_t nwritten; + size_t nread; struct cb_ctx cb_ctx; CURLcode result = CURLE_OK; @@ -454,12 +433,12 @@ static CURLcode cf_recv_body(struct Curl_cfilter *cf, cb_ctx.cf = cf; cb_ctx.data = data; - nwritten = Curl_bufq_slurp(&stream->recvbuf, - stream_resp_read, &cb_ctx, &result); + result = Curl_bufq_slurp(&stream->recvbuf, + stream_resp_read, &cb_ctx, &nread); - if(nwritten < 0 && result != CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] recv_body error %zd", - stream->id, nwritten); + if(result && result != CURLE_AGAIN) { + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] recv_body error %zu", + stream->id, nread); failf(data, "Error %d in HTTP/3 response body for stream[%"FMT_PRIu64"]", result, stream->id); stream->closed = TRUE; @@ -562,7 +541,7 @@ static CURLcode cf_quiche_ev_process(struct Curl_cfilter *cf, quiche_h3_event *ev) { CURLcode result = h3_process_event(cf, data, stream, ev); - h3_drain_stream(cf, data); + Curl_multi_mark_dirty(data); if(result) CURL_TRC_CF(data, cf, "error processing event %s " "for [%"FMT_PRIu64"] -> %d", cf_ev_name(ev), @@ -731,27 +710,25 @@ struct read_ctx { quiche_send_info send_info; }; -static ssize_t read_pkt_to_send(void *userp, - unsigned char *buf, size_t buflen, - CURLcode *err) +static CURLcode read_pkt_to_send(void *userp, + unsigned char *buf, size_t buflen, + size_t *pnread) { struct read_ctx *x = userp; struct cf_quiche_ctx *ctx = x->cf->ctx; - ssize_t nwritten; + ssize_t n; - nwritten = quiche_conn_send(ctx->qconn, buf, buflen, &x->send_info); - if(nwritten == QUICHE_ERR_DONE) { - *err = CURLE_AGAIN; - return -1; - } + *pnread = 0; + n = quiche_conn_send(ctx->qconn, buf, buflen, &x->send_info); + if(n == QUICHE_ERR_DONE) + return CURLE_AGAIN; - if(nwritten < 0) { - failf(x->data, "quiche_conn_send returned %zd", nwritten); - *err = CURLE_SEND_ERROR; - return -1; + if(n < 0) { + failf(x->data, "quiche_conn_send returned %zd", n); + return CURLE_SEND_ERROR; } - *err = CURLE_OK; - return nwritten; + *pnread = (size_t)n; + return CURLE_OK; } /* @@ -762,7 +739,7 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, struct Curl_easy *data) { struct cf_quiche_ctx *ctx = cf->ctx; - ssize_t nread; + size_t nread; CURLcode result; curl_int64_t expiry_ns; curl_int64_t timeout_ns; @@ -800,9 +777,9 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, gsolen = quiche_conn_max_send_udp_payload_size(ctx->qconn); for(;;) { /* add the next packet to send, if any, to our buffer */ - nread = Curl_bufq_sipn(&ctx->q.sendbuf, 0, - read_pkt_to_send, &readx, &result); - if(nread < 0) { + result = Curl_bufq_sipn(&ctx->q.sendbuf, 0, + read_pkt_to_send, &readx, &nread); + if(result) { if(result != CURLE_AGAIN) return result; /* Nothing more to add, flush and leave */ @@ -818,7 +795,7 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf, } ++pkt_count; - if((size_t)nread < gsolen || pkt_count >= MAX_PKT_BURST) { + if(nread < gsolen || pkt_count >= MAX_PKT_BURST) { result = vquic_send(cf, data, &ctx->q, gsolen); if(result) { if(result == CURLE_AGAIN) { @@ -840,123 +817,105 @@ out: return result; } -static ssize_t recv_closed_stream(struct Curl_cfilter *cf, - struct Curl_easy *data, - CURLcode *err) +static CURLcode recv_closed_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + size_t *pnread) { struct cf_quiche_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - ssize_t nread = -1; + CURLcode result = CURLE_OK; DEBUGASSERT(stream); + *pnread = 0; if(stream->reset) { failf(data, "HTTP/3 stream %" FMT_PRIu64 " reset by server", stream->id); - *err = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; + result = data->req.bytecount ? CURLE_PARTIAL_FILE : CURLE_HTTP3; CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] cf_recv, was reset -> %d", - stream->id, *err); + stream->id, result); } else if(!stream->resp_got_header) { failf(data, "HTTP/3 stream %" FMT_PRIu64 " was closed cleanly, but before " "getting all response header fields, treated as error", stream->id); - /* *err = CURLE_PARTIAL_FILE; */ - *err = CURLE_HTTP3; - CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] cf_recv, closed incomplete" - " -> %d", stream->id, *err); + result = CURLE_HTTP3; } - else { - *err = CURLE_OK; - nread = 0; - } - return nread; + return result; } -static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) +static CURLcode cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, size_t *pnread) { struct cf_quiche_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); - ssize_t nread = -1; - CURLcode result; + CURLcode result = CURLE_OK; + *pnread = 0; vquic_ctx_update_time(&ctx->q); - if(!stream) { - *err = CURLE_RECV_ERROR; - return -1; - } + if(!stream) + return CURLE_RECV_ERROR; + if(!Curl_bufq_is_empty(&stream->recvbuf)) { - nread = Curl_bufq_read(&stream->recvbuf, - (unsigned char *)buf, len, err); + result = Curl_bufq_cread(&stream->recvbuf, buf, len, pnread); CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->id, len, nread, *err); - if(nread < 0) + "-> %d, %zu", stream->id, len, result, *pnread); + if(result) goto out; } if(cf_process_ingress(cf, data)) { CURL_TRC_CF(data, cf, "cf_recv, error on ingress"); - *err = CURLE_RECV_ERROR; - nread = -1; + result = CURLE_RECV_ERROR; goto out; } /* recvbuf had nothing before, maybe after progressing ingress? */ - if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) { - nread = Curl_bufq_read(&stream->recvbuf, - (unsigned char *)buf, len, err); + if(!*pnread && !Curl_bufq_is_empty(&stream->recvbuf)) { + result = Curl_bufq_cread(&stream->recvbuf, buf, len, pnread); CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] read recvbuf(len=%zu) " - "-> %zd, %d", stream->id, len, nread, *err); - if(nread < 0) + "-> %d, %zu", stream->id, len, result, *pnread); + if(result) goto out; } - if(nread > 0) { + if(*pnread) { if(stream->closed) - h3_drain_stream(cf, data); + Curl_multi_mark_dirty(data); } else { - if(stream->closed) { - nread = recv_closed_stream(cf, data, err); - goto out; - } + if(stream->closed) + result = recv_closed_stream(cf, data, pnread); else if(quiche_conn_is_draining(ctx->qconn)) { failf(data, "QUIC connection is draining"); - *err = CURLE_HTTP3; - nread = -1; - goto out; + result = CURLE_HTTP3; } - *err = CURLE_AGAIN; - nread = -1; + else + result = CURLE_AGAIN; } out: - result = cf_flush_egress(cf, data); - if(result) { - CURL_TRC_CF(data, cf, "cf_recv, flush egress failed"); - *err = result; - nread = -1; - } - if(nread > 0) - ctx->data_recvd += nread; + result = Curl_1st_err(result, cf_flush_egress(cf, data)); + if(*pnread > 0) + ctx->data_recvd += *pnread; CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] cf_recv(total=%" - FMT_OFF_T ") -> %zd, %d", - stream->id, ctx->data_recvd, nread, *err); - return nread; + FMT_OFF_T ") -> %d, %zu", + stream->id, ctx->data_recvd, result, *pnread); + return result; } -static ssize_t cf_quiche_send_body(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct h3_stream_ctx *stream, - const void *buf, size_t len, bool eos, - CURLcode *err) +static CURLcode cf_quiche_send_body(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct h3_stream_ctx *stream, + const void *buf, size_t len, bool eos, + size_t *pnwritten) { struct cf_quiche_ctx *ctx = cf->ctx; ssize_t nwritten; + *pnwritten = 0; nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id, (uint8_t *)CURL_UNCONST(buf), len, eos); if(nwritten == QUICHE_H3_ERR_DONE || (nwritten == 0 && len > 0)) { @@ -967,26 +926,22 @@ static ssize_t cf_quiche_send_body(struct Curl_cfilter *cf, "-> window exhausted", stream->id, len); stream->quic_flow_blocked = TRUE; } - *err = CURLE_AGAIN; - return -1; + return CURLE_AGAIN; } else if(nwritten == QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE) { CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " "-> invalid stream state", stream->id, len); - *err = CURLE_HTTP3; - return -1; + return CURLE_HTTP3; } else if(nwritten == QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE) { CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " "-> exceeds size", stream->id, len); - *err = CURLE_SEND_ERROR; - return -1; + return CURLE_SEND_ERROR; } else if(nwritten < 0) { CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " "-> quiche err %zd", stream->id, len, nwritten); - *err = CURLE_SEND_ERROR; - return -1; + return CURLE_SEND_ERROR; } else { if(eos && (len == (size_t)nwritten)) @@ -994,8 +949,8 @@ static ssize_t cf_quiche_send_body(struct Curl_cfilter *cf, CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send body(len=%zu, " "eos=%d) -> %zd", stream->id, len, stream->send_closed, nwritten); - *err = CURLE_OK; - return nwritten; + *pnwritten = (size_t)nwritten; + return CURLE_OK; } } @@ -1003,10 +958,10 @@ static ssize_t cf_quiche_send_body(struct Curl_cfilter *cf, field list. */ #define AUTHORITY_DST_IDX 3 -static ssize_t h3_open_stream(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char *buf, size_t len, bool eos, - CURLcode *err) +static CURLcode h3_open_stream(struct Curl_cfilter *cf, + struct Curl_easy *data, + const char *buf, size_t blen, bool eos, + size_t *pnwritten) { struct cf_quiche_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); @@ -1014,13 +969,14 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, curl_int64_t stream3_id; struct dynhds h2_headers; quiche_h3_header *nva = NULL; + CURLcode result = CURLE_OK; ssize_t nwritten; + *pnwritten = 0; if(!stream) { - *err = h3_data_setup(cf, data); - if(*err) { - return -1; - } + result = h3_data_setup(cf, data); + if(result) + return result; stream = H3_STREAM_CTX(ctx, data); DEBUGASSERT(stream); } @@ -1028,7 +984,7 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); DEBUGASSERT(stream); - nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); + nwritten = Curl_h1_req_parse_read(&stream->h1, buf, blen, NULL, 0, &result); if(nwritten < 0) goto out; if(!stream->h1.done) { @@ -1037,19 +993,17 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, } DEBUGASSERT(stream->h1.req); - *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); - if(*err) { - nwritten = -1; + result = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); + if(result) goto out; - } + /* no longer needed */ Curl_h1_req_parse_free(&stream->h1); nheader = Curl_dynhds_count(&h2_headers); nva = malloc(sizeof(quiche_h3_header) * nheader); if(!nva) { - *err = CURLE_OUT_OF_MEMORY; - nwritten = -1; + result = CURLE_OUT_OF_MEMORY; goto out; } @@ -1061,7 +1015,11 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, nva[i].value_len = e->valuelen; } - if(eos && ((size_t)nwritten == len)) + *pnwritten = (size_t)nwritten; + buf += *pnwritten; + blen -= *pnwritten; + + if(eos && !blen) stream->send_closed = TRUE; stream3_id = quiche_h3_send_request(ctx->h3c, ctx->qconn, nva, nheader, @@ -1072,21 +1030,18 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, * exhausted. Which happens frequently and intermittent. */ CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] blocked", stream->id); stream->quic_flow_blocked = TRUE; - *err = CURLE_AGAIN; - nwritten = -1; + result = CURLE_AGAIN; goto out; } else { CURL_TRC_CF(data, cf, "send_request(%s) -> %" FMT_PRIu64, data->state.url, stream3_id); } - *err = CURLE_SEND_ERROR; - nwritten = -1; + result = CURLE_SEND_ERROR; goto out; } DEBUGASSERT(!stream->opened); - *err = CURLE_OK; stream->id = stream3_id; stream->opened = TRUE; stream->closed = FALSE; @@ -1102,48 +1057,43 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf, } } - if(nwritten > 0 && ((size_t)nwritten < len)) { - /* after the headers, there was request BODY data */ - size_t hds_len = (size_t)nwritten; - ssize_t bwritten; + if(blen) { /* after the headers, there was request BODY data */ + size_t bwritten; + CURLcode r2 = CURLE_OK; - bwritten = cf_quiche_send_body(cf, data, stream, - buf + hds_len, len - hds_len, eos, err); - if((bwritten < 0) && (CURLE_AGAIN != *err)) { - /* real error, fail */ - nwritten = -1; + r2 = cf_quiche_send_body(cf, data, stream, buf, blen, eos, &bwritten); + if(r2 && (CURLE_AGAIN != r2)) { /* real error, fail */ + result = r2; } else if(bwritten > 0) { - nwritten += bwritten; + *pnwritten += (size_t)bwritten; } } out: free(nva); Curl_dynhds_free(&h2_headers); - return nwritten; + return result; } -static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, bool eos, - CURLcode *err) +static CURLcode cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *buf, size_t len, bool eos, + size_t *pnwritten) { struct cf_quiche_ctx *ctx = cf->ctx; struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); CURLcode result; - ssize_t nwritten; + *pnwritten = 0; vquic_ctx_update_time(&ctx->q); - *err = cf_process_ingress(cf, data); - if(*err) { - nwritten = -1; + result = cf_process_ingress(cf, data); + if(result) goto out; - } if(!stream || !stream->opened) { - nwritten = h3_open_stream(cf, data, buf, len, eos, err); - if(nwritten < 0) + result = h3_open_stream(cf, data, buf, len, eos, pnwritten); + if(result) goto out; stream = H3_STREAM_CTX(ctx, data); } @@ -1159,29 +1109,26 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data, * it would see the response and stop/discard sending on its own- */ CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] discarding data" "on closed stream with response", stream->id); - *err = CURLE_OK; - nwritten = (ssize_t)len; + result = CURLE_OK; + *pnwritten = len; goto out; } CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) " "-> stream closed", stream->id, len); - *err = CURLE_HTTP3; - nwritten = -1; + result = CURLE_HTTP3; goto out; } else { - nwritten = cf_quiche_send_body(cf, data, stream, buf, len, eos, err); + result = cf_quiche_send_body(cf, data, stream, buf, len, eos, pnwritten); } out: - result = cf_flush_egress(cf, data); - if(result) { - *err = result; - nwritten = -1; - } - CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] cf_send(len=%zu) -> %zd, %d", - stream ? stream->id : (curl_uint64_t)~0, len, nwritten, *err); - return nwritten; + result = Curl_1st_err(result, cf_flush_egress(cf, data)); + + CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] cf_send(len=%zu) -> %d, %zu", + stream ? stream->id : (curl_uint64_t)~0, len, + result, *pnwritten); + return result; } static bool stream_is_writeable(struct Curl_cfilter *cf, @@ -1240,9 +1187,9 @@ static CURLcode h3_data_pause(struct Curl_cfilter *cf, { /* There seems to exist no API in quiche to shrink/enlarge the streams * windows. As we do in HTTP/2. */ + (void)cf; if(!pause) { - h3_drain_stream(cf, data); - Curl_expire(data, 0, EXPIRE_RUN_NOW); + Curl_multi_mark_dirty(data); } return CURLE_OK; } @@ -1269,13 +1216,13 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf, struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); if(stream && !stream->send_closed) { unsigned char body[1]; - ssize_t sent; + size_t sent; stream->send_closed = TRUE; body[0] = 'X'; - sent = cf_quiche_send(cf, data, body, 0, TRUE, &result); - CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] DONE_SEND -> %zd, %d", - stream->id, sent, result); + result = cf_quiche_send(cf, data, body, 0, TRUE, &sent); + CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] DONE_SEND -> %d, %zu", + stream->id, result, sent); } break; } @@ -1569,7 +1516,7 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf, switch(query) { case CF_QUERY_MAX_CONCURRENT: { curl_uint64_t max_streams = CONN_ATTACHED(cf->conn); - if(!ctx->goaway) { + if(!ctx->goaway && ctx->qconn) { max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn); } *pres1 = (max_streams > INT_MAX) ? INT_MAX : (int)max_streams; @@ -1601,6 +1548,14 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf, case CF_QUERY_HTTP_VERSION: *pres1 = 30; return CURLE_OK; + case CF_QUERY_SSL_INFO: + case CF_QUERY_SSL_CTX_INFO: { + struct curl_tlssessioninfo *info = pres2; + if(Curl_vquic_tls_get_ssl_info(&ctx->tls, + (query == CF_QUERY_SSL_INFO), info)) + return CURLE_OK; + break; + } default: break; } @@ -1654,7 +1609,6 @@ struct Curl_cftype Curl_cft_http3 = { cf_quiche_connect, cf_quiche_close, cf_quiche_shutdown, - Curl_cf_def_get_host, cf_quiche_adjust_pollset, cf_quiche_data_pending, cf_quiche_send, @@ -1707,20 +1661,4 @@ out: return result; } -bool Curl_conn_is_quiche(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex) -{ - struct Curl_cfilter *cf = conn ? conn->cfilter[sockindex] : NULL; - - (void)data; - for(; cf; cf = cf->next) { - if(cf->cft == &Curl_cft_http3) - return TRUE; - if(cf->cft->flags & CF_TYPE_IP_CONNECT) - return FALSE; - } - return FALSE; -} - #endif diff --git a/lib/vquic/curl_quiche.h b/lib/vquic/curl_quiche.h index 9832687bdc..b2c767216f 100644 --- a/lib/vquic/curl_quiche.h +++ b/lib/vquic/curl_quiche.h @@ -41,10 +41,6 @@ CURLcode Curl_cf_quiche_create(struct Curl_cfilter **pcf, struct connectdata *conn, const struct Curl_addrinfo *ai); -bool Curl_conn_is_quiche(const struct Curl_easy *data, - const struct connectdata *conn, - int sockindex); - #endif #endif /* HEADER_CURL_VQUIC_CURL_QUICHE_H */ diff --git a/lib/vquic/vquic-tls.c b/lib/vquic/vquic-tls.c index 2a5be138fc..8a53c83b33 100644 --- a/lib/vquic/vquic-tls.c +++ b/lib/vquic/vquic-tls.c @@ -197,4 +197,46 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, } +bool Curl_vquic_tls_get_ssl_info(struct curl_tls_ctx *ctx, + bool give_ssl_ctx, + struct curl_tlssessioninfo *info) +{ +#ifdef USE_OPENSSL + info->backend = CURLSSLBACKEND_OPENSSL; + info->internals = give_ssl_ctx ? + (void *)ctx->ossl.ssl_ctx : (void *)ctx->ossl.ssl; + return TRUE; +#elif defined(USE_GNUTLS) + (void)give_ssl_ctx; /* gnutls always returns its session */ + info->backend = CURLSSLBACKEND_OPENSSL; + info->internals = ctx->gtls.session; + return TRUE; +#elif defined(USE_WOLFSSL) + info->backend = CURLSSLBACKEND_WOLFSSL; + info->internals = give_ssl_ctx ? + (void *)ctx->wssl.ssl_ctx : (void *)ctx->wssl.ssl; + return TRUE; +#else + return FALSE; +#endif +} + +void Curl_vquic_report_handshake(struct curl_tls_ctx *ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data) +{ + (void)cf; +#ifdef USE_OPENSSL + (void)cf; + Curl_ossl_report_handshake(data, &ctx->ossl); +#elif defined(USE_GNUTLS) + Curl_gtls_report_handshake(data, &ctx->gtls); +#elif defined(USE_WOLFSSL) + Curl_wssl_report_handshake(data, &ctx->wssl); +#else + (void)data; + (void)ctx; +#endif +} + #endif /* !USE_HTTP3 && (USE_OPENSSL || USE_GNUTLS || USE_WOLFSSL) */ diff --git a/lib/vquic/vquic-tls.h b/lib/vquic/vquic-tls.h index bf29eecc91..c694e23e4e 100644 --- a/lib/vquic/vquic-tls.h +++ b/lib/vquic/vquic-tls.h @@ -37,6 +37,7 @@ struct ssl_peer; struct Curl_ssl_session; +struct curl_tlssessioninfo; struct curl_tls_ctx { #ifdef USE_OPENSSL @@ -106,6 +107,14 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx, struct Curl_easy *data, struct ssl_peer *peer); +bool Curl_vquic_tls_get_ssl_info(struct curl_tls_ctx *ctx, + bool give_ssl_ctx, + struct curl_tlssessioninfo *info); + +void Curl_vquic_report_handshake(struct curl_tls_ctx *ctx, + struct Curl_cfilter *cf, + struct Curl_easy *data); + #endif /* !USE_HTTP3 && (USE_OPENSSL || USE_GNUTLS || USE_WOLFSSL) */ #endif /* HEADER_CURL_VQUIC_TLS_H */ diff --git a/lib/vquic/vquic.c b/lib/vquic/vquic.c index b5dc44f8aa..691c0d3fa7 100644 --- a/lib/vquic/vquic.c +++ b/lib/vquic/vquic.c @@ -308,7 +308,7 @@ CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, } CURLcode vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data, - struct cf_quic_ctx *qctx, size_t gsolen) + struct cf_quic_ctx *qctx, size_t gsolen) { qctx->gsolen = gsolen; return vquic_flush(cf, data, qctx); @@ -703,9 +703,10 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, } CURLcode Curl_conn_may_http3(struct Curl_easy *data, - const struct connectdata *conn) + const struct connectdata *conn, + unsigned char transport) { - if(conn->transport == TRNSPRT_UNIX) { + if(transport == TRNSPRT_UNIX) { /* cannot do QUIC over a Unix domain socket */ return CURLE_QUIC_CONNECT_ERROR; } @@ -730,10 +731,12 @@ CURLcode Curl_conn_may_http3(struct Curl_easy *data, #else /* USE_HTTP3 */ CURLcode Curl_conn_may_http3(struct Curl_easy *data, - const struct connectdata *conn) + const struct connectdata *conn, + unsigned char transport) { (void)conn; (void)data; + (void)transport; DEBUGF(infof(data, "QUIC is not supported in this build")); return CURLE_NOT_BUILT_IN; } diff --git a/lib/vquic/vquic.h b/lib/vquic/vquic.h index dbf63b1f6f..3577a857e3 100644 --- a/lib/vquic/vquic.h +++ b/lib/vquic/vquic.h @@ -54,6 +54,7 @@ extern struct Curl_cftype Curl_cft_http3; #endif /* !USE_HTTP3 */ CURLcode Curl_conn_may_http3(struct Curl_easy *data, - const struct connectdata *conn); + const struct connectdata *conn, + unsigned char transport); #endif /* HEADER_CURL_VQUIC_QUIC_H */ diff --git a/lib/vquic/vquic_int.h b/lib/vquic/vquic_int.h index 4641c3125b..d271368d29 100644 --- a/lib/vquic/vquic_int.h +++ b/lib/vquic/vquic_int.h @@ -68,7 +68,7 @@ CURLcode vquic_send_blocked_pkts(struct Curl_cfilter *cf, struct cf_quic_ctx *qctx); CURLcode vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data, - struct cf_quic_ctx *qctx, size_t gsolen); + struct cf_quic_ctx *qctx, size_t gsolen); CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, struct cf_quic_ctx *qctx, size_t gsolen, diff --git a/lib/vssh/curl_path.c b/lib/vssh/curl_path.c index 117d2e6009..474a5ecb8e 100644 --- a/lib/vssh/curl_path.c +++ b/lib/vssh/curl_path.c @@ -84,6 +84,12 @@ CURLcode Curl_getworkingpath(struct Curl_easy *data, return CURLE_OUT_OF_MEMORY; } } + else { + if(curlx_dyn_add(&npath, "/")) { + free(working_path); + return CURLE_OUT_OF_MEMORY; + } + } } if(curlx_dyn_len(&npath)) { diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 86f1e1a568..3bae943c7c 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -58,11 +58,9 @@ #include "../speedcheck.h" #include "../getinfo.h" #include "../strdup.h" -#include "../strcase.h" #include "../vtls/vtls.h" #include "../cfilters.h" #include "../connect.h" -#include "../inet_ntop.h" #include "../parsedate.h" /* for the week day and month names */ #include "../sockaddr.h" /* required for Curl_sockaddr_storage */ #include "../curlx/strparse.h" @@ -71,9 +69,6 @@ #include "../curlx/warnless.h" #include "curl_path.h" -#ifdef HAVE_SYS_STAT_H -#include -#endif #ifdef HAVE_UNISTD_H #include #endif @@ -130,10 +125,6 @@ CURLcode sftp_perform(struct Curl_easy *data, bool *connected, bool *dophase_done); -static void sftp_quote(struct Curl_easy *data, - struct ssh_conn *sshc, - struct SSHPROTO *sshp); -static void sftp_quote_stat(struct Curl_easy *data, struct ssh_conn *sshc); static int myssh_getsock(struct Curl_easy *data, struct connectdata *conn, curl_socket_t *sock); static void myssh_block2waitfor(struct connectdata *conn, @@ -228,9 +219,9 @@ static CURLcode sftp_error_to_CURLE(int err) } #ifndef DEBUGBUILD -#define myssh_state(x,y,z) myssh_set_state(x,y,z) +#define myssh_to(x,y,z) myssh_set_state(x,y,z) #else -#define myssh_state(x,y,z) myssh_set_state(x,y,z, __LINE__) +#define myssh_to(x,y,z) myssh_set_state(x,y,z, __LINE__) #endif /* @@ -371,7 +362,7 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) infof(data, "SSH MD5 fingerprint: %s", md5buffer); - if(!strcasecompare(md5buffer, pubkey_md5)) { + if(!curl_strequal(md5buffer, pubkey_md5)) { failf(data, "Denied establishing ssh session: mismatch md5 fingerprint. " "Remote %s is not equal to %s", md5buffer, pubkey_md5); @@ -519,52 +510,302 @@ cleanup: return rc; } -#define MOVE_TO_ERROR_STATE(_r) do { \ - myssh_state(data, sshc, SSH_SESSION_DISCONNECT); \ - sshc->actualcode = _r; \ - rc = SSH_ERROR; \ - } while(0) +static int myssh_to_ERROR(struct Curl_easy *data, + struct ssh_conn *sshc, + CURLcode result) +{ + myssh_to(data, sshc, SSH_SESSION_DISCONNECT); + sshc->actualcode = result; + return SSH_ERROR; +} -#define MOVE_TO_SFTP_CLOSE_STATE() do { \ - myssh_state(data, sshc, SSH_SFTP_CLOSE); \ - sshc->actualcode = \ - sftp_error_to_CURLE(sftp_get_error(sshc->sftp_session)); \ - rc = SSH_ERROR; \ - } while(0) +static int myssh_to_SFTP_CLOSE(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->actualcode = + sftp_error_to_CURLE(sftp_get_error(sshc->sftp_session)); + return SSH_ERROR; +} -#define MOVE_TO_PASSWD_AUTH do { \ - if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { \ - rc = SSH_OK; \ - myssh_state(data, sshc, SSH_AUTH_PASS_INIT); \ - } \ - else { \ - MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); \ - } \ - } while(0) +static int myssh_to_PASSWD_AUTH(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { + myssh_to(data, sshc, SSH_AUTH_PASS_INIT); + return SSH_OK; + } + return myssh_to_ERROR(data, sshc, CURLE_LOGIN_DENIED); +} -#define MOVE_TO_KEY_AUTH do { \ - if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { \ - rc = SSH_OK; \ - myssh_state(data, sshc, SSH_AUTH_KEY_INIT); \ - } \ - else { \ - MOVE_TO_PASSWD_AUTH; \ - } \ - } while(0) +static int myssh_to_KEY_AUTH(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { + myssh_to(data, sshc, SSH_AUTH_KEY_INIT); + return SSH_OK; + } + return myssh_to_PASSWD_AUTH(data, sshc); +} -#define MOVE_TO_GSSAPI_AUTH do { \ - if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { \ - rc = SSH_OK; \ - myssh_state(data, sshc, SSH_AUTH_GSSAPI); \ - } \ - else { \ - MOVE_TO_KEY_AUTH; \ - } \ - } while(0) +static int myssh_to_GSSAPI_AUTH(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { + myssh_to(data, sshc, SSH_AUTH_GSSAPI); + return SSH_OK; + } + return myssh_to_KEY_AUTH(data, sshc); +} -static -int myssh_auth_interactive(struct connectdata *conn, - struct ssh_conn *sshc) +static int myssh_in_SFTP_READDIR_INIT(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + Curl_pgrsSetDownloadSize(data, -1); + if(data->req.no_body) { + myssh_to(data, sshc, SSH_STOP); + return SSH_NO_ERROR; + } + + /* + * This is a directory that we are trying to get, so produce a directory + * listing + */ + sshc->sftp_dir = sftp_opendir(sshc->sftp_session, + sshp->path); + if(!sshc->sftp_dir) { + failf(data, "Could not open directory for reading: %s", + ssh_get_error(sshc->ssh_session)); + return myssh_to_SFTP_CLOSE(data, sshc); + } + myssh_to(data, sshc, SSH_SFTP_READDIR); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_READDIR(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + CURLcode result = CURLE_OK; + + curlx_dyn_reset(&sshc->readdir_buf); + if(sshc->readdir_attrs) + sftp_attributes_free(sshc->readdir_attrs); + + sshc->readdir_attrs = sftp_readdir(sshc->sftp_session, sshc->sftp_dir); + if(sshc->readdir_attrs) { + sshc->readdir_filename = sshc->readdir_attrs->name; + sshc->readdir_longentry = sshc->readdir_attrs->longname; + sshc->readdir_len = strlen(sshc->readdir_filename); + + if(data->set.list_only) { + char *tmpLine; + + tmpLine = aprintf("%s\n", sshc->readdir_filename); + if(!tmpLine) { + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + return SSH_ERROR; + } + result = Curl_client_write(data, CLIENTWRITE_BODY, + tmpLine, sshc->readdir_len + 1); + free(tmpLine); + + if(result) { + myssh_to(data, sshc, SSH_STOP); + return SSH_NO_ERROR; + } + + } + else { + if(curlx_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) { + sshc->actualcode = CURLE_OUT_OF_MEMORY; + myssh_to(data, sshc, SSH_STOP); + return SSH_ERROR; + } + + if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) && + ((sshc->readdir_attrs->permissions & SSH_S_IFMT) == + SSH_S_IFLNK)) { + sshc->readdir_linkPath = aprintf("%s%s", sshp->path, + sshc->readdir_filename); + + if(!sshc->readdir_linkPath) { + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + return SSH_ERROR; + } + + myssh_to(data, sshc, SSH_SFTP_READDIR_LINK); + return SSH_NO_ERROR; + } + myssh_to(data, sshc, SSH_SFTP_READDIR_BOTTOM); + return SSH_NO_ERROR; + } + } + else if(sftp_dir_eof(sshc->sftp_dir)) { + myssh_to(data, sshc, SSH_SFTP_READDIR_DONE); + } + else { + failf(data, "Could not open remote directory for reading: %s", + ssh_get_error(sshc->ssh_session)); + return myssh_to_SFTP_CLOSE(data, sshc); + } + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_READDIR_LINK(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(sshc->readdir_link_attrs) + sftp_attributes_free(sshc->readdir_link_attrs); + + sshc->readdir_link_attrs = sftp_lstat(sshc->sftp_session, + sshc->readdir_linkPath); + if(!sshc->readdir_link_attrs) { + failf(data, "Could not read symlink for reading: %s", + ssh_get_error(sshc->ssh_session)); + return myssh_to_SFTP_CLOSE(data, sshc); + } + + if(!sshc->readdir_link_attrs->name) { + sshc->readdir_tmp = sftp_readlink(sshc->sftp_session, + sshc->readdir_linkPath); + if(!sshc->readdir_tmp) + sshc->readdir_len = 0; + else + sshc->readdir_len = strlen(sshc->readdir_tmp); + sshc->readdir_longentry = NULL; + sshc->readdir_filename = sshc->readdir_tmp; + } + else { + sshc->readdir_len = strlen(sshc->readdir_link_attrs->name); + sshc->readdir_filename = sshc->readdir_link_attrs->name; + sshc->readdir_longentry = sshc->readdir_link_attrs->longname; + } + + Curl_safefree(sshc->readdir_linkPath); + + if(curlx_dyn_addf(&sshc->readdir_buf, " -> %s", + sshc->readdir_filename)) { + /* Not using: + * return myssh_to_SFTP_CLOSE(data, sshc); + * + * as that assumes an sftp related error while + * assigning sshc->actualcode whereas the current + * error is curlx_dyn_addf() related. + */ + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->actualcode = CURLE_OUT_OF_MEMORY; + return SSH_ERROR; + } + + sftp_attributes_free(sshc->readdir_link_attrs); + sshc->readdir_link_attrs = NULL; + sshc->readdir_filename = NULL; + sshc->readdir_longentry = NULL; + + myssh_to(data, sshc, SSH_SFTP_READDIR_BOTTOM); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_READDIR_BOTTOM(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + CURLcode result; + + if(curlx_dyn_addn(&sshc->readdir_buf, "\n", 1)) + result = CURLE_OUT_OF_MEMORY; + else + result = Curl_client_write(data, CLIENTWRITE_BODY, + curlx_dyn_ptr(&sshc->readdir_buf), + curlx_dyn_len(&sshc->readdir_buf)); + + ssh_string_free_char(sshc->readdir_tmp); + sshc->readdir_tmp = NULL; + + if(result) + myssh_to(data, sshc, SSH_STOP); + else + myssh_to(data, sshc, SSH_SFTP_READDIR); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_READDIR_DONE(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + sftp_closedir(sshc->sftp_dir); + sshc->sftp_dir = NULL; + + /* no data to transfer */ + Curl_xfer_setup_nop(data); + myssh_to(data, sshc, SSH_STOP); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_STATVFS(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + sftp_statvfs_t statvfs; + + statvfs = sftp_statvfs(sshc->sftp_session, sshc->quote_path1); + if(!statvfs && !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + failf(data, "statvfs command failed: %s", + ssh_get_error(sshc->ssh_session)); + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return SSH_OK; + } + else if(statvfs) { + #ifdef _MSC_VER + #define CURL_LIBSSH_VFS_SIZE_MASK "I64u" + #else + #define CURL_LIBSSH_VFS_SIZE_MASK PRIu64 + #endif + CURLcode result; + char *tmp = aprintf("statvfs:\n" + "f_bsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_frsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_blocks: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_bfree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_bavail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_files: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_ffree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_favail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_fsid: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_flag: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" + "f_namemax: %" CURL_LIBSSH_VFS_SIZE_MASK "\n", + statvfs->f_bsize, statvfs->f_frsize, + statvfs->f_blocks, statvfs->f_bfree, + statvfs->f_bavail, statvfs->f_files, + statvfs->f_ffree, statvfs->f_favail, + statvfs->f_fsid, statvfs->f_flag, + statvfs->f_namemax); + sftp_statvfs_free(statvfs); + + if(!tmp) { + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + return SSH_OK; + } + + result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); + free(tmp); + if(result) { + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + } + } + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_OK; +} + +static int myssh_auth_interactive(struct connectdata *conn, + struct ssh_conn *sshc) { int rc; int nprompts; @@ -642,11 +883,11 @@ static void myssh_state_init(struct Curl_easy *data, non-blocking */ ssh_set_blocking(sshc->ssh_session, 0); - myssh_state(data, sshc, SSH_S_STARTUP); + myssh_to(data, sshc, SSH_S_STARTUP); } -static int myssh_state_startup(struct Curl_easy *data, - struct ssh_conn *sshc) +static int myssh_in_S_STARTUP(struct Curl_easy *data, + struct ssh_conn *sshc) { struct connectdata *conn = data->conn; int rc = ssh_connect(sshc->ssh_session); @@ -657,16 +898,16 @@ static int myssh_state_startup(struct Curl_easy *data, } else if(rc != SSH_OK) { failf(data, "Failure establishing ssh session"); - MOVE_TO_ERROR_STATE(CURLE_FAILED_INIT); + rc = myssh_to_ERROR(data, sshc, CURLE_FAILED_INIT); } else - myssh_state(data, sshc, SSH_HOSTKEY); + myssh_to(data, sshc, SSH_HOSTKEY); return rc; } -static int myssh_state_authlist(struct Curl_easy *data, - struct ssh_conn *sshc) +static int myssh_in_AUTHLIST(struct Curl_easy *data, + struct ssh_conn *sshc) { int rc; sshc->authed = FALSE; @@ -678,11 +919,11 @@ static int myssh_state_authlist(struct Curl_easy *data, if(rc == SSH_AUTH_SUCCESS) { sshc->authed = TRUE; infof(data, "Authenticated with none"); - myssh_state(data, sshc, SSH_AUTH_DONE); + myssh_to(data, sshc, SSH_AUTH_DONE); return rc; } else if(rc == SSH_AUTH_ERROR) { - MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); + rc = myssh_to_ERROR(data, sshc, CURLE_LOGIN_DENIED); return rc; } @@ -699,31 +940,31 @@ static int myssh_state_authlist(struct Curl_easy *data, sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD ? "password": ""); if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) { - myssh_state(data, sshc, SSH_AUTH_PKEY_INIT); + myssh_to(data, sshc, SSH_AUTH_PKEY_INIT); infof(data, "Authentication using SSH public key file"); } else if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { - myssh_state(data, sshc, SSH_AUTH_GSSAPI); + myssh_to(data, sshc, SSH_AUTH_GSSAPI); } else if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { - myssh_state(data, sshc, SSH_AUTH_KEY_INIT); + myssh_to(data, sshc, SSH_AUTH_KEY_INIT); } else if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { - myssh_state(data, sshc, SSH_AUTH_PASS_INIT); + myssh_to(data, sshc, SSH_AUTH_PASS_INIT); } else { /* unsupported authentication method */ - MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); + rc = myssh_to_ERROR(data, sshc, CURLE_LOGIN_DENIED); } return rc; } -static int myssh_state_auth_pkey_init(struct Curl_easy *data, - struct ssh_conn *sshc) +static int myssh_in_AUTH_PKEY_INIT(struct Curl_easy *data, + struct ssh_conn *sshc) { int rc; if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY)) { - MOVE_TO_GSSAPI_AUTH; - return 0; + rc = myssh_to_GSSAPI_AUTH(data, sshc); + return rc; } /* Two choices, (1) private key was given on CMD, @@ -736,7 +977,7 @@ static int myssh_state_auth_pkey_init(struct Curl_easy *data, return SSH_AGAIN; if(rc != SSH_OK) { - MOVE_TO_GSSAPI_AUTH; + rc = myssh_to_GSSAPI_AUTH(data, sshc); return rc; } } @@ -748,11 +989,11 @@ static int myssh_state_auth_pkey_init(struct Curl_easy *data, if(rc != SSH_OK) { failf(data, "Could not load private key file %s", data->set.str[STRING_SSH_PRIVATE_KEY]); - MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); + rc = myssh_to_ERROR(data, sshc, CURLE_LOGIN_DENIED); return rc; } - myssh_state(data, sshc, SSH_AUTH_PKEY); + myssh_to(data, sshc, SSH_AUTH_PKEY); } else { rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL, @@ -764,18 +1005,131 @@ static int myssh_state_auth_pkey_init(struct Curl_easy *data, rc = SSH_OK; sshc->authed = TRUE; infof(data, "Completed public key authentication"); - myssh_state(data, sshc, SSH_AUTH_DONE); + myssh_to(data, sshc, SSH_AUTH_DONE); return rc; } - MOVE_TO_GSSAPI_AUTH; + rc = myssh_to_GSSAPI_AUTH(data, sshc); } return rc; } -static int myssh_state_upload_init(struct Curl_easy *data, - struct ssh_conn *sshc, - struct SSHPROTO *sshp) +static int myssh_in_AUTH_PKEY(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = ssh_userauth_publickey(sshc->ssh_session, NULL, sshc->privkey); + if(rc == SSH_AUTH_AGAIN) + return SSH_AGAIN; + else if(rc == SSH_AUTH_SUCCESS) { + sshc->authed = TRUE; + infof(data, "Completed public key authentication"); + myssh_to(data, sshc, SSH_AUTH_DONE); + return SSH_OK; + } + else { + infof(data, "Failed public key authentication (rc: %d)", rc); + return myssh_to_GSSAPI_AUTH(data, sshc); + } +} + +static int myssh_in_AUTH_GSSAPI(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc; + if(!(data->set.ssh_auth_types & CURLSSH_AUTH_GSSAPI)) + return myssh_to_KEY_AUTH(data, sshc); + + rc = ssh_userauth_gssapi(sshc->ssh_session); + if(rc == SSH_AUTH_AGAIN) + return SSH_AGAIN; + + if(rc == SSH_AUTH_SUCCESS) { + sshc->authed = TRUE; + infof(data, "Completed gssapi authentication"); + myssh_to(data, sshc, SSH_AUTH_DONE); + return SSH_OK; + } + + return myssh_to_KEY_AUTH(data, sshc); +} + +static int myssh_in_AUTH_KEY_INIT(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) { + myssh_to(data, sshc, SSH_AUTH_KEY); + return SSH_NO_ERROR; + } + return myssh_to_PASSWD_AUTH(data, sshc); +} + +static int myssh_in_AUTH_KEY(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + /* keyboard-interactive authentication */ + int rc = myssh_auth_interactive(data->conn, sshc); + if(rc == SSH_AGAIN) + return rc; + else if(rc == SSH_OK) { + sshc->authed = TRUE; + infof(data, "completed keyboard interactive authentication"); + myssh_to(data, sshc, SSH_AUTH_DONE); + return SSH_NO_ERROR; + } + else + return myssh_to_PASSWD_AUTH(data, sshc); +} + +static int myssh_in_AUTH_PASS_INIT(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD)) + return myssh_to_ERROR(data, sshc, CURLE_LOGIN_DENIED); + myssh_to(data, sshc, SSH_AUTH_PASS); + return SSH_NO_ERROR; +} + +static int myssh_in_AUTH_PASS(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc = ssh_userauth_password(sshc->ssh_session, NULL, data->conn->passwd); + if(rc == SSH_AUTH_AGAIN) + return SSH_AGAIN; + else if(rc == SSH_AUTH_SUCCESS) { + sshc->authed = TRUE; + infof(data, "Completed password authentication"); + myssh_to(data, sshc, SSH_AUTH_DONE); + return SSH_NO_ERROR; + } + return myssh_to_ERROR(data, sshc, CURLE_LOGIN_DENIED); +} + +static int myssh_in_AUTH_DONE(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(!sshc->authed) { + failf(data, "Authentication failure"); + return myssh_to_ERROR(data, sshc, CURLE_LOGIN_DENIED); + } + + /* At this point we have an authenticated ssh session. */ + infof(data, "Authentication complete"); + Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */ + data->conn->sockfd = data->conn->sock[FIRSTSOCKET]; + data->conn->writesockfd = CURL_SOCKET_BAD; + + if(data->conn->handler->protocol == CURLPROTO_SFTP) { + myssh_to(data, sshc, SSH_SFTP_INIT); + return SSH_NO_ERROR; + } + infof(data, "SSH CONNECT phase done"); + myssh_to(data, sshc, SSH_STOP); + return SSH_NO_ERROR; +} + +static int myssh_in_UPLOAD_INIT(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) { int flags; int rc = 0; @@ -789,7 +1143,7 @@ static int myssh_state_upload_init(struct Curl_easy *data, curl_off_t size = attrs->size; if(size < 0) { failf(data, "Bad file size (%" FMT_OFF_T ")", size); - MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME); + rc = myssh_to_ERROR(data, sshc, CURLE_BAD_DOWNLOAD_RESUME); return rc; } data->state.resume_from = attrs->size; @@ -827,11 +1181,11 @@ static int myssh_state_upload_init(struct Curl_easy *data, /* try to create the path remotely */ rc = 0; sshc->secondCreateDirs = 1; - myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_INIT); + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_INIT); return rc; } else { - MOVE_TO_SFTP_CLOSE_STATE(); + rc = myssh_to_SFTP_CLOSE(data, sshc); return rc; } } @@ -853,7 +1207,7 @@ static int myssh_state_upload_init(struct Curl_easy *data, if(seekerr != CURL_SEEKFUNC_CANTSEEK) { failf(data, "Could not seek stream"); - MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST); + rc = myssh_to_ERROR(data, sshc, CURLE_FTP_COULDNT_USE_REST); return rc; } /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ @@ -873,7 +1227,7 @@ static int myssh_state_upload_init(struct Curl_easy *data, /* this checks for greater-than only to make sure that the CURL_READFUNC_ABORT return code still aborts */ failf(data, "Failed to read data"); - MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST); + rc = myssh_to_ERROR(data, sshc, CURLE_FTP_COULDNT_USE_REST); return rc; } } while(passed < data->state.resume_from); @@ -888,7 +1242,7 @@ static int myssh_state_upload_init(struct Curl_easy *data, rc = sftp_seek64(sshc->sftp_file, data->state.resume_from); if(rc) { - MOVE_TO_SFTP_CLOSE_STATE(); + rc = myssh_to_SFTP_CLOSE(data, sshc); return rc; } } @@ -906,24 +1260,39 @@ static int myssh_state_upload_init(struct Curl_easy *data, figure out a "real" bitmask */ sshc->orig_waitfor = data->req.keepon; - /* we want to use the _sending_ function even when the socket turns - out readable as the underlying libssh sftp send function will deal - with both accordingly */ - data->state.select_bits = CURL_CSELECT_OUT; - /* since we do not really wait for anything at this point, we want the - state machine to move on as soon as possible so we set a very short - timeout here */ - Curl_expire(data, 0, EXPIRE_RUN_NOW); + state machine to move on as soon as possible so we mark this as dirty */ + Curl_multi_mark_dirty(data); #if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0) sshc->sftp_send_state = 0; #endif - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return rc; } -static int myssh_state_sftp_download_stat(struct Curl_easy *data, - struct ssh_conn *sshc) +static int myssh_in_SFTP_DOWNLOAD_INIT(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + /* Work on getting the specified file */ + if(sshc->sftp_file) + sftp_close(sshc->sftp_file); + + sshc->sftp_file = sftp_open(sshc->sftp_session, sshp->path, + O_RDONLY, (mode_t)data->set.new_file_perms); + if(!sshc->sftp_file) { + failf(data, "Could not open remote file for reading: %s", + ssh_get_error(sshc->ssh_session)); + + return myssh_to_SFTP_CLOSE(data, sshc); + } + sftp_file_set_nonblocking(sshc->sftp_file); + myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_STAT); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_DOWNLOAD_STAT(struct Curl_easy *data, + struct ssh_conn *sshc) { curl_off_t size; int rc = 0; @@ -951,7 +1320,7 @@ static int myssh_state_sftp_download_stat(struct Curl_easy *data, if(size < 0) { failf(data, "Bad file size (%" FMT_OFF_T ")", size); - MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME); + rc = myssh_to_ERROR(data, sshc, CURLE_BAD_DOWNLOAD_RESUME); return rc; } if(data->state.use_range) { @@ -961,7 +1330,7 @@ static int myssh_state_sftp_download_stat(struct Curl_easy *data, from_t = curlx_str_number(&p, &from, CURL_OFF_T_MAX); if(from_t == STRE_OVERFLOW) { - MOVE_TO_ERROR_STATE(CURLE_RANGE_ERROR); + rc = myssh_to_ERROR(data, sshc, CURLE_RANGE_ERROR); return rc; } curlx_str_passblanks(&p); @@ -985,7 +1354,7 @@ static int myssh_state_sftp_download_stat(struct Curl_easy *data, if(from > size) { failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" FMT_OFF_T ")", from, size); - MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME); + rc = myssh_to_ERROR(data, sshc, CURLE_BAD_DOWNLOAD_RESUME); return rc; } if(from > to) { @@ -994,7 +1363,7 @@ static int myssh_state_sftp_download_stat(struct Curl_easy *data, } else { if((to - from) == CURL_OFF_T_MAX) { - MOVE_TO_ERROR_STATE(CURLE_RANGE_ERROR); + rc = myssh_to_ERROR(data, sshc, CURLE_RANGE_ERROR); return rc; } size = to - from + 1; @@ -1002,7 +1371,7 @@ static int myssh_state_sftp_download_stat(struct Curl_easy *data, rc = sftp_seek64(sshc->sftp_file, from); if(rc) { - MOVE_TO_SFTP_CLOSE_STATE(); + rc = myssh_to_SFTP_CLOSE(data, sshc); return rc; } } @@ -1018,7 +1387,7 @@ static int myssh_state_sftp_download_stat(struct Curl_easy *data, if((curl_off_t)size < -data->state.resume_from) { failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" FMT_OFF_T ")", data->state.resume_from, size); - MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME); + rc = myssh_to_ERROR(data, sshc, CURLE_BAD_DOWNLOAD_RESUME); return rc; } /* download from where? */ @@ -1029,7 +1398,7 @@ static int myssh_state_sftp_download_stat(struct Curl_easy *data, failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%" FMT_OFF_T ")", data->state.resume_from, size); - MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME); + rc = myssh_to_ERROR(data, sshc, CURLE_BAD_DOWNLOAD_RESUME); return rc; } } @@ -1041,7 +1410,7 @@ static int myssh_state_sftp_download_stat(struct Curl_easy *data, rc = sftp_seek64(sshc->sftp_file, data->state.resume_from); if(rc) { - MOVE_TO_SFTP_CLOSE_STATE(); + rc = myssh_to_SFTP_CLOSE(data, sshc); return rc; } } @@ -1051,7 +1420,7 @@ static int myssh_state_sftp_download_stat(struct Curl_easy *data, /* no data to transfer */ Curl_xfer_setup_nop(data); infof(data, "File already completely downloaded"); - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return rc; } Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE); @@ -1059,17 +1428,468 @@ static int myssh_state_sftp_download_stat(struct Curl_easy *data, /* not set by Curl_xfer_setup to preserve keepon bits */ data->conn->writesockfd = data->conn->sockfd; - /* we want to use the _receiving_ function even when the socket turns - out writableable as the underlying libssh recv function will deal - with both accordingly */ - data->state.select_bits = CURL_CSELECT_IN; - sshc->sftp_recv_state = 0; - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); return rc; } +static int myssh_in_SFTP_CLOSE(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + if(sshc->sftp_file) { + sftp_close(sshc->sftp_file); + sshc->sftp_file = NULL; + } + Curl_safefree(sshp->path); + + DEBUGF(infof(data, "SFTP DONE done")); + + /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT + After nextstate is executed, the control should come back to + SSH_SFTP_CLOSE to pass the correct result back */ + if(sshc->nextstate != SSH_NO_STATE && + sshc->nextstate != SSH_SFTP_CLOSE) { + myssh_to(data, sshc, sshc->nextstate); + sshc->nextstate = SSH_SFTP_CLOSE; + } + else { + myssh_to(data, sshc, SSH_STOP); + } + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_SHUTDOWN(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + /* during times we get here due to a broken transfer and then the + sftp_handle might not have been taken down so make sure that is done + before we proceed */ + ssh_set_blocking(sshc->ssh_session, 0); +#if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0) + if(sshc->sftp_aio) { + sftp_aio_free(sshc->sftp_aio); + sshc->sftp_aio = NULL; + } +#endif + + if(sshc->sftp_file) { + sftp_close(sshc->sftp_file); + sshc->sftp_file = NULL; + } + + if(sshc->sftp_session) { + sftp_free(sshc->sftp_session); + sshc->sftp_session = NULL; + } + + SSH_STRING_FREE_CHAR(sshc->homedir); + + myssh_to(data, sshc, SSH_SESSION_DISCONNECT); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_INIT(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + int rc; + ssh_set_blocking(sshc->ssh_session, 1); + + sshc->sftp_session = sftp_new(sshc->ssh_session); + if(!sshc->sftp_session) { + failf(data, "Failure initializing sftp session: %s", + ssh_get_error(sshc->ssh_session)); + return myssh_to_ERROR(data, sshc, CURLE_COULDNT_CONNECT); + } + + rc = sftp_init(sshc->sftp_session); + if(rc != SSH_OK) { + failf(data, "Failure initializing sftp session: %s", + ssh_get_error(sshc->ssh_session)); + return myssh_to_ERROR(data, sshc, sftp_error_to_CURLE(SSH_FX_FAILURE)); + } + myssh_to(data, sshc, SSH_SFTP_REALPATH); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_REALPATH(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + /* Get the "home" directory */ + sshc->homedir = sftp_canonicalize_path(sshc->sftp_session, "."); + if(!sshc->homedir) + return myssh_to_ERROR(data, sshc, CURLE_COULDNT_CONNECT); + + free(data->state.most_recent_ftp_entrypath); + data->state.most_recent_ftp_entrypath = strdup(sshc->homedir); + if(!data->state.most_recent_ftp_entrypath) + return myssh_to_ERROR(data, sshc, CURLE_OUT_OF_MEMORY); + + /* This is the last step in the SFTP connect phase. Do note that while + we get the homedir here, we get the "workingpath" in the DO action + since the homedir will remain the same between request but the + working path will not. */ + DEBUGF(infof(data, "SSH CONNECT phase done")); + myssh_to(data, sshc, SSH_STOP); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_INIT(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + CURLcode result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); + if(result) { + sshc->actualcode = result; + myssh_to(data, sshc, SSH_STOP); + } + else if(data->set.quote) { + infof(data, "Sending quote commands"); + sshc->quote_item = data->set.quote; + myssh_to(data, sshc, SSH_SFTP_QUOTE); + } + else + myssh_to(data, sshc, SSH_SFTP_GETINFO); + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_POSTQUOTE_INIT(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + if(data->set.postquote) { + infof(data, "Sending quote commands"); + sshc->quote_item = data->set.postquote; + myssh_to(data, sshc, SSH_SFTP_QUOTE); + } + else { + myssh_to(data, sshc, SSH_STOP); + } + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE(struct Curl_easy *data, + struct ssh_conn *sshc, + struct SSHPROTO *sshp) +{ + const char *cp; + CURLcode result; + + /* + * Support some of the "FTP" commands + */ + char *cmd = sshc->quote_item->data; + sshc->acceptfail = FALSE; + + /* if a command starts with an asterisk, which a legal SFTP command never + can, the command will be allowed to fail without it causing any + aborts or cancels etc. It will cause libcurl to act as if the command + is successful, whatever the server responds. */ + + if(cmd[0] == '*') { + cmd++; + sshc->acceptfail = TRUE; + } + + if(curl_strequal("pwd", cmd)) { + /* output debug output if that is requested */ + char *tmp = aprintf("257 \"%s\" is current directory.\n", sshp->path); + if(!tmp) { + sshc->actualcode = CURLE_OUT_OF_MEMORY; + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + return SSH_NO_ERROR; + } + Curl_debug(data, CURLINFO_HEADER_OUT, "PWD\n", 4); + Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp)); + + /* this sends an FTP-like "header" to the header callback so that the + current directory can be read very similar to how it is read when + using ordinary FTP. */ + result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); + free(tmp); + if(result) { + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + } + else + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); + return SSH_NO_ERROR; + } + + /* + * the arguments following the command must be separated from the + * command with a space so we can check for it unconditionally + */ + cp = strchr(cmd, ' '); + if(!cp) { + failf(data, "Syntax error in SFTP command. Supply parameter(s)"); + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return SSH_NO_ERROR; + } + + /* + * also, every command takes at least one argument so we get that + * first argument right now + */ + result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error: Bad first parameter"); + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + return SSH_NO_ERROR; + } + + /* + * SFTP is a binary protocol, so we do not send text commands + * to the server. Instead, we scan for commands used by + * OpenSSH's sftp program and call the appropriate libssh + * functions. + */ + if(!strncmp(cmd, "chgrp ", 6) || + !strncmp(cmd, "chmod ", 6) || + !strncmp(cmd, "chown ", 6) || + !strncmp(cmd, "atime ", 6) || + !strncmp(cmd, "mtime ", 6)) { + /* attribute change */ + + /* sshc->quote_path1 contains the mode to set */ + /* get the destination */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error in chgrp/chmod/chown/atime/mtime: " + "Bad second parameter"); + Curl_safefree(sshc->quote_path1); + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + return SSH_NO_ERROR; + } + sshc->quote_attrs = NULL; + myssh_to(data, sshc, SSH_SFTP_QUOTE_STAT); + return SSH_NO_ERROR; + } + if(!strncmp(cmd, "ln ", 3) || + !strncmp(cmd, "symlink ", 8)) { + /* symbolic linking */ + /* sshc->quote_path1 is the source */ + /* get the destination */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error in ln/symlink: Bad second parameter"); + Curl_safefree(sshc->quote_path1); + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + return SSH_NO_ERROR; + } + myssh_to(data, sshc, SSH_SFTP_QUOTE_SYMLINK); + return SSH_NO_ERROR; + } + else if(!strncmp(cmd, "mkdir ", 6)) { + /* create dir */ + myssh_to(data, sshc, SSH_SFTP_QUOTE_MKDIR); + return SSH_NO_ERROR; + } + else if(!strncmp(cmd, "rename ", 7)) { + /* rename file */ + /* first param is the source path */ + /* second param is the dest. path */ + result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); + if(result) { + if(result == CURLE_OUT_OF_MEMORY) + failf(data, "Out of memory"); + else + failf(data, "Syntax error in rename: Bad second parameter"); + Curl_safefree(sshc->quote_path1); + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + return SSH_NO_ERROR; + } + myssh_to(data, sshc, SSH_SFTP_QUOTE_RENAME); + return SSH_NO_ERROR; + } + else if(!strncmp(cmd, "rmdir ", 6)) { + /* delete dir */ + myssh_to(data, sshc, SSH_SFTP_QUOTE_RMDIR); + return SSH_NO_ERROR; + } + else if(!strncmp(cmd, "rm ", 3)) { + myssh_to(data, sshc, SSH_SFTP_QUOTE_UNLINK); + return SSH_NO_ERROR; + } +#ifdef HAS_STATVFS_SUPPORT + else if(!strncmp(cmd, "statvfs ", 8)) { + myssh_to(data, sshc, SSH_SFTP_QUOTE_STATVFS); + return SSH_NO_ERROR; + } +#endif + + failf(data, "Unknown SFTP command"); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_NEXT_QUOTE(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + + sshc->quote_item = sshc->quote_item->next; + + if(sshc->quote_item) { + myssh_to(data, sshc, SSH_SFTP_QUOTE); + } + else { + if(sshc->nextstate != SSH_NO_STATE) { + myssh_to(data, sshc, sshc->nextstate); + sshc->nextstate = SSH_NO_STATE; + } + else { + myssh_to(data, sshc, SSH_SFTP_GETINFO); + } + } + return SSH_NO_ERROR; +} + +static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data, + struct ssh_conn *sshc) +{ + char *cmd = sshc->quote_item->data; + sshc->acceptfail = FALSE; + + /* if a command starts with an asterisk, which a legal SFTP command never + can, the command will be allowed to fail without it causing any + aborts or cancels etc. It will cause libcurl to act as if the command + is successful, whatever the server responds. */ + + if(cmd[0] == '*') { + cmd++; + sshc->acceptfail = TRUE; + } + + /* We read the file attributes, store them in sshc->quote_attrs + * and modify them accordingly to command. Then we switch to + * QUOTE_SETSTAT state to write new ones. + */ + + if(sshc->quote_attrs) + sftp_attributes_free(sshc->quote_attrs); + sshc->quote_attrs = sftp_stat(sshc->sftp_session, sshc->quote_path2); + if(!sshc->quote_attrs) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Attempt to get SFTP stats failed: %d", + sftp_get_error(sshc->sftp_session)); + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return SSH_NO_ERROR; + } + + /* Now set the new attributes... */ + if(!strncmp(cmd, "chgrp", 5)) { + const char *p = sshc->quote_path1; + curl_off_t gid; + (void)curlx_str_number(&p, &gid, UINT_MAX); + sshc->quote_attrs->gid = (uint32_t)gid; + if(sshc->quote_attrs->gid == 0 && !ISDIGIT(sshc->quote_path1[0]) && + !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Syntax error: chgrp gid not a number"); + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return SSH_NO_ERROR; + } + sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID; + } + else if(!strncmp(cmd, "chmod", 5)) { + curl_off_t perms; + const char *p = sshc->quote_path1; + if(curlx_str_octal(&p, &perms, 07777)) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Syntax error: chmod permissions not a number"); + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return SSH_NO_ERROR; + } + sshc->quote_attrs->permissions = (mode_t)perms; + sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_PERMISSIONS; + } + else if(!strncmp(cmd, "chown", 5)) { + const char *p = sshc->quote_path1; + curl_off_t uid; + (void)curlx_str_number(&p, &uid, UINT_MAX); + if(sshc->quote_attrs->uid == 0 && !ISDIGIT(sshc->quote_path1[0]) && + !sshc->acceptfail) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + failf(data, "Syntax error: chown uid not a number"); + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return SSH_NO_ERROR; + } + sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID; + } + else if(!strncmp(cmd, "atime", 5) || + !strncmp(cmd, "mtime", 5)) { + time_t date = Curl_getdate_capped(sshc->quote_path1); + bool fail = FALSE; + if(date == -1) { + failf(data, "incorrect date format for %.*s", 5, cmd); + fail = TRUE; + } +#if SIZEOF_TIME_T > 4 + else if(date > 0xffffffff) { + failf(data, "date overflow"); + fail = TRUE; /* avoid setting a capped time */ + } +#endif + if(fail) { + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + myssh_to(data, sshc, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = CURLE_QUOTE_ERROR; + return SSH_NO_ERROR; + } + if(!strncmp(cmd, "atime", 5)) + sshc->quote_attrs->atime = (uint32_t)date; + else /* mtime */ + sshc->quote_attrs->mtime = (uint32_t)date; + + sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_ACMODTIME; + } + + /* Now send the completed structure... */ + myssh_to(data, sshc, SSH_SFTP_QUOTE_SETSTAT); + return SSH_NO_ERROR; +} + /* * ssh_statemach_act() runs the SSH state machine as far as it can without * blocking and without reaching the end. The data the pointer 'block' points @@ -1083,253 +1903,76 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, { CURLcode result = CURLE_OK; struct connectdata *conn = data->conn; - curl_socket_t sock = conn->sock[FIRSTSOCKET]; int rc = SSH_NO_ERROR, err; const char *err_msg; - *block = 0; /* we are not blocking by default */ + *block = FALSE; /* we are not blocking by default */ do { switch(sshc->state) { case SSH_INIT: myssh_state_init(data, sshc); FALLTHROUGH(); - case SSH_S_STARTUP: - rc = myssh_state_startup(data, sshc); + rc = myssh_in_S_STARTUP(data, sshc); if(rc) break; FALLTHROUGH(); case SSH_HOSTKEY: rc = myssh_is_known(data, sshc); if(rc != SSH_OK) { - MOVE_TO_ERROR_STATE(CURLE_PEER_FAILED_VERIFICATION); + rc = myssh_to_ERROR(data, sshc, CURLE_PEER_FAILED_VERIFICATION); break; } - - myssh_state(data, sshc, SSH_AUTHLIST); + myssh_to(data, sshc, SSH_AUTHLIST); FALLTHROUGH(); case SSH_AUTHLIST: - rc = myssh_state_authlist(data, sshc); + rc = myssh_in_AUTHLIST(data, sshc); break; case SSH_AUTH_PKEY_INIT: - rc = myssh_state_auth_pkey_init(data, sshc); + rc = myssh_in_AUTH_PKEY_INIT(data, sshc); break; case SSH_AUTH_PKEY: - rc = ssh_userauth_publickey(sshc->ssh_session, NULL, sshc->privkey); - if(rc == SSH_AUTH_AGAIN) { - rc = SSH_AGAIN; - break; - } - - if(rc == SSH_AUTH_SUCCESS) { - sshc->authed = TRUE; - infof(data, "Completed public key authentication"); - myssh_state(data, sshc, SSH_AUTH_DONE); - break; - } - else { - infof(data, "Failed public key authentication (rc: %d)", rc); - MOVE_TO_GSSAPI_AUTH; - } + rc = myssh_in_AUTH_PKEY(data, sshc); break; - case SSH_AUTH_GSSAPI: - if(!(data->set.ssh_auth_types & CURLSSH_AUTH_GSSAPI)) { - MOVE_TO_KEY_AUTH; - break; - } - - rc = ssh_userauth_gssapi(sshc->ssh_session); - if(rc == SSH_AUTH_AGAIN) { - rc = SSH_AGAIN; - break; - } - - if(rc == SSH_AUTH_SUCCESS) { - rc = SSH_OK; - sshc->authed = TRUE; - infof(data, "Completed gssapi authentication"); - myssh_state(data, sshc, SSH_AUTH_DONE); - break; - } - - MOVE_TO_KEY_AUTH; + rc = myssh_in_AUTH_GSSAPI(data, sshc); break; - case SSH_AUTH_KEY_INIT: - if(data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) { - myssh_state(data, sshc, SSH_AUTH_KEY); - } - else { - MOVE_TO_PASSWD_AUTH; - } + rc = myssh_in_AUTH_KEY_INIT(data, sshc); break; - case SSH_AUTH_KEY: - /* keyboard-interactive authentication */ - rc = myssh_auth_interactive(conn, sshc); - if(rc == SSH_AGAIN) { - break; - } - if(rc == SSH_OK) { - sshc->authed = TRUE; - infof(data, "completed keyboard interactive authentication"); - myssh_state(data, sshc, SSH_AUTH_DONE); - } - else { - MOVE_TO_PASSWD_AUTH; - } + rc = myssh_in_AUTH_KEY(data, sshc); break; - case SSH_AUTH_PASS_INIT: - if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD)) { - MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); - break; - } - myssh_state(data, sshc, SSH_AUTH_PASS); - FALLTHROUGH(); - + rc = myssh_in_AUTH_PASS_INIT(data, sshc); + break; case SSH_AUTH_PASS: - rc = ssh_userauth_password(sshc->ssh_session, NULL, conn->passwd); - if(rc == SSH_AUTH_AGAIN) { - rc = SSH_AGAIN; - break; - } - - if(rc == SSH_AUTH_SUCCESS) { - sshc->authed = TRUE; - infof(data, "Completed password authentication"); - myssh_state(data, sshc, SSH_AUTH_DONE); - } - else { - MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); - } + rc = myssh_in_AUTH_PASS(data, sshc); break; - case SSH_AUTH_DONE: - if(!sshc->authed) { - failf(data, "Authentication failure"); - MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); - break; - } - - /* - * At this point we have an authenticated ssh session. - */ - infof(data, "Authentication complete"); - - Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */ - - conn->sockfd = sock; - conn->writesockfd = CURL_SOCKET_BAD; - - if(conn->handler->protocol == CURLPROTO_SFTP) { - myssh_state(data, sshc, SSH_SFTP_INIT); - break; - } - infof(data, "SSH CONNECT phase done"); - myssh_state(data, sshc, SSH_STOP); + rc = myssh_in_AUTH_DONE(data, sshc); break; - case SSH_SFTP_INIT: - ssh_set_blocking(sshc->ssh_session, 1); - - sshc->sftp_session = sftp_new(sshc->ssh_session); - if(!sshc->sftp_session) { - failf(data, "Failure initializing sftp session: %s", - ssh_get_error(sshc->ssh_session)); - MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT); - break; - } - - rc = sftp_init(sshc->sftp_session); - if(rc != SSH_OK) { - failf(data, "Failure initializing sftp session: %s", - ssh_get_error(sshc->ssh_session)); - MOVE_TO_ERROR_STATE(sftp_error_to_CURLE(SSH_FX_FAILURE)); - break; - } - myssh_state(data, sshc, SSH_SFTP_REALPATH); - FALLTHROUGH(); + rc = myssh_in_SFTP_INIT(data, sshc); + break; case SSH_SFTP_REALPATH: - /* - * Get the "home" directory - */ - sshc->homedir = sftp_canonicalize_path(sshc->sftp_session, "."); - if(!sshc->homedir) { - MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT); - break; - } - free(data->state.most_recent_ftp_entrypath); - data->state.most_recent_ftp_entrypath = strdup(sshc->homedir); - if(!data->state.most_recent_ftp_entrypath) - return CURLE_OUT_OF_MEMORY; - - /* This is the last step in the SFTP connect phase. Do note that while - we get the homedir here, we get the "workingpath" in the DO action - since the homedir will remain the same between request but the - working path will not. */ - DEBUGF(infof(data, "SSH CONNECT phase done")); - myssh_state(data, sshc, SSH_STOP); + rc = myssh_in_SFTP_REALPATH(data, sshc); break; - case SSH_SFTP_QUOTE_INIT: - result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); - if(result) { - sshc->actualcode = result; - myssh_state(data, sshc, SSH_STOP); - break; - } - - if(data->set.quote) { - infof(data, "Sending quote commands"); - sshc->quote_item = data->set.quote; - myssh_state(data, sshc, SSH_SFTP_QUOTE); - } - else { - myssh_state(data, sshc, SSH_SFTP_GETINFO); - } + rc = myssh_in_SFTP_QUOTE_INIT(data, sshc, sshp); break; - case SSH_SFTP_POSTQUOTE_INIT: - if(data->set.postquote) { - infof(data, "Sending quote commands"); - sshc->quote_item = data->set.postquote; - myssh_state(data, sshc, SSH_SFTP_QUOTE); - } - else { - myssh_state(data, sshc, SSH_STOP); - } + rc = myssh_in_SFTP_POSTQUOTE_INIT(data, sshc); break; - case SSH_SFTP_QUOTE: - /* Send any quote commands */ - sftp_quote(data, sshc, sshp); + rc = myssh_in_SFTP_QUOTE(data, sshc, sshp); break; - case SSH_SFTP_NEXT_QUOTE: - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - - sshc->quote_item = sshc->quote_item->next; - - if(sshc->quote_item) { - myssh_state(data, sshc, SSH_SFTP_QUOTE); - } - else { - if(sshc->nextstate != SSH_NO_STATE) { - myssh_state(data, sshc, sshc->nextstate); - sshc->nextstate = SSH_NO_STATE; - } - else { - myssh_state(data, sshc, SSH_SFTP_GETINFO); - } - } + rc = myssh_in_SFTP_NEXT_QUOTE(data, sshc); break; - case SSH_SFTP_QUOTE_STAT: - sftp_quote_stat(data, sshc); + rc = myssh_in_SFTP_QUOTE_STAT(data, sshc); break; case SSH_SFTP_QUOTE_SETSTAT: @@ -1340,7 +1983,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, Curl_safefree(sshc->quote_path2); failf(data, "Attempt to set SFTP stats failed: %s", ssh_get_error(sshc->ssh_session)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; /* sshc->actualcode = sftp_error_to_CURLE(err); @@ -1348,7 +1991,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, * the error the libssh2 backend is returning */ break; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); break; case SSH_SFTP_QUOTE_SYMLINK: @@ -1359,12 +2002,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, Curl_safefree(sshc->quote_path2); failf(data, "symlink command failed: %s", ssh_get_error(sshc->ssh_session)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; break; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); break; case SSH_SFTP_QUOTE_MKDIR: @@ -1374,12 +2017,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, Curl_safefree(sshc->quote_path1); failf(data, "mkdir command failed: %s", ssh_get_error(sshc->ssh_session)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; break; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); break; case SSH_SFTP_QUOTE_RENAME: @@ -1390,12 +2033,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, Curl_safefree(sshc->quote_path2); failf(data, "rename command failed: %s", ssh_get_error(sshc->ssh_session)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; break; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); break; case SSH_SFTP_QUOTE_RMDIR: @@ -1404,12 +2047,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, Curl_safefree(sshc->quote_path1); failf(data, "rmdir command failed: %s", ssh_get_error(sshc->ssh_session)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; break; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); break; case SSH_SFTP_QUOTE_UNLINK: @@ -1418,84 +2061,28 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, Curl_safefree(sshc->quote_path1); failf(data, "rm command failed: %s", ssh_get_error(sshc->ssh_session)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; break; } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + myssh_to(data, sshc, SSH_SFTP_NEXT_QUOTE); break; case SSH_SFTP_QUOTE_STATVFS: - { - sftp_statvfs_t statvfs; - - statvfs = sftp_statvfs(sshc->sftp_session, sshc->quote_path1); - if(!statvfs && !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - failf(data, "statvfs command failed: %s", - ssh_get_error(sshc->ssh_session)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - break; - } - else if(statvfs) { - #ifdef _MSC_VER - #define CURL_LIBSSH_VFS_SIZE_MASK "I64u" - #else - #define CURL_LIBSSH_VFS_SIZE_MASK PRIu64 - #endif - char *tmp = aprintf("statvfs:\n" - "f_bsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_frsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_blocks: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_bfree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_bavail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_files: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_ffree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_favail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_fsid: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_flag: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" - "f_namemax: %" CURL_LIBSSH_VFS_SIZE_MASK "\n", - statvfs->f_bsize, statvfs->f_frsize, - statvfs->f_blocks, statvfs->f_bfree, - statvfs->f_bavail, statvfs->f_files, - statvfs->f_ffree, statvfs->f_favail, - statvfs->f_fsid, statvfs->f_flag, - statvfs->f_namemax); - sftp_statvfs_free(statvfs); - - if(!tmp) { - result = CURLE_OUT_OF_MEMORY; - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - break; - } - - result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); - free(tmp); - if(result) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - } - } - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); + rc = myssh_in_SFTP_QUOTE_STATVFS(data, sshc); break; - } case SSH_SFTP_GETINFO: if(data->set.get_filetime) { - myssh_state(data, sshc, SSH_SFTP_FILETIME); + myssh_to(data, sshc, SSH_SFTP_FILETIME); } else { - myssh_state(data, sshc, SSH_SFTP_TRANS_INIT); + myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); } break; - case SSH_SFTP_FILETIME: - { + case SSH_SFTP_FILETIME: { sftp_attributes attrs; attrs = sftp_stat(sshc->sftp_session, sshp->path); @@ -1504,32 +2091,32 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, sftp_attributes_free(attrs); } - myssh_state(data, sshc, SSH_SFTP_TRANS_INIT); + myssh_to(data, sshc, SSH_SFTP_TRANS_INIT); break; } case SSH_SFTP_TRANS_INIT: if(data->state.upload) - myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT); + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); else { if(sshp->path[strlen(sshp->path)-1] == '/') - myssh_state(data, sshc, SSH_SFTP_READDIR_INIT); + myssh_to(data, sshc, SSH_SFTP_READDIR_INIT); else - myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_INIT); + myssh_to(data, sshc, SSH_SFTP_DOWNLOAD_INIT); } break; case SSH_SFTP_UPLOAD_INIT: - rc = myssh_state_upload_init(data, sshc, sshp); + rc = myssh_in_UPLOAD_INIT(data, sshc, sshp); break; case SSH_SFTP_CREATE_DIRS_INIT: if(strlen(sshp->path) > 1) { sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */ - myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS); + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); } else { - myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT); + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); } break; @@ -1539,10 +2126,10 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, *sshc->slash_pos = 0; infof(data, "Creating directory '%s'", sshp->path); - myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR); break; } - myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT); + myssh_to(data, sshc, SSH_SFTP_UPLOAD_INIT); break; case SSH_SFTP_CREATE_DIRS_MKDIR: @@ -1561,254 +2148,47 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, if((err != SSH_FX_FILE_ALREADY_EXISTS) && (err != SSH_FX_FAILURE) && (err != SSH_FX_PERMISSION_DENIED)) { - MOVE_TO_SFTP_CLOSE_STATE(); + rc = myssh_to_SFTP_CLOSE(data, sshc); break; } rc = 0; /* clear rc and continue */ } - myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS); + myssh_to(data, sshc, SSH_SFTP_CREATE_DIRS); break; case SSH_SFTP_READDIR_INIT: - Curl_pgrsSetDownloadSize(data, -1); - if(data->req.no_body) { - myssh_state(data, sshc, SSH_STOP); - break; - } - - /* - * This is a directory that we are trying to get, so produce a directory - * listing - */ - sshc->sftp_dir = sftp_opendir(sshc->sftp_session, - sshp->path); - if(!sshc->sftp_dir) { - failf(data, "Could not open directory for reading: %s", - ssh_get_error(sshc->ssh_session)); - MOVE_TO_SFTP_CLOSE_STATE(); - break; - } - myssh_state(data, sshc, SSH_SFTP_READDIR); + rc = myssh_in_SFTP_READDIR_INIT(data, sshc, sshp); break; - case SSH_SFTP_READDIR: - curlx_dyn_reset(&sshc->readdir_buf); - if(sshc->readdir_attrs) - sftp_attributes_free(sshc->readdir_attrs); - - sshc->readdir_attrs = sftp_readdir(sshc->sftp_session, sshc->sftp_dir); - if(sshc->readdir_attrs) { - sshc->readdir_filename = sshc->readdir_attrs->name; - sshc->readdir_longentry = sshc->readdir_attrs->longname; - sshc->readdir_len = strlen(sshc->readdir_filename); - - if(data->set.list_only) { - char *tmpLine; - - tmpLine = aprintf("%s\n", sshc->readdir_filename); - if(!tmpLine) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } - result = Curl_client_write(data, CLIENTWRITE_BODY, - tmpLine, sshc->readdir_len + 1); - free(tmpLine); - - if(result) { - myssh_state(data, sshc, SSH_STOP); - break; - } - - } - else { - if(curlx_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) { - sshc->actualcode = CURLE_OUT_OF_MEMORY; - myssh_state(data, sshc, SSH_STOP); - break; - } - - if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) && - ((sshc->readdir_attrs->permissions & SSH_S_IFMT) == - SSH_S_IFLNK)) { - sshc->readdir_linkPath = aprintf("%s%s", sshp->path, - sshc->readdir_filename); - - if(!sshc->readdir_linkPath) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } - - myssh_state(data, sshc, SSH_SFTP_READDIR_LINK); - break; - } - myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM); - break; - } - } - else if(sftp_dir_eof(sshc->sftp_dir)) { - myssh_state(data, sshc, SSH_SFTP_READDIR_DONE); - break; - } - else { - failf(data, "Could not open remote file for reading: %s", - ssh_get_error(sshc->ssh_session)); - MOVE_TO_SFTP_CLOSE_STATE(); - break; - } + rc = myssh_in_SFTP_READDIR(data, sshc, sshp); break; - case SSH_SFTP_READDIR_LINK: - if(sshc->readdir_link_attrs) - sftp_attributes_free(sshc->readdir_link_attrs); - - sshc->readdir_link_attrs = sftp_lstat(sshc->sftp_session, - sshc->readdir_linkPath); - if(sshc->readdir_link_attrs == 0) { - failf(data, "Could not read symlink for reading: %s", - ssh_get_error(sshc->ssh_session)); - MOVE_TO_SFTP_CLOSE_STATE(); - break; - } - - if(!sshc->readdir_link_attrs->name) { - sshc->readdir_tmp = sftp_readlink(sshc->sftp_session, - sshc->readdir_linkPath); - if(!sshc->readdir_filename) - sshc->readdir_len = 0; - else - sshc->readdir_len = strlen(sshc->readdir_tmp); - sshc->readdir_longentry = NULL; - sshc->readdir_filename = sshc->readdir_tmp; - } - else { - sshc->readdir_len = strlen(sshc->readdir_link_attrs->name); - sshc->readdir_filename = sshc->readdir_link_attrs->name; - sshc->readdir_longentry = sshc->readdir_link_attrs->longname; - } - - Curl_safefree(sshc->readdir_linkPath); - - if(curlx_dyn_addf(&sshc->readdir_buf, " -> %s", - sshc->readdir_filename)) { - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; - } - - sftp_attributes_free(sshc->readdir_link_attrs); - sshc->readdir_link_attrs = NULL; - sshc->readdir_filename = NULL; - sshc->readdir_longentry = NULL; - - myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM); - FALLTHROUGH(); + rc = myssh_in_SFTP_READDIR_LINK(data, sshc); + break; case SSH_SFTP_READDIR_BOTTOM: - if(curlx_dyn_addn(&sshc->readdir_buf, "\n", 1)) - result = CURLE_OUT_OF_MEMORY; - else - result = Curl_client_write(data, CLIENTWRITE_BODY, - curlx_dyn_ptr(&sshc->readdir_buf), - curlx_dyn_len(&sshc->readdir_buf)); - - ssh_string_free_char(sshc->readdir_tmp); - sshc->readdir_tmp = NULL; - - if(result) { - myssh_state(data, sshc, SSH_STOP); - } - else - myssh_state(data, sshc, SSH_SFTP_READDIR); + rc = myssh_in_SFTP_READDIR_BOTTOM(data, sshc); break; - case SSH_SFTP_READDIR_DONE: - sftp_closedir(sshc->sftp_dir); - sshc->sftp_dir = NULL; - - /* no data to transfer */ - Curl_xfer_setup_nop(data); - myssh_state(data, sshc, SSH_STOP); + rc = myssh_in_SFTP_READDIR_DONE(data, sshc); break; - case SSH_SFTP_DOWNLOAD_INIT: - /* - * Work on getting the specified file - */ - if(sshc->sftp_file) - sftp_close(sshc->sftp_file); - - sshc->sftp_file = sftp_open(sshc->sftp_session, sshp->path, - O_RDONLY, (mode_t)data->set.new_file_perms); - if(!sshc->sftp_file) { - failf(data, "Could not open remote file for reading: %s", - ssh_get_error(sshc->ssh_session)); - - MOVE_TO_SFTP_CLOSE_STATE(); - break; - } - sftp_file_set_nonblocking(sshc->sftp_file); - myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT); + rc = myssh_in_SFTP_DOWNLOAD_INIT(data, sshc, sshp); break; - case SSH_SFTP_DOWNLOAD_STAT: - rc = myssh_state_sftp_download_stat(data, sshc); + rc = myssh_in_SFTP_DOWNLOAD_STAT(data, sshc); break; - case SSH_SFTP_CLOSE: - if(sshc->sftp_file) { - sftp_close(sshc->sftp_file); - sshc->sftp_file = NULL; - } - Curl_safefree(sshp->path); - - DEBUGF(infof(data, "SFTP DONE done")); - - /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT - After nextstate is executed, the control should come back to - SSH_SFTP_CLOSE to pass the correct result back */ - if(sshc->nextstate != SSH_NO_STATE && - sshc->nextstate != SSH_SFTP_CLOSE) { - myssh_state(data, sshc, sshc->nextstate); - sshc->nextstate = SSH_SFTP_CLOSE; - } - else { - myssh_state(data, sshc, SSH_STOP); - result = sshc->actualcode; - } + rc = myssh_in_SFTP_CLOSE(data, sshc, sshp); break; - case SSH_SFTP_SHUTDOWN: - /* during times we get here due to a broken transfer and then the - sftp_handle might not have been taken down so make sure that is done - before we proceed */ - ssh_set_blocking(sshc->ssh_session, 0); -#if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0) - if(sshc->sftp_aio) { - sftp_aio_free(sshc->sftp_aio); - sshc->sftp_aio = NULL; - } -#endif - - if(sshc->sftp_file) { - sftp_close(sshc->sftp_file); - sshc->sftp_file = NULL; - } - - if(sshc->sftp_session) { - sftp_free(sshc->sftp_session); - sshc->sftp_session = NULL; - } - - SSH_STRING_FREE_CHAR(sshc->homedir); - - myssh_state(data, sshc, SSH_SESSION_DISCONNECT); + rc = myssh_in_SFTP_SHUTDOWN(data, sshc); break; case SSH_SCP_TRANS_INIT: result = Curl_getworkingpath(data, sshc->homedir, &sshp->path); if(result) { sshc->actualcode = result; - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); break; } @@ -1819,26 +2199,25 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, if(data->state.infilesize < 0) { failf(data, "SCP requires a known file size for upload"); sshc->actualcode = CURLE_UPLOAD_FAILED; - MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); + rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); break; } sshc->scp_session = ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, sshp->path); - myssh_state(data, sshc, SSH_SCP_UPLOAD_INIT); + myssh_to(data, sshc, SSH_SCP_UPLOAD_INIT); } else { sshc->scp_session = ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, sshp->path); - myssh_state(data, sshc, SSH_SCP_DOWNLOAD_INIT); + myssh_to(data, sshc, SSH_SCP_DOWNLOAD_INIT); } if(!sshc->scp_session) { err_msg = ssh_get_error(sshc->ssh_session); failf(data, "%s", err_msg); - MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); + rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); } - break; case SSH_SCP_UPLOAD_INIT: @@ -1847,7 +2226,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, if(rc != SSH_OK) { err_msg = ssh_get_error(sshc->ssh_session); failf(data, "%s", err_msg); - MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); + rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); break; } @@ -1858,7 +2237,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, if(rc != SSH_OK) { err_msg = ssh_get_error(sshc->ssh_session); failf(data, "%s", err_msg); - MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); + rc = myssh_to_ERROR(data, sshc, CURLE_UPLOAD_FAILED); break; } @@ -1872,12 +2251,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, figure out a "real" bitmask */ sshc->orig_waitfor = data->req.keepon; - /* we want to use the _sending_ function even when the socket turns - out readable as the underlying libssh scp send function will deal - with both accordingly */ - data->state.select_bits = CURL_CSELECT_OUT; - - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); break; @@ -1887,20 +2261,20 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, if(rc != SSH_OK) { err_msg = ssh_get_error(sshc->ssh_session); failf(data, "%s", err_msg); - MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT); + rc = myssh_to_ERROR(data, sshc, CURLE_COULDNT_CONNECT); break; } - myssh_state(data, sshc, SSH_SCP_DOWNLOAD); + myssh_to(data, sshc, SSH_SCP_DOWNLOAD); FALLTHROUGH(); - case SSH_SCP_DOWNLOAD:{ + case SSH_SCP_DOWNLOAD: { curl_off_t bytecount; rc = ssh_scp_pull_request(sshc->scp_session); if(rc != SSH_SCP_REQUEST_NEWFILE) { err_msg = ssh_get_error(sshc->ssh_session); failf(data, "%s", err_msg); - MOVE_TO_ERROR_STATE(CURLE_REMOTE_FILE_NOT_FOUND); + rc = myssh_to_ERROR(data, sshc, CURLE_REMOTE_FILE_NOT_FOUND); break; } @@ -1912,19 +2286,14 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, /* not set by Curl_xfer_setup to preserve keepon bits */ conn->writesockfd = conn->sockfd; - /* we want to use the _receiving_ function even when the socket turns - out writableable as the underlying libssh recv function will deal - with both accordingly */ - data->state.select_bits = CURL_CSELECT_IN; - - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); break; - } + } case SSH_SCP_DONE: if(data->state.upload) - myssh_state(data, sshc, SSH_SCP_SEND_EOF); + myssh_to(data, sshc, SSH_SCP_SEND_EOF); else - myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE); + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); break; case SSH_SCP_SEND_EOF: @@ -1942,7 +2311,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, } } - myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE); + myssh_to(data, sshc, SSH_SCP_CHANNEL_FREE); break; case SSH_SCP_CHANNEL_FREE: @@ -1954,7 +2323,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, ssh_set_blocking(sshc->ssh_session, 0); - myssh_state(data, sshc, SSH_SESSION_DISCONNECT); + myssh_to(data, sshc, SSH_SESSION_DISCONNECT); FALLTHROUGH(); case SSH_SESSION_DISCONNECT: @@ -1985,7 +2354,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, SSH_STRING_FREE_CHAR(sshc->homedir); - myssh_state(data, sshc, SSH_SESSION_FREE); + myssh_to(data, sshc, SSH_SESSION_FREE); FALLTHROUGH(); case SSH_SESSION_FREE: sshc_cleanup(sshc); @@ -1995,14 +2364,14 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, connclose(conn, "SSH session free"); sshc->state = SSH_SESSION_FREE; /* current */ sshc->nextstate = SSH_NO_STATE; - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); break; case SSH_QUIT: default: /* internal error */ sshc->nextstate = SSH_NO_STATE; - myssh_state(data, sshc, SSH_STOP); + myssh_to(data, sshc, SSH_STOP); break; } @@ -2014,7 +2383,9 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, right direction too)! */ *block = TRUE; } - + if(!result && (sshc->state == SSH_STOP)) + result = sshc->actualcode; + DEBUGF(infof(data, "SSH: myssh_statemach_act -> %d", result)); return result; } @@ -2284,7 +2655,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done) /* we do not verify here, we do it at the state machine, * after connection */ - myssh_state(data, sshc, SSH_INIT); + myssh_to(data, sshc, SSH_INIT); result = myssh_multi_statemach(data, done); @@ -2327,7 +2698,7 @@ CURLcode scp_perform(struct Curl_easy *data, return CURLE_FAILED_INIT; /* start the first command in the DO phase */ - myssh_state(data, sshc, SSH_SCP_TRANS_INIT); + myssh_to(data, sshc, SSH_SCP_TRANS_INIT); result = myssh_multi_statemach(data, dophase_done); @@ -2441,7 +2812,7 @@ static CURLcode scp_disconnect(struct Curl_easy *data, if(sshc && sshc->ssh_session && sshp) { /* only if there is a session still around to use! */ - myssh_state(data, sshc, SSH_SESSION_DISCONNECT); + myssh_to(data, sshc, SSH_SESSION_DISCONNECT); result = myssh_block_statemach(data, sshc, sshp, TRUE); } @@ -2482,24 +2853,25 @@ static CURLcode scp_done(struct Curl_easy *data, CURLcode status, if(!sshc) return CURLE_FAILED_INIT; if(!status) - myssh_state(data, sshc, SSH_SCP_DONE); + myssh_to(data, sshc, SSH_SCP_DONE); return myssh_done(data, sshc, status); } -static ssize_t scp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, bool eos, CURLcode *err) +static CURLcode scp_send(struct Curl_easy *data, int sockindex, + const void *mem, size_t len, bool eos, + size_t *pnwritten) { int rc; struct connectdata *conn = data->conn; struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); + (void) sockindex; /* we only support SCP on the fixed known primary socket */ (void)eos; + *pnwritten = 0; - if(!sshc) { - *err = CURLE_FAILED_INIT; - return -1; - } + if(!sshc) + return CURLE_FAILED_INIT; rc = ssh_scp_write(sshc->scp_session, mem, len); @@ -2509,32 +2881,30 @@ static ssize_t scp_send(struct Curl_easy *data, int sockindex, * Currently rc can only be number of bytes read or SSH_ERROR. */ myssh_block2waitfor(conn, sshc, (rc == SSH_AGAIN)); - if(rc == SSH_AGAIN) { - *err = CURLE_AGAIN; - return 0; - } + if(rc == SSH_AGAIN) + return CURLE_AGAIN; else #endif - if(rc != SSH_OK) { - *err = CURLE_SSH; - return -1; - } + if(rc != SSH_OK) + return CURLE_SSH; - return len; + *pnwritten = len; + return CURLE_OK; } -static ssize_t scp_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, CURLcode *err) +static CURLcode scp_recv(struct Curl_easy *data, int sockindex, + char *mem, size_t len, size_t *pnread) { - ssize_t nread; struct connectdata *conn = data->conn; struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); + ssize_t nread; + (void) sockindex; /* we only support SCP on the fixed known primary socket */ + *pnread = 0; + + if(!sshc) + return CURLE_FAILED_INIT; - if(!sshc) { - *err = CURLE_FAILED_INIT; - return -1; - } /* libssh returns int */ nread = ssh_scp_read(sshc->scp_session, mem, len); @@ -2544,13 +2914,11 @@ static ssize_t scp_recv(struct Curl_easy *data, int sockindex, * Currently rc can only be SSH_OK or SSH_ERROR. */ myssh_block2waitfor(conn, sshc, (nread == SSH_AGAIN)); - if(nread == SSH_AGAIN) { - *err = CURLE_AGAIN; - nread = -1; - } + if(nread == SSH_AGAIN) + return CURLE_AGAIN; #endif - - return nread; + *pnread = (size_t)nread; + return CURLE_OK; } /* @@ -2581,7 +2949,7 @@ CURLcode sftp_perform(struct Curl_easy *data, return CURLE_FAILED_INIT; /* start the first command in the DO phase */ - myssh_state(data, sshc, SSH_SFTP_QUOTE_INIT); + myssh_to(data, sshc, SSH_SFTP_QUOTE_INIT); /* run the state-machine */ result = myssh_multi_statemach(data, dophase_done); @@ -2622,7 +2990,7 @@ static CURLcode sftp_disconnect(struct Curl_easy *data, if(sshc && sshc->ssh_session && sshp) { /* only if there is a session still around to use! */ - myssh_state(data, sshc, SSH_SFTP_SHUTDOWN); + myssh_to(data, sshc, SSH_SFTP_SHUTDOWN); result = myssh_block_statemach(data, sshc, sshp, TRUE); } @@ -2644,26 +3012,27 @@ static CURLcode sftp_done(struct Curl_easy *data, CURLcode status, operation */ if(!premature && data->set.postquote && !conn->bits.retry) sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT; - myssh_state(data, sshc, SSH_SFTP_CLOSE); + myssh_to(data, sshc, SSH_SFTP_CLOSE); } return myssh_done(data, sshc, status); } /* return number of sent bytes */ -static ssize_t sftp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, bool eos, - CURLcode *err) +static CURLcode sftp_send(struct Curl_easy *data, int sockindex, + const void *mem, size_t len, bool eos, + size_t *pnwritten) { - ssize_t nwrite; struct connectdata *conn = data->conn; struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); + ssize_t nwrite; + (void)sockindex; (void)eos; + *pnwritten = 0; + + if(!sshc) + return CURLE_FAILED_INIT; - if(!sshc) { - *err = CURLE_FAILED_INIT; - return -1; - } /* limit the writes to the maximum specified in Section 3 of * https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02 */ @@ -2675,31 +3044,27 @@ static ssize_t sftp_send(struct Curl_easy *data, int sockindex, sftp_file_set_nonblocking(sshc->sftp_file); if(sftp_aio_begin_write(sshc->sftp_file, mem, len, &sshc->sftp_aio) == SSH_ERROR) { - *err = CURLE_SEND_ERROR; - return -1; + return CURLE_SEND_ERROR; } sshc->sftp_send_state = 1; FALLTHROUGH(); case 1: nwrite = sftp_aio_wait_write(&sshc->sftp_aio); myssh_block2waitfor(conn, sshc, (nwrite == SSH_AGAIN) ? TRUE : FALSE); - if(nwrite == SSH_AGAIN) { - *err = CURLE_AGAIN; - return 0; - } - else if(nwrite < 0) { - *err = CURLE_SEND_ERROR; - return -1; - } + if(nwrite == SSH_AGAIN) + return CURLE_AGAIN; + else if(nwrite < 0) + return CURLE_SEND_ERROR; if(sshc->sftp_aio) { sftp_aio_free(sshc->sftp_aio); sshc->sftp_aio = NULL; } sshc->sftp_send_state = 0; - return nwrite; + *pnwritten = (size_t)nwrite; + return CURLE_OK; default: /* we never reach here */ - return -1; + return CURLE_SEND_ERROR; } #else nwrite = sftp_write(sshc->sftp_file, mem, len); @@ -2713,12 +3078,11 @@ static ssize_t sftp_send(struct Curl_easy *data, int sockindex, } else #endif - if(nwrite < 0) { - *err = CURLE_SSH; - nwrite = -1; - } + if(nwrite < 0) + return CURLE_SSH; - return nwrite; + *pnwritten = (size_t)nwrite; + return CURLE_OK; #endif } @@ -2726,28 +3090,26 @@ static ssize_t sftp_send(struct Curl_easy *data, int sockindex, * Return number of received (decrypted) bytes * or <0 on error */ -static ssize_t sftp_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, CURLcode *err) +static CURLcode sftp_recv(struct Curl_easy *data, int sockindex, + char *mem, size_t len, size_t *pnread) { - ssize_t nread; struct connectdata *conn = data->conn; struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); + ssize_t nread; + (void)sockindex; + *pnread = 0; DEBUGASSERT(len < CURL_MAX_READ_SIZE); - if(!sshc) { - *err = CURLE_FAILED_INIT; - return -1; - } + if(!sshc) + return CURLE_FAILED_INIT; switch(sshc->sftp_recv_state) { case 0: sshc->sftp_file_index = sftp_async_read_begin(sshc->sftp_file, (uint32_t)len); - if(sshc->sftp_file_index < 0) { - *err = CURLE_RECV_ERROR; - return -1; - } + if(sshc->sftp_file_index < 0) + return CURLE_RECV_ERROR; FALLTHROUGH(); case 1: @@ -2757,321 +3119,21 @@ static ssize_t sftp_recv(struct Curl_easy *data, int sockindex, myssh_block2waitfor(conn, sshc, (nread == SSH_AGAIN)); - if(nread == SSH_AGAIN) { - *err = CURLE_AGAIN; - return -1; - } - else if(nread < 0) { - *err = CURLE_RECV_ERROR; - return -1; - } + if(nread == SSH_AGAIN) + return CURLE_AGAIN; + else if(nread < 0) + return CURLE_RECV_ERROR; sshc->sftp_recv_state = 0; - return nread; + *pnread = (size_t)nread; + return CURLE_OK; default: /* we never reach here */ - return -1; + return CURLE_RECV_ERROR; } } -static void sftp_quote(struct Curl_easy *data, - struct ssh_conn *sshc, - struct SSHPROTO *sshp) -{ - const char *cp; - CURLcode result; - - /* - * Support some of the "FTP" commands - */ - char *cmd = sshc->quote_item->data; - sshc->acceptfail = FALSE; - - /* if a command starts with an asterisk, which a legal SFTP command never - can, the command will be allowed to fail without it causing any - aborts or cancels etc. It will cause libcurl to act as if the command - is successful, whatever the server responds. */ - - if(cmd[0] == '*') { - cmd++; - sshc->acceptfail = TRUE; - } - - if(strcasecompare("pwd", cmd)) { - /* output debug output if that is requested */ - char *tmp = aprintf("257 \"%s\" is current directory.\n", sshp->path); - if(!tmp) { - sshc->actualcode = CURLE_OUT_OF_MEMORY; - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - return; - } - Curl_debug(data, CURLINFO_HEADER_OUT, "PWD\n", 4); - Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp)); - - /* this sends an FTP-like "header" to the header callback so that the - current directory can be read very similar to how it is read when - using ordinary FTP. */ - result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); - free(tmp); - if(result) { - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - } - else - myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE); - return; - } - - /* - * the arguments following the command must be separated from the - * command with a space so we can check for it unconditionally - */ - cp = strchr(cmd, ' '); - if(!cp) { - failf(data, "Syntax error in SFTP command. Supply parameter(s)"); - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - return; - } - - /* - * also, every command takes at least one argument so we get that - * first argument right now - */ - result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error: Bad first parameter"); - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - return; - } - - /* - * SFTP is a binary protocol, so we do not send text commands - * to the server. Instead, we scan for commands used by - * OpenSSH's sftp program and call the appropriate libssh - * functions. - */ - if(!strncmp(cmd, "chgrp ", 6) || - !strncmp(cmd, "chmod ", 6) || - !strncmp(cmd, "chown ", 6) || - !strncmp(cmd, "atime ", 6) || - !strncmp(cmd, "mtime ", 6)) { - /* attribute change */ - - /* sshc->quote_path1 contains the mode to set */ - /* get the destination */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error in chgrp/chmod/chown/atime/mtime: " - "Bad second parameter"); - Curl_safefree(sshc->quote_path1); - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - return; - } - sshc->quote_attrs = NULL; - myssh_state(data, sshc, SSH_SFTP_QUOTE_STAT); - return; - } - if(!strncmp(cmd, "ln ", 3) || - !strncmp(cmd, "symlink ", 8)) { - /* symbolic linking */ - /* sshc->quote_path1 is the source */ - /* get the destination */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error in ln/symlink: Bad second parameter"); - Curl_safefree(sshc->quote_path1); - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - return; - } - myssh_state(data, sshc, SSH_SFTP_QUOTE_SYMLINK); - return; - } - else if(!strncmp(cmd, "mkdir ", 6)) { - /* create dir */ - myssh_state(data, sshc, SSH_SFTP_QUOTE_MKDIR); - return; - } - else if(!strncmp(cmd, "rename ", 7)) { - /* rename file */ - /* first param is the source path */ - /* second param is the dest. path */ - result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); - if(result) { - if(result == CURLE_OUT_OF_MEMORY) - failf(data, "Out of memory"); - else - failf(data, "Syntax error in rename: Bad second parameter"); - Curl_safefree(sshc->quote_path1); - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = result; - return; - } - myssh_state(data, sshc, SSH_SFTP_QUOTE_RENAME); - return; - } - else if(!strncmp(cmd, "rmdir ", 6)) { - /* delete dir */ - myssh_state(data, sshc, SSH_SFTP_QUOTE_RMDIR); - return; - } - else if(!strncmp(cmd, "rm ", 3)) { - myssh_state(data, sshc, SSH_SFTP_QUOTE_UNLINK); - return; - } -#ifdef HAS_STATVFS_SUPPORT - else if(!strncmp(cmd, "statvfs ", 8)) { - myssh_state(data, sshc, SSH_SFTP_QUOTE_STATVFS); - return; - } -#endif - - failf(data, "Unknown SFTP command"); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; -} - -static void sftp_quote_stat(struct Curl_easy *data, - struct ssh_conn *sshc) -{ - char *cmd = sshc->quote_item->data; - sshc->acceptfail = FALSE; - - /* if a command starts with an asterisk, which a legal SFTP command never - can, the command will be allowed to fail without it causing any - aborts or cancels etc. It will cause libcurl to act as if the command - is successful, whatever the server responds. */ - - if(cmd[0] == '*') { - cmd++; - sshc->acceptfail = TRUE; - } - - /* We read the file attributes, store them in sshc->quote_attrs - * and modify them accordingly to command. Then we switch to - * QUOTE_SETSTAT state to write new ones. - */ - - if(sshc->quote_attrs) - sftp_attributes_free(sshc->quote_attrs); - sshc->quote_attrs = sftp_stat(sshc->sftp_session, sshc->quote_path2); - if(!sshc->quote_attrs) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Attempt to get SFTP stats failed: %d", - sftp_get_error(sshc->sftp_session)); - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - return; - } - - /* Now set the new attributes... */ - if(!strncmp(cmd, "chgrp", 5)) { - const char *p = sshc->quote_path1; - curl_off_t gid; - (void)curlx_str_number(&p, &gid, UINT_MAX); - sshc->quote_attrs->gid = (uint32_t)gid; - if(sshc->quote_attrs->gid == 0 && !ISDIGIT(sshc->quote_path1[0]) && - !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Syntax error: chgrp gid not a number"); - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - return; - } - sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID; - } - else if(!strncmp(cmd, "chmod", 5)) { - curl_off_t perms; - const char *p = sshc->quote_path1; - if(curlx_str_octal(&p, &perms, 07777)) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Syntax error: chmod permissions not a number"); - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - return; - } - sshc->quote_attrs->permissions = (mode_t)perms; - sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_PERMISSIONS; - } - else if(!strncmp(cmd, "chown", 5)) { - const char *p = sshc->quote_path1; - curl_off_t uid; - (void)curlx_str_number(&p, &uid, UINT_MAX); - if(sshc->quote_attrs->uid == 0 && !ISDIGIT(sshc->quote_path1[0]) && - !sshc->acceptfail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - failf(data, "Syntax error: chown uid not a number"); - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - return; - } - sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID; - } - else if(!strncmp(cmd, "atime", 5) || - !strncmp(cmd, "mtime", 5)) { - time_t date = Curl_getdate_capped(sshc->quote_path1); - bool fail = FALSE; - if(date == -1) { - failf(data, "incorrect date format for %.*s", 5, cmd); - fail = TRUE; - } -#if SIZEOF_TIME_T > 4 - else if(date > 0xffffffff) { - failf(data, "date overflow"); - fail = TRUE; /* avoid setting a capped time */ - } -#endif - if(fail) { - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - myssh_state(data, sshc, SSH_SFTP_CLOSE); - sshc->nextstate = SSH_NO_STATE; - sshc->actualcode = CURLE_QUOTE_ERROR; - return; - } - if(!strncmp(cmd, "atime", 5)) - sshc->quote_attrs->atime = (uint32_t)date; - else /* mtime */ - sshc->quote_attrs->mtime = (uint32_t)date; - - sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_ACMODTIME; - } - - /* Now send the completed structure... */ - myssh_state(data, sshc, SSH_SFTP_QUOTE_SETSTAT); - return; -} CURLcode Curl_ssh_init(void) { diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index c16b3ac388..d599d3cc50 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -61,11 +61,9 @@ #include "../speedcheck.h" #include "../getinfo.h" #include "../strdup.h" -#include "../strcase.h" #include "../vtls/vtls.h" #include "../cfilters.h" #include "../connect.h" -#include "../inet_ntop.h" #include "../parsedate.h" /* for the week day and month names */ #include "../sockaddr.h" /* required for Curl_sockaddr_storage */ #include "../multiif.h" @@ -696,7 +694,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, /* This does NOT verify the length of 'pubkey_md5' separately, which will make the comparison below fail unless it is exactly 32 characters */ - if(!fingerprint || !strcasecompare(md5buffer, pubkey_md5)) { + if(!fingerprint || !curl_strequal(md5buffer, pubkey_md5)) { if(fingerprint) { failf(data, "Denied establishing ssh session: mismatch md5 fingerprint. " @@ -920,7 +918,7 @@ static CURLcode sftp_quote(struct Curl_easy *data, sshc->acceptfail = TRUE; } - if(strcasecompare("pwd", cmd)) { + if(curl_strequal("pwd", cmd)) { /* output debug output if that is requested */ char *tmp = aprintf("257 \"%s\" is current directory.\n", sshp->path); if(!tmp) @@ -1210,15 +1208,9 @@ sftp_upload_init(struct Curl_easy *data, figure out a "real" bitmask */ sshc->orig_waitfor = data->req.keepon; - /* we want to use the _sending_ function even when the socket turns - out readable as the underlying libssh2 sftp send function will deal - with both accordingly */ - data->state.select_bits = CURL_CSELECT_OUT; - /* since we do not really wait for anything at this point, we want the - state machine to move on as soon as possible so we set a very short - timeout here */ - Curl_expire(data, 0, EXPIRE_RUN_NOW); + state machine to move on as soon as possible so mark this as dirty */ + Curl_multi_mark_dirty(data); myssh_state(data, sshc, SSH_STOP); return CURLE_OK; @@ -1553,10 +1545,6 @@ sftp_download_stat(struct Curl_easy *data, /* not set by Curl_xfer_setup to preserve keepon bits */ data->conn->writesockfd = data->conn->sockfd; - /* we want to use the _receiving_ function even when the socket turns - out writableable as the underlying libssh2 recv function will deal - with both accordingly */ - data->state.select_bits = CURL_CSELECT_IN; myssh_state(data, sshc, SSH_STOP); return CURLE_OK; @@ -1809,7 +1797,7 @@ static CURLcode ssh_state_auth_host_init(struct Curl_easy *data, } static CURLcode ssh_state_auth_agent_init(struct Curl_easy *data, - struct ssh_conn *sshc) + struct ssh_conn *sshc) { int rc = 0; if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT) @@ -1918,7 +1906,7 @@ static CURLcode ssh_state_auth_key_init(struct Curl_easy *data, } static CURLcode ssh_state_auth_key(struct Curl_easy *data, - struct ssh_conn *sshc) + struct ssh_conn *sshc) { /* Authentication failed. Continue with keyboard-interactive now. */ struct connectdata *conn = data->conn; @@ -2274,7 +2262,7 @@ static CURLcode ssh_state_sftp_quote_unlink(struct Curl_easy *data, } static CURLcode ssh_state_sftp_quote_statvfs(struct Curl_easy *data, - struct ssh_conn *sshc) + struct ssh_conn *sshc) { LIBSSH2_SFTP_STATVFS statvfs; int rc = libssh2_sftp_statvfs(sshc->sftp_session, sshc->quote_path1, @@ -2477,11 +2465,6 @@ static CURLcode ssh_state_scp_download_init(struct Curl_easy *data, /* not set by Curl_xfer_setup to preserve keepon bits */ data->conn->writesockfd = data->conn->sockfd; - /* we want to use the _receiving_ function even when the socket turns - out writableable as the underlying libssh2 recv function will deal - with both accordingly */ - data->state.select_bits = CURL_CSELECT_IN; - myssh_state(data, sshc, SSH_STOP); return CURLE_OK; } @@ -2524,7 +2507,7 @@ static CURLcode ssh_state_sftp_close(struct Curl_easy *data, } static CURLcode ssh_state_sftp_shutdown(struct Curl_easy *data, - struct ssh_conn *sshc) + struct ssh_conn *sshc) { /* during times we get here due to a broken transfer and then the sftp_handle might not have been taken down so make sure that is done @@ -2635,11 +2618,6 @@ static CURLcode ssh_state_scp_upload_init(struct Curl_easy *data, figure out a "real" bitmask */ sshc->orig_waitfor = data->req.keepon; - /* we want to use the _sending_ function even when the socket turns - out readable as the underlying libssh2 scp send function will deal - with both accordingly */ - data->state.select_bits = CURL_CSELECT_OUT; - myssh_state(data, sshc, SSH_STOP); return CURLE_OK; @@ -3267,7 +3245,6 @@ static CURLcode ssh_setup_connection(struct Curl_easy *data, if(!sshc) return CURLE_OUT_OF_MEMORY; - sshc->initialised = TRUE; if(Curl_conn_meta_set(conn, CURL_META_SSH_CONN, sshc, myssh_conn_dtor)) return CURLE_OUT_OF_MEMORY; @@ -3291,7 +3268,7 @@ static ssize_t ssh_tls_recv(libssh2_socket_t sock, void *buffer, size_t length, int flags, void **abstract) { struct Curl_easy *data = (struct Curl_easy *)*abstract; - ssize_t nread; + size_t nread; CURLcode result; struct connectdata *conn = data->conn; Curl_recv *backup = conn->recv[0]; @@ -3312,7 +3289,7 @@ static ssize_t ssh_tls_recv(libssh2_socket_t sock, void *buffer, else if(result) return -1; /* generic error */ Curl_debug(data, CURLINFO_DATA_IN, (const char *)buffer, (size_t)nread); - return nread; + return (ssize_t)nread; } static ssize_t ssh_tls_send(libssh2_socket_t sock, const void *buffer, @@ -3611,101 +3588,99 @@ static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data, { int rc; - if(sshc->initialised) { - if(sshc->kh) { - libssh2_knownhost_free(sshc->kh); - sshc->kh = NULL; - } - - if(sshc->ssh_agent) { - rc = libssh2_agent_disconnect(sshc->ssh_agent); - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - if((rc < 0) && data) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to disconnect from libssh2 agent: %d %s", - rc, err_msg); - } - libssh2_agent_free(sshc->ssh_agent); - sshc->ssh_agent = NULL; - - /* NB: there is no need to free identities, they are part of internal - agent stuff */ - sshc->sshagent_identity = NULL; - sshc->sshagent_prev_identity = NULL; - } - - if(sshc->sftp_handle) { - rc = libssh2_sftp_close(sshc->sftp_handle); - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - if((rc < 0) && data) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, - NULL, 0); - infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg); - } - sshc->sftp_handle = NULL; - } - - if(sshc->ssh_channel) { - rc = libssh2_channel_free(sshc->ssh_channel); - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - if((rc < 0) && data) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to free libssh2 scp subsystem: %d %s", - rc, err_msg); - } - sshc->ssh_channel = NULL; - } - - if(sshc->sftp_session) { - rc = libssh2_sftp_shutdown(sshc->sftp_session); - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - if((rc < 0) && data) - infof(data, "Failed to stop libssh2 sftp subsystem"); - sshc->sftp_session = NULL; - } - - if(sshc->ssh_session) { - rc = libssh2_session_free(sshc->ssh_session); - if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) - return CURLE_AGAIN; - - if((rc < 0) && data) { - char *err_msg = NULL; - (void)libssh2_session_last_error(sshc->ssh_session, - &err_msg, NULL, 0); - infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg); - } - sshc->ssh_session = NULL; - } - - /* worst-case scenario cleanup */ - DEBUGASSERT(sshc->ssh_session == NULL); - DEBUGASSERT(sshc->ssh_channel == NULL); - DEBUGASSERT(sshc->sftp_session == NULL); - DEBUGASSERT(sshc->sftp_handle == NULL); - DEBUGASSERT(sshc->kh == NULL); - DEBUGASSERT(sshc->ssh_agent == NULL); - - Curl_safefree(sshc->rsa_pub); - Curl_safefree(sshc->rsa); - Curl_safefree(sshc->quote_path1); - Curl_safefree(sshc->quote_path2); - Curl_safefree(sshc->homedir); - sshc->initialised = FALSE; + if(sshc->kh) { + libssh2_knownhost_free(sshc->kh); + sshc->kh = NULL; } + + if(sshc->ssh_agent) { + rc = libssh2_agent_disconnect(sshc->ssh_agent); + if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) + return CURLE_AGAIN; + + if((rc < 0) && data) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Failed to disconnect from libssh2 agent: %d %s", + rc, err_msg); + } + libssh2_agent_free(sshc->ssh_agent); + sshc->ssh_agent = NULL; + + /* NB: there is no need to free identities, they are part of internal + agent stuff */ + sshc->sshagent_identity = NULL; + sshc->sshagent_prev_identity = NULL; + } + + if(sshc->sftp_handle) { + rc = libssh2_sftp_close(sshc->sftp_handle); + if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) + return CURLE_AGAIN; + + if((rc < 0) && data) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, + NULL, 0); + infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg); + } + sshc->sftp_handle = NULL; + } + + if(sshc->ssh_channel) { + rc = libssh2_channel_free(sshc->ssh_channel); + if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) + return CURLE_AGAIN; + + if((rc < 0) && data) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Failed to free libssh2 scp subsystem: %d %s", + rc, err_msg); + } + sshc->ssh_channel = NULL; + } + + if(sshc->sftp_session) { + rc = libssh2_sftp_shutdown(sshc->sftp_session); + if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) + return CURLE_AGAIN; + + if((rc < 0) && data) + infof(data, "Failed to stop libssh2 sftp subsystem"); + sshc->sftp_session = NULL; + } + + if(sshc->ssh_session) { + rc = libssh2_session_free(sshc->ssh_session); + if(!block && (rc == LIBSSH2_ERROR_EAGAIN)) + return CURLE_AGAIN; + + if((rc < 0) && data) { + char *err_msg = NULL; + (void)libssh2_session_last_error(sshc->ssh_session, + &err_msg, NULL, 0); + infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg); + } + sshc->ssh_session = NULL; + } + + /* worst-case scenario cleanup */ + DEBUGASSERT(sshc->ssh_session == NULL); + DEBUGASSERT(sshc->ssh_channel == NULL); + DEBUGASSERT(sshc->sftp_session == NULL); + DEBUGASSERT(sshc->sftp_handle == NULL); + DEBUGASSERT(sshc->kh == NULL); + DEBUGASSERT(sshc->ssh_agent == NULL); + + Curl_safefree(sshc->rsa_pub); + Curl_safefree(sshc->rsa); + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + Curl_safefree(sshc->homedir); + return CURLE_OK; } @@ -3770,58 +3745,63 @@ static CURLcode scp_done(struct Curl_easy *data, CURLcode status, return ssh_done(data, status); } -static ssize_t scp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, bool eos, CURLcode *err) +static CURLcode scp_send(struct Curl_easy *data, int sockindex, + const void *mem, size_t len, bool eos, + size_t *pnwritten) { - ssize_t nwrite; struct connectdata *conn = data->conn; struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); + CURLcode result = CURLE_OK; + ssize_t nwritten; + (void)sockindex; /* we only support SCP on the fixed known primary socket */ (void)eos; + *pnwritten = 0; + + if(!sshc) + return CURLE_FAILED_INIT; - if(!sshc) { - *err = CURLE_FAILED_INIT; - return -1; - } /* libssh2_channel_write() returns int! */ - nwrite = (ssize_t) libssh2_channel_write(sshc->ssh_channel, mem, len); + nwritten = (ssize_t) libssh2_channel_write(sshc->ssh_channel, mem, len); - ssh_block2waitfor(data, sshc, (nwrite == LIBSSH2_ERROR_EAGAIN)); + ssh_block2waitfor(data, sshc, (nwritten == LIBSSH2_ERROR_EAGAIN)); - if(nwrite == LIBSSH2_ERROR_EAGAIN) { - *err = CURLE_AGAIN; - nwrite = 0; - } - else if(nwrite < LIBSSH2_ERROR_NONE) { - *err = libssh2_session_error_to_CURLE((int)nwrite); - nwrite = -1; - } + if(nwritten == LIBSSH2_ERROR_EAGAIN) + result = CURLE_AGAIN; + else if(nwritten < LIBSSH2_ERROR_NONE) + result = libssh2_session_error_to_CURLE((int)nwritten); + else + *pnwritten = (size_t)nwritten; - return nwrite; + return result; } -static ssize_t scp_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, CURLcode *err) +static CURLcode scp_recv(struct Curl_easy *data, int sockindex, + char *mem, size_t len, size_t *pnread) { - ssize_t nread; struct connectdata *conn = data->conn; struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); + CURLcode result = CURLE_OK; + ssize_t nread; + (void)sockindex; /* we only support SCP on the fixed known primary socket */ + *pnread = 0; + + if(!sshc) + return CURLE_FAILED_INIT; - if(!sshc) { - *err = CURLE_FAILED_INIT; - return -1; - } /* libssh2_channel_read() returns int */ nread = (ssize_t) libssh2_channel_read(sshc->ssh_channel, mem, len); ssh_block2waitfor(data, sshc, (nread == LIBSSH2_ERROR_EAGAIN)); - if(nread == LIBSSH2_ERROR_EAGAIN) { - *err = CURLE_AGAIN; - nread = -1; - } + if(nread == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + else if(nread < LIBSSH2_ERROR_NONE) + result = libssh2_session_error_to_CURLE((int)nread); + else + *pnread = (size_t)nread; - return nread; + return result; } /* @@ -3906,7 +3886,7 @@ static CURLcode sftp_disconnect(struct Curl_easy *data, } static CURLcode sftp_done(struct Curl_easy *data, CURLcode status, - bool premature) + bool premature) { struct connectdata *conn = data->conn; struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); @@ -3926,64 +3906,61 @@ static CURLcode sftp_done(struct Curl_easy *data, CURLcode status, } /* return number of sent bytes */ -static ssize_t sftp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, bool eos, CURLcode *err) +static CURLcode sftp_send(struct Curl_easy *data, int sockindex, + const void *mem, size_t len, bool eos, + size_t *pnwritten) { - ssize_t nwrite; struct connectdata *conn = data->conn; struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); + ssize_t nwrite; + (void)sockindex; (void)eos; + *pnwritten = 0; + + if(!sshc) + return CURLE_FAILED_INIT; - if(!sshc) { - *err = CURLE_FAILED_INIT; - return -1; - } nwrite = libssh2_sftp_write(sshc->sftp_handle, mem, len); ssh_block2waitfor(data, sshc, (nwrite == LIBSSH2_ERROR_EAGAIN)); - if(nwrite == LIBSSH2_ERROR_EAGAIN) { - *err = CURLE_AGAIN; - nwrite = 0; - } - else if(nwrite < LIBSSH2_ERROR_NONE) { - *err = libssh2_session_error_to_CURLE((int)nwrite); - nwrite = -1; - } - - return nwrite; + if(nwrite == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + else if(nwrite < LIBSSH2_ERROR_NONE) + return libssh2_session_error_to_CURLE((int)nwrite); + *pnwritten = (size_t)nwrite; + return CURLE_OK; } /* * Return number of received (decrypted) bytes * or <0 on error */ -static ssize_t sftp_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, CURLcode *err) +static CURLcode sftp_recv(struct Curl_easy *data, int sockindex, + char *mem, size_t len, size_t *pnread) { - ssize_t nread; struct connectdata *conn = data->conn; struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); + ssize_t nread; + (void)sockindex; + *pnread = 0; + + if(!sshc) + return CURLE_FAILED_INIT; - if(!sshc) { - *err = CURLE_FAILED_INIT; - return -1; - } nread = libssh2_sftp_read(sshc->sftp_handle, mem, len); ssh_block2waitfor(data, sshc, (nread == LIBSSH2_ERROR_EAGAIN)); - if(nread == LIBSSH2_ERROR_EAGAIN) { - *err = CURLE_AGAIN; - nread = -1; + if(nread == LIBSSH2_ERROR_EAGAIN) + return CURLE_AGAIN; + else if(nread < 0) + return libssh2_session_error_to_CURLE((int)nread); - } - else if(nread < 0) { - *err = libssh2_session_error_to_CURLE((int)nread); - } - return nread; + *pnread = (size_t)nread; + return CURLE_OK; } static const char *sftp_libssh2_strerror(unsigned long err) diff --git a/lib/vssh/ssh.h b/lib/vssh/ssh.h index feee886562..4056c39b20 100644 --- a/lib/vssh/ssh.h +++ b/lib/vssh/ssh.h @@ -191,6 +191,7 @@ struct ssh_conn { const char *readdir_filename; /* points within readdir_attrs */ const char *readdir_longentry; char *readdir_tmp; + BIT(initialised); #elif defined(USE_LIBSSH2) LIBSSH2_SESSION *ssh_session; /* Secure Shell session */ LIBSSH2_CHANNEL *ssh_channel; /* Secure Shell channel handle */ @@ -214,8 +215,8 @@ struct ssh_conn { word32 handleSz; byte handle[WOLFSSH_MAX_HANDLE]; curl_off_t offset; -#endif /* USE_LIBSSH */ BIT(initialised); +#endif /* USE_LIBSSH */ BIT(authed); /* the connection has been authenticated fine */ BIT(acceptfail); /* used by the SFTP_QUOTE (continue if quote command fails) */ diff --git a/lib/vssh/wolfssh.c b/lib/vssh/wolfssh.c index 5097ca02c5..eed73fd919 100644 --- a/lib/vssh/wolfssh.c +++ b/lib/vssh/wolfssh.c @@ -220,37 +220,35 @@ static void wssh_state(struct Curl_easy *data, sshc->state = nowstate; } -static ssize_t wscp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, bool eos, - CURLcode *err) +static CURLcode wscp_send(struct Curl_easy *data, int sockindex, + const void *mem, size_t len, bool eos, + size_t *pnwritten) { - ssize_t nwrite = 0; (void)data; (void)sockindex; /* we only support SCP on the fixed known primary socket */ (void)mem; (void)len; (void)eos; - (void)err; - - return nwrite; + *pnwritten = 0; + return CURLE_OK; } -static ssize_t wscp_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, CURLcode *err) +static CURLcode wscp_recv(struct Curl_easy *data, int sockindex, + char *mem, size_t len, size_t *pnread) { - ssize_t nread = 0; (void)data; (void)sockindex; /* we only support SCP on the fixed known primary socket */ (void)mem; (void)len; - (void)err; + *pnread = 0; - return nread; + return CURLE_OK; } /* return number of sent bytes */ -static ssize_t wsftp_send(struct Curl_easy *data, int sockindex, - const void *mem, size_t len, bool eos, CURLcode *err) +static CURLcode wsftp_send(struct Curl_easy *data, int sockindex, + const void *mem, size_t len, bool eos, + size_t *pnwritten) { struct connectdata *conn = data->conn; struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN); @@ -259,10 +257,10 @@ static ssize_t wsftp_send(struct Curl_easy *data, int sockindex, (void)sockindex; (void)eos; - if(!sshc) { - *err = CURLE_FAILED_INIT; - return -1; - } + *pnwritten = 0; + if(!sshc) + return CURLE_FAILED_INIT; + offset[0] = (word32)sshc->offset & 0xFFFFFFFF; offset[1] = (word32)(sshc->offset >> 32) & 0xFFFFFFFF; @@ -275,31 +273,30 @@ static ssize_t wsftp_send(struct Curl_easy *data, int sockindex, rc = wolfSSH_get_error(sshc->ssh_session); if(rc == WS_WANT_READ) { conn->waitfor = KEEP_RECV; - *err = CURLE_AGAIN; - return -1; + return CURLE_AGAIN; } else if(rc == WS_WANT_WRITE) { conn->waitfor = KEEP_SEND; - *err = CURLE_AGAIN; - return -1; + return CURLE_AGAIN; } if(rc < 0) { failf(data, "wolfSSH_SFTP_SendWritePacket returned %d", rc); - return -1; + return CURLE_SEND_ERROR; } DEBUGASSERT(rc == (int)len); + *pnwritten = (size_t)rc; + sshc->offset += *pnwritten; infof(data, "sent %zu bytes SFTP from offset %" FMT_OFF_T, - len, sshc->offset); - sshc->offset += len; - return (ssize_t)rc; + *pnwritten, sshc->offset); + return CURLE_OK; } /* * Return number of received (decrypted) bytes * or <0 on error */ -static ssize_t wsftp_recv(struct Curl_easy *data, int sockindex, - char *mem, size_t len, CURLcode *err) +static CURLcode wsftp_recv(struct Curl_easy *data, int sockindex, + char *mem, size_t len, size_t *pnread) { int rc; struct connectdata *conn = data->conn; @@ -307,10 +304,10 @@ static ssize_t wsftp_recv(struct Curl_easy *data, int sockindex, word32 offset[2]; (void)sockindex; - if(!sshc) { - *err = CURLE_FAILED_INIT; - return -1; - } + *pnread = 0; + if(!sshc) + return CURLE_FAILED_INIT; + offset[0] = (word32)sshc->offset & 0xFFFFFFFF; offset[1] = (word32)(sshc->offset >> 32) & 0xFFFFFFFF; @@ -322,24 +319,22 @@ static ssize_t wsftp_recv(struct Curl_easy *data, int sockindex, rc = wolfSSH_get_error(sshc->ssh_session); if(rc == WS_WANT_READ) { conn->waitfor = KEEP_RECV; - *err = CURLE_AGAIN; - return -1; + return CURLE_AGAIN; } else if(rc == WS_WANT_WRITE) { conn->waitfor = KEEP_SEND; - *err = CURLE_AGAIN; - return -1; + return CURLE_AGAIN; } DEBUGASSERT(rc <= (int)len); if(rc < 0) { failf(data, "wolfSSH_SFTP_SendReadPacket returned %d", rc); - return -1; + return CURLE_RECV_ERROR; } - sshc->offset += len; - - return (ssize_t)rc; + *pnread = (size_t)rc; + sshc->offset += *pnread; + return CURLE_OK; } static void wssh_easy_dtor(void *key, size_t klen, void *entry) @@ -737,15 +732,9 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, figure out a "real" bitmask */ sshc->orig_waitfor = data->req.keepon; - /* we want to use the _sending_ function even when the socket turns - out readable as the underlying libssh2 sftp send function will deal - with both accordingly */ - data->state.select_bits = CURL_CSELECT_OUT; - /* since we do not really wait for anything at this point, we want the - state machine to move on as soon as possible so we set a very short - timeout here */ - Curl_expire(data, 0, EXPIRE_RUN_NOW); + state machine to move on as soon as possible */ + Curl_multi_mark_dirty(data); wssh_state(data, sshc, SSH_STOP); } @@ -833,11 +822,6 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, /* not set by Curl_xfer_setup to preserve keepon bits */ conn->writesockfd = conn->sockfd; - /* we want to use the _receiving_ function even when the socket turns - out writableable as the underlying libssh2 recv function will deal - with both accordingly */ - data->state.select_bits = CURL_CSELECT_IN; - if(result) { /* this should never occur; the close state should be entered at the time the error occurs */ @@ -1123,7 +1107,7 @@ static void wssh_sshc_cleanup(struct ssh_conn *sshc) #if 0 static CURLcode wscp_done(struct Curl_easy *data, - CURLcode code, bool premature) + CURLcode code, bool premature) { CURLcode result = CURLE_OK; (void)conn; @@ -1134,7 +1118,7 @@ static CURLcode wscp_done(struct Curl_easy *data, } static CURLcode wscp_doing(struct Curl_easy *data, - bool *dophase_done) + bool *dophase_done) { CURLcode result = CURLE_OK; (void)conn; @@ -1156,7 +1140,7 @@ static CURLcode wscp_disconnect(struct Curl_easy *data, #endif static CURLcode wsftp_done(struct Curl_easy *data, - CURLcode code, bool premature) + CURLcode code, bool premature) { struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN); (void)premature; @@ -1169,7 +1153,7 @@ static CURLcode wsftp_done(struct Curl_easy *data, } static CURLcode wsftp_doing(struct Curl_easy *data, - bool *dophase_done) + bool *dophase_done) { CURLcode result = wssh_multi_statemach(data, dophase_done); diff --git a/lib/vtls/bearssl.c b/lib/vtls/bearssl.c deleted file mode 100644 index 4b65244451..0000000000 --- a/lib/vtls/bearssl.c +++ /dev/null @@ -1,1104 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Michael Forney, - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - * SPDX-License-Identifier: curl - * - ***************************************************************************/ -#include "../curl_setup.h" - -#ifdef USE_BEARSSL - -#include - -#include "bearssl.h" -#include "cipher_suite.h" -#include "../urldata.h" -#include "../sendf.h" -#include "../curlx/inet_pton.h" -#include "vtls.h" -#include "vtls_int.h" -#include "vtls_scache.h" -#include "../connect.h" -#include "../select.h" -#include "../multiif.h" -#include "../curl_printf.h" - -/* The last #include files should be: */ -#include "../curl_memory.h" -#include "../memdebug.h" - -struct x509_context { - const br_x509_class *vtable; - br_x509_minimal_context minimal; - br_x509_decoder_context decoder; - bool verifyhost; - bool verifypeer; - int cert_num; -}; - -struct bearssl_ssl_backend_data { - br_ssl_client_context ctx; - struct x509_context x509; - unsigned char buf[BR_SSL_BUFSIZE_BIDI]; - br_x509_trust_anchor *anchors; - size_t anchors_len; - const char *protocols[ALPN_ENTRIES_MAX]; - /* SSL client context is active */ - bool active; - /* size of pending write, yet to be flushed */ - size_t pending_write; - BIT(sent_shutdown); -}; - -struct cafile_parser { - CURLcode err; - bool in_cert; - br_x509_decoder_context xc; - /* array of trust anchors loaded from CAfile */ - br_x509_trust_anchor *anchors; - size_t anchors_len; - /* buffer for DN data */ - unsigned char dn[1024]; - size_t dn_len; -}; - -#define CAFILE_SOURCE_PATH 1 -#define CAFILE_SOURCE_BLOB 2 -struct cafile_source { - int type; - const char *data; - size_t len; -}; - -static void append_dn(void *ctx, const void *buf, size_t len) -{ - struct cafile_parser *ca = ctx; - - if(ca->err != CURLE_OK || !ca->in_cert) - return; - if(sizeof(ca->dn) - ca->dn_len < len) { - ca->err = CURLE_FAILED_INIT; - return; - } - memcpy(ca->dn + ca->dn_len, buf, len); - ca->dn_len += len; -} - -static void x509_push(void *ctx, const void *buf, size_t len) -{ - struct cafile_parser *ca = ctx; - - if(ca->in_cert) - br_x509_decoder_push(&ca->xc, buf, len); -} - -static CURLcode load_cafile(struct cafile_source *source, - br_x509_trust_anchor **anchors, - size_t *anchors_len) -{ - struct cafile_parser ca; - br_pem_decoder_context pc; - br_x509_trust_anchor *ta; - size_t ta_size; - br_x509_trust_anchor *new_anchors; - size_t new_anchors_len; - br_x509_pkey *pkey; - FILE *fp = 0; - unsigned char buf[BUFSIZ]; - const unsigned char *p = NULL; - const char *name; - size_t n = 0, i, pushed; - - DEBUGASSERT(source->type == CAFILE_SOURCE_PATH - || source->type == CAFILE_SOURCE_BLOB); - - if(source->type == CAFILE_SOURCE_PATH) { - fp = fopen(source->data, "rb"); - if(!fp) - return CURLE_SSL_CACERT_BADFILE; - } - - if(source->type == CAFILE_SOURCE_BLOB && source->len > (size_t)INT_MAX) - return CURLE_SSL_CACERT_BADFILE; - - ca.err = CURLE_OK; - ca.in_cert = FALSE; - ca.anchors = NULL; - ca.anchors_len = 0; - br_pem_decoder_init(&pc); - br_pem_decoder_setdest(&pc, x509_push, &ca); - do { - if(source->type == CAFILE_SOURCE_PATH) { - n = fread(buf, 1, sizeof(buf), fp); - if(n == 0) - break; - p = buf; - } - else if(source->type == CAFILE_SOURCE_BLOB) { - n = source->len; - p = (const unsigned char *) source->data; - } - while(n) { - pushed = br_pem_decoder_push(&pc, p, n); - if(ca.err) - goto fail; - p += pushed; - n -= pushed; - - switch(br_pem_decoder_event(&pc)) { - case 0: - break; - case BR_PEM_BEGIN_OBJ: - name = br_pem_decoder_name(&pc); - if(strcmp(name, "CERTIFICATE") && strcmp(name, "X509 CERTIFICATE")) - break; - br_x509_decoder_init(&ca.xc, append_dn, &ca); - ca.in_cert = TRUE; - ca.dn_len = 0; - break; - case BR_PEM_END_OBJ: - if(!ca.in_cert) - break; - ca.in_cert = FALSE; - if(br_x509_decoder_last_error(&ca.xc)) { - ca.err = CURLE_SSL_CACERT_BADFILE; - goto fail; - } - /* add trust anchor */ - if(ca.anchors_len == SIZE_MAX / sizeof(ca.anchors[0])) { - ca.err = CURLE_OUT_OF_MEMORY; - goto fail; - } - new_anchors_len = ca.anchors_len + 1; - new_anchors = realloc(ca.anchors, - new_anchors_len * sizeof(ca.anchors[0])); - if(!new_anchors) { - ca.err = CURLE_OUT_OF_MEMORY; - goto fail; - } - ca.anchors = new_anchors; - ca.anchors_len = new_anchors_len; - ta = &ca.anchors[ca.anchors_len - 1]; - ta->dn.data = NULL; - ta->flags = 0; - if(br_x509_decoder_isCA(&ca.xc)) - ta->flags |= BR_X509_TA_CA; - pkey = br_x509_decoder_get_pkey(&ca.xc); - if(!pkey) { - ca.err = CURLE_SSL_CACERT_BADFILE; - goto fail; - } - ta->pkey = *pkey; - - /* calculate space needed for trust anchor data */ - ta_size = ca.dn_len; - switch(pkey->key_type) { - case BR_KEYTYPE_RSA: - ta_size += pkey->key.rsa.nlen + pkey->key.rsa.elen; - break; - case BR_KEYTYPE_EC: - ta_size += pkey->key.ec.qlen; - break; - default: - ca.err = CURLE_FAILED_INIT; - goto fail; - } - - /* fill in trust anchor DN and public key data */ - ta->dn.data = malloc(ta_size); - if(!ta->dn.data) { - ca.err = CURLE_OUT_OF_MEMORY; - goto fail; - } - memcpy(ta->dn.data, ca.dn, ca.dn_len); - ta->dn.len = ca.dn_len; - switch(pkey->key_type) { - case BR_KEYTYPE_RSA: - ta->pkey.key.rsa.n = ta->dn.data + ta->dn.len; - memcpy(ta->pkey.key.rsa.n, pkey->key.rsa.n, pkey->key.rsa.nlen); - ta->pkey.key.rsa.e = ta->pkey.key.rsa.n + ta->pkey.key.rsa.nlen; - memcpy(ta->pkey.key.rsa.e, pkey->key.rsa.e, pkey->key.rsa.elen); - break; - case BR_KEYTYPE_EC: - ta->pkey.key.ec.q = ta->dn.data + ta->dn.len; - memcpy(ta->pkey.key.ec.q, pkey->key.ec.q, pkey->key.ec.qlen); - break; - } - break; - default: - ca.err = CURLE_SSL_CACERT_BADFILE; - goto fail; - } - } - } while(source->type != CAFILE_SOURCE_BLOB); - if(fp && ferror(fp)) - ca.err = CURLE_READ_ERROR; - else if(ca.in_cert) - ca.err = CURLE_SSL_CACERT_BADFILE; - -fail: - if(fp) - fclose(fp); - if(ca.err == CURLE_OK) { - *anchors = ca.anchors; - *anchors_len = ca.anchors_len; - } - else { - for(i = 0; i < ca.anchors_len; ++i) - free(ca.anchors[i].dn.data); - free(ca.anchors); - } - - return ca.err; -} - -static void x509_start_chain(const br_x509_class **ctx, - const char *server_name) -{ - struct x509_context *x509 = (struct x509_context *)ctx; - - if(!x509->verifypeer) { - x509->cert_num = 0; - return; - } - - if(!x509->verifyhost) - server_name = NULL; - x509->minimal.vtable->start_chain(&x509->minimal.vtable, server_name); -} - -static void x509_start_cert(const br_x509_class **ctx, uint32_t length) -{ - struct x509_context *x509 = (struct x509_context *)ctx; - - if(!x509->verifypeer) { - /* Only decode the first cert in the chain to obtain the public key */ - if(x509->cert_num == 0) - br_x509_decoder_init(&x509->decoder, NULL, NULL); - return; - } - - x509->minimal.vtable->start_cert(&x509->minimal.vtable, length); -} - -static void x509_append(const br_x509_class **ctx, const unsigned char *buf, - size_t len) -{ - struct x509_context *x509 = (struct x509_context *)ctx; - - if(!x509->verifypeer) { - if(x509->cert_num == 0) - br_x509_decoder_push(&x509->decoder, buf, len); - return; - } - - x509->minimal.vtable->append(&x509->minimal.vtable, buf, len); -} - -static void x509_end_cert(const br_x509_class **ctx) -{ - struct x509_context *x509 = (struct x509_context *)ctx; - - if(!x509->verifypeer) { - x509->cert_num++; - return; - } - - x509->minimal.vtable->end_cert(&x509->minimal.vtable); -} - -static unsigned x509_end_chain(const br_x509_class **ctx) -{ - struct x509_context *x509 = (struct x509_context *)ctx; - - if(!x509->verifypeer) { - return (unsigned)br_x509_decoder_last_error(&x509->decoder); - } - - return x509->minimal.vtable->end_chain(&x509->minimal.vtable); -} - -static const br_x509_pkey *x509_get_pkey(const br_x509_class *const *ctx, - unsigned *usages) -{ - struct x509_context *x509 = (struct x509_context *)CURL_UNCONST(ctx); - - if(!x509->verifypeer) { - /* Nothing in the chain is verified, just return the public key of the - first certificate and allow its usage for both TLS_RSA_* and - TLS_ECDHE_* */ - if(usages) - *usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN; - return br_x509_decoder_get_pkey(&x509->decoder); - } - - return x509->minimal.vtable->get_pkey(&x509->minimal.vtable, usages); -} - -static const br_x509_class x509_vtable = { - sizeof(struct x509_context), - x509_start_chain, - x509_start_cert, - x509_append, - x509_end_cert, - x509_end_chain, - x509_get_pkey -}; - -static CURLcode -bearssl_set_ssl_version_min_max(struct Curl_easy *data, - br_ssl_engine_context *ssl_eng, - struct ssl_primary_config *conn_config) -{ - unsigned version_min, version_max; - - switch(conn_config->version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - case CURL_SSLVERSION_TLSv1_0: - version_min = BR_TLS10; - break; - case CURL_SSLVERSION_TLSv1_1: - version_min = BR_TLS11; - break; - case CURL_SSLVERSION_TLSv1_2: - version_min = BR_TLS12; - break; - case CURL_SSLVERSION_TLSv1_3: - failf(data, "BearSSL: does not support TLS 1.3"); - return CURLE_SSL_CONNECT_ERROR; - default: - failf(data, "BearSSL: unsupported minimum TLS version value"); - return CURLE_SSL_CONNECT_ERROR; - } - - switch(conn_config->version_max) { - case CURL_SSLVERSION_MAX_DEFAULT: - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_TLSv1_3: - case CURL_SSLVERSION_MAX_TLSv1_2: - version_max = BR_TLS12; - break; - case CURL_SSLVERSION_MAX_TLSv1_1: - version_max = BR_TLS11; - break; - case CURL_SSLVERSION_MAX_TLSv1_0: - version_max = BR_TLS10; - break; - default: - failf(data, "BearSSL: unsupported maximum TLS version value"); - return CURLE_SSL_CONNECT_ERROR; - } - - br_ssl_engine_set_versions(ssl_eng, version_min, version_max); - - return CURLE_OK; -} - -static const uint16_t ciphertable[] = { - /* RFC 2246 TLS 1.0 */ - BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x000A */ - - /* RFC 3268 TLS 1.0 AES */ - BR_TLS_RSA_WITH_AES_128_CBC_SHA, /* 0x002F */ - BR_TLS_RSA_WITH_AES_256_CBC_SHA, /* 0x0035 */ - - /* RFC 5246 TLS 1.2 */ - BR_TLS_RSA_WITH_AES_128_CBC_SHA256, /* 0x003C */ - BR_TLS_RSA_WITH_AES_256_CBC_SHA256, /* 0x003D */ - - /* RFC 5288 TLS 1.2 AES GCM */ - BR_TLS_RSA_WITH_AES_128_GCM_SHA256, /* 0x009C */ - BR_TLS_RSA_WITH_AES_256_GCM_SHA384, /* 0x009D */ - - /* RFC 4492 TLS 1.0 ECC */ - BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC003 */ - BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC004 */ - BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC005 */ - BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, /* 0xC008 */ - BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC009 */ - BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC00A */ - BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC00D */ - BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, /* 0xC00E */ - BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, /* 0xC00F */ - BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, /* 0xC012 */ - BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* 0xC013 */ - BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, /* 0xC014 */ - - /* RFC 5289 TLS 1.2 ECC HMAC SHA256/384 */ - BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC023 */ - BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC024 */ - BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC025 */ - BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC026 */ - BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, /* 0xC027 */ - BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, /* 0xC028 */ - BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, /* 0xC029 */ - BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, /* 0xC02A */ - - /* RFC 5289 TLS 1.2 GCM */ - BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02B */ - BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02C */ - BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02D */ - BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02E */ - BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* 0xC02F */ - BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, /* 0xC030 */ - BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, /* 0xC031 */ - BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, /* 0xC032 */ - -#ifdef BR_TLS_RSA_WITH_AES_128_CCM - /* RFC 6655 TLS 1.2 CCM - Supported since BearSSL 0.6 */ - BR_TLS_RSA_WITH_AES_128_CCM, /* 0xC09C */ - BR_TLS_RSA_WITH_AES_256_CCM, /* 0xC09D */ - BR_TLS_RSA_WITH_AES_128_CCM_8, /* 0xC0A0 */ - BR_TLS_RSA_WITH_AES_256_CCM_8, /* 0xC0A1 */ - - /* RFC 7251 TLS 1.2 ECC CCM - Supported since BearSSL 0.6 */ - BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM, /* 0xC0AC */ - BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM, /* 0xC0AD */ - BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, /* 0xC0AE */ - BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, /* 0xC0AF */ -#endif - - /* RFC 7905 TLS 1.2 ChaCha20-Poly1305 - Supported since BearSSL 0.2 */ - BR_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA8 */ - BR_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */ -}; - -#define NUM_OF_CIPHERS CURL_ARRAYSIZE(ciphertable) - -static CURLcode bearssl_set_selected_ciphers(struct Curl_easy *data, - br_ssl_engine_context *ssl_eng, - const char *ciphers) -{ - uint16_t selected[NUM_OF_CIPHERS]; - size_t count = 0, i; - const char *ptr, *end; - - for(ptr = ciphers; ptr[0] != '\0' && count < NUM_OF_CIPHERS; ptr = end) { - uint16_t id = Curl_cipher_suite_walk_str(&ptr, &end); - - /* Check if cipher is supported */ - if(id) { - for(i = 0; i < NUM_OF_CIPHERS && ciphertable[i] != id; i++); - if(i == NUM_OF_CIPHERS) - id = 0; - } - if(!id) { - if(ptr[0] != '\0') - infof(data, "BearSSL: unknown cipher in list: \"%.*s\"", - (int) (end - ptr), ptr); - continue; - } - - /* No duplicates allowed */ - for(i = 0; i < count && selected[i] != id; i++); - if(i < count) { - infof(data, "BearSSL: duplicate cipher in list: \"%.*s\"", - (int) (end - ptr), ptr); - continue; - } - - selected[count++] = id; - } - - if(count == 0) { - failf(data, "BearSSL: no supported cipher in list"); - return CURLE_SSL_CIPHER; - } - - br_ssl_engine_set_suites(ssl_eng, selected, count); - return CURLE_OK; -} - -static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; - const char * const ssl_cafile = - /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ - (ca_info_blob ? NULL : conn_config->CAfile); - const char *hostname = connssl->peer.hostname; - const bool verifypeer = conn_config->verifypeer; - const bool verifyhost = conn_config->verifyhost; - CURLcode ret; - int session_set = 0; - - DEBUGASSERT(backend); - CURL_TRC_CF(data, cf, "connect_step1"); - - if(verifypeer) { - if(ca_info_blob) { - struct cafile_source source; - source.type = CAFILE_SOURCE_BLOB; - source.data = ca_info_blob->data; - source.len = ca_info_blob->len; - - CURL_TRC_CF(data, cf, "connect_step1, load ca_info_blob"); - ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); - if(ret != CURLE_OK) { - failf(data, "error importing CA certificate blob"); - return ret; - } - } - - if(ssl_cafile) { - struct cafile_source source; - source.type = CAFILE_SOURCE_PATH; - source.data = ssl_cafile; - source.len = 0; - - CURL_TRC_CF(data, cf, "connect_step1, load cafile"); - ret = load_cafile(&source, &backend->anchors, &backend->anchors_len); - if(ret != CURLE_OK) { - failf(data, "error setting certificate verify locations." - " CAfile: %s", ssl_cafile); - return ret; - } - } - } - - /* initialize SSL context */ - br_ssl_client_init_full(&backend->ctx, &backend->x509.minimal, - backend->anchors, backend->anchors_len); - - ret = bearssl_set_ssl_version_min_max(data, &backend->ctx.eng, conn_config); - if(ret != CURLE_OK) - return ret; - - br_ssl_engine_set_buffer(&backend->ctx.eng, backend->buf, - sizeof(backend->buf), 1); - - if(conn_config->cipher_list) { - /* Override the ciphers as specified. For the default cipher list see the - BearSSL source code of br_ssl_client_init_full() */ - CURL_TRC_CF(data, cf, "connect_step1, set ciphers"); - ret = bearssl_set_selected_ciphers(data, &backend->ctx.eng, - conn_config->cipher_list); - if(ret) - return ret; - } - - /* initialize X.509 context */ - backend->x509.vtable = &x509_vtable; - backend->x509.verifypeer = verifypeer; - backend->x509.verifyhost = verifyhost; - br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable); - - if(ssl_config->primary.cache_session) { - struct Curl_ssl_session *sc_session = NULL; - - ret = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key, - &sc_session); - if(!ret && sc_session && sc_session->sdata && sc_session->sdata_len) { - const br_ssl_session_parameters *session; - session = (const br_ssl_session_parameters *)sc_session->sdata; - br_ssl_engine_set_session_parameters(&backend->ctx.eng, session); - session_set = 1; - infof(data, "BearSSL: reusing session ID"); - /* single use of sessions */ - Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, sc_session); - } - } - - if(connssl->alpn) { - struct alpn_proto_buf proto; - size_t i; - - for(i = 0; i < connssl->alpn->count; ++i) { - backend->protocols[i] = connssl->alpn->entries[i]; - } - br_ssl_engine_set_protocol_names(&backend->ctx.eng, backend->protocols, - connssl->alpn->count); - Curl_alpn_to_proto_str(&proto, connssl->alpn); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); - } - - if(connssl->peer.type != CURL_SSL_PEER_DNS) { - if(verifyhost) { - failf(data, "BearSSL: " - "host verification of IP address is not supported"); - return CURLE_PEER_FAILED_VERIFICATION; - } - hostname = NULL; - } - else { - if(!connssl->peer.sni) { - failf(data, "Failed to set SNI"); - return CURLE_SSL_CONNECT_ERROR; - } - hostname = connssl->peer.sni; - CURL_TRC_CF(data, cf, "connect_step1, SNI set"); - } - - /* give application a chance to interfere with SSL set up. */ - if(data->set.ssl.fsslctx) { - Curl_set_in_callback(data, TRUE); - ret = (*data->set.ssl.fsslctx)(data, &backend->ctx, - data->set.ssl.fsslctxp); - Curl_set_in_callback(data, FALSE); - if(ret) { - failf(data, "BearSSL: error signaled by ssl ctx callback"); - return ret; - } - } - - if(!br_ssl_client_reset(&backend->ctx, hostname, session_set)) - return CURLE_FAILED_INIT; - backend->active = TRUE; - - connssl->connecting_state = ssl_connect_2; - - return CURLE_OK; -} - -static CURLcode bearssl_run_until(struct Curl_cfilter *cf, - struct Curl_easy *data, - unsigned target) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; - unsigned state; - unsigned char *buf; - size_t len; - ssize_t ret; - CURLcode result; - int err; - - DEBUGASSERT(backend); - - connssl->io_need = CURL_SSL_IO_NEED_NONE; - for(;;) { - state = br_ssl_engine_current_state(&backend->ctx.eng); - if(state & BR_SSL_CLOSED) { - err = br_ssl_engine_last_error(&backend->ctx.eng); - switch(err) { - case BR_ERR_OK: - /* TLS close notify */ - if(connssl->state != ssl_connection_complete) { - failf(data, "SSL: connection closed during handshake"); - return CURLE_SSL_CONNECT_ERROR; - } - return CURLE_OK; - case BR_ERR_X509_EXPIRED: - failf(data, "SSL: X.509 verification: " - "certificate is expired or not yet valid"); - return CURLE_PEER_FAILED_VERIFICATION; - case BR_ERR_X509_BAD_SERVER_NAME: - failf(data, "SSL: X.509 verification: " - "expected server name was not found in the chain"); - return CURLE_PEER_FAILED_VERIFICATION; - case BR_ERR_X509_NOT_TRUSTED: - failf(data, "SSL: X.509 verification: " - "chain could not be linked to a trust anchor"); - return CURLE_PEER_FAILED_VERIFICATION; - default:; - } - failf(data, "BearSSL: connection error 0x%04x", err); - /* X.509 errors are documented to have the range 32..63 */ - if(err >= 32 && err < 64) - return CURLE_PEER_FAILED_VERIFICATION; - return CURLE_SSL_CONNECT_ERROR; - } - if(state & target) - return CURLE_OK; - if(state & BR_SSL_SENDREC) { - buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len); - ret = Curl_conn_cf_send(cf->next, data, (const char *)buf, len, FALSE, - &result); - CURL_TRC_CF(data, cf, "ssl_send(len=%zu) -> %zd, %d", len, ret, result); - if(ret <= 0) { - if(result == CURLE_AGAIN) - connssl->io_need |= CURL_SSL_IO_NEED_SEND; - return result; - } - br_ssl_engine_sendrec_ack(&backend->ctx.eng, ret); - } - else if(state & BR_SSL_RECVREC) { - buf = br_ssl_engine_recvrec_buf(&backend->ctx.eng, &len); - ret = Curl_conn_cf_recv(cf->next, data, (char *)buf, len, &result); - CURL_TRC_CF(data, cf, "ssl_recv(len=%zu) -> %zd, %d", len, ret, result); - if(ret == 0) { - failf(data, "SSL: EOF without close notify"); - return CURLE_RECV_ERROR; - } - if(ret <= 0) { - if(result == CURLE_AGAIN) - connssl->io_need |= CURL_SSL_IO_NEED_RECV; - return result; - } - br_ssl_engine_recvrec_ack(&backend->ctx.eng, ret); - } - } -} - -static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; - br_ssl_session_parameters session; - char cipher_str[64]; - CURLcode ret; - - DEBUGASSERT(backend); - CURL_TRC_CF(data, cf, "connect_step2"); - - ret = bearssl_run_until(cf, data, BR_SSL_SENDAPP | BR_SSL_RECVAPP); - if(ret == CURLE_AGAIN) - return CURLE_OK; - if(ret == CURLE_OK) { - unsigned int tver; - int subver = 0; - - if(br_ssl_engine_current_state(&backend->ctx.eng) == BR_SSL_CLOSED) { - failf(data, "SSL: connection closed during handshake"); - return CURLE_SSL_CONNECT_ERROR; - } - connssl->connecting_state = ssl_connect_3; - /* Informational message */ - tver = br_ssl_engine_get_version(&backend->ctx.eng); - switch(tver) { - case BR_TLS12: - subver = 2; /* 1.2 */ - break; - case BR_TLS11: - subver = 1; /* 1.1 */ - break; - case BR_TLS10: /* 1.0 */ - default: /* unknown, leave it at zero */ - break; - } - br_ssl_engine_get_session_parameters(&backend->ctx.eng, &session); - Curl_cipher_suite_get_str(session.cipher_suite, cipher_str, - sizeof(cipher_str), TRUE); - infof(data, "BearSSL: TLS v1.%d connection using %s", subver, - cipher_str); - } - return ret; -} - -static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - CURLcode ret; - - DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); - DEBUGASSERT(backend); - CURL_TRC_CF(data, cf, "connect_step3"); - - if(connssl->alpn) { - const char *proto; - - proto = br_ssl_engine_get_selected_protocol(&backend->ctx.eng); - Curl_alpn_set_negotiated(cf, data, connssl, (const unsigned char *)proto, - proto ? strlen(proto) : 0); - } - - if(ssl_config->primary.cache_session) { - struct Curl_ssl_session *sc_session; - br_ssl_session_parameters *session; - - session = malloc(sizeof(*session)); - if(!session) - return CURLE_OUT_OF_MEMORY; - br_ssl_engine_get_session_parameters(&backend->ctx.eng, session); - ret = Curl_ssl_session_create((unsigned char *)session, sizeof(*session), - (int)session->version, - connssl->negotiated.alpn, - 0, 0, &sc_session); - if(!ret) { - ret = Curl_ssl_scache_put(cf, data, connssl->peer.scache_key, - sc_session); - /* took ownership of `sc_session` */ - } - if(ret) - return ret; - } - - connssl->connecting_state = ssl_connect_done; - - return CURLE_OK; -} - -static ssize_t bearssl_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; - unsigned char *app; - size_t applen; - - DEBUGASSERT(backend); - - for(;;) { - *err = bearssl_run_until(cf, data, BR_SSL_SENDAPP); - if(*err) - return -1; - app = br_ssl_engine_sendapp_buf(&backend->ctx.eng, &applen); - if(!app) { - failf(data, "SSL: connection closed during write"); - *err = CURLE_SEND_ERROR; - return -1; - } - if(backend->pending_write) { - applen = backend->pending_write; - backend->pending_write = 0; - return applen; - } - if(applen > len) - applen = len; - memcpy(app, buf, applen); - br_ssl_engine_sendapp_ack(&backend->ctx.eng, applen); - br_ssl_engine_flush(&backend->ctx.eng, 0); - backend->pending_write = applen; - } -} - -static ssize_t bearssl_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; - unsigned char *app; - size_t applen; - - DEBUGASSERT(backend); - - *err = bearssl_run_until(cf, data, BR_SSL_RECVAPP); - if(*err != CURLE_OK) - return -1; - app = br_ssl_engine_recvapp_buf(&backend->ctx.eng, &applen); - if(!app) - return 0; - if(applen > len) - applen = len; - memcpy(buf, app, applen); - br_ssl_engine_recvapp_ack(&backend->ctx.eng, applen); - - return applen; -} - -static CURLcode bearssl_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) -{ - CURLcode ret; - struct ssl_connect_data *connssl = cf->ctx; - - CURL_TRC_CF(data, cf, "connect()"); - /* check if the connection has already been established */ - if(ssl_connection_complete == connssl->state) { - CURL_TRC_CF(data, cf, "connect_common, connected"); - *done = TRUE; - return CURLE_OK; - } - - *done = FALSE; - connssl->io_need = CURL_SSL_IO_NEED_NONE; - - if(ssl_connect_1 == connssl->connecting_state) { - ret = bearssl_connect_step1(cf, data); - if(ret) - return ret; - } - - if(ssl_connect_2 == connssl->connecting_state) { - ret = bearssl_connect_step2(cf, data); - if(ret) - return ret; - } - - if(ssl_connect_3 == connssl->connecting_state) { - ret = bearssl_connect_step3(cf, data); - if(ret) - return ret; - } - - if(ssl_connect_done == connssl->connecting_state) { - connssl->state = ssl_connection_complete; - *done = TRUE; - } - - return CURLE_OK; -} - -static size_t bearssl_version(char *buffer, size_t size) -{ - return msnprintf(buffer, size, "BearSSL"); -} - -static bool bearssl_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - struct ssl_connect_data *ctx = cf->ctx; - struct bearssl_ssl_backend_data *backend; - - (void)data; - DEBUGASSERT(ctx && ctx->backend); - backend = (struct bearssl_ssl_backend_data *)ctx->backend; - return br_ssl_engine_current_state(&backend->ctx.eng) & BR_SSL_RECVAPP; -} - -static CURLcode bearssl_random(struct Curl_easy *data UNUSED_PARAM, - unsigned char *entropy, size_t length) -{ - static br_hmac_drbg_context ctx; - static bool seeded = FALSE; - - if(!seeded) { - br_prng_seeder seeder; - - br_hmac_drbg_init(&ctx, &br_sha256_vtable, NULL, 0); - seeder = br_prng_seeder_system(NULL); - if(!seeder || !seeder(&ctx.vtable)) - return CURLE_FAILED_INIT; - seeded = TRUE; - } - br_hmac_drbg_generate(&ctx, entropy, length); - - return CURLE_OK; -} - -static void *bearssl_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; - DEBUGASSERT(backend); - return &backend->ctx; -} - -static CURLcode bearssl_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool send_shutdown, bool *done) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; - CURLcode result; - - DEBUGASSERT(backend); - if(!backend->active || cf->shutdown) { - *done = TRUE; - return CURLE_OK; - } - - *done = FALSE; - if(!backend->sent_shutdown) { - (void)send_shutdown; /* unknown how to suppress our close notify */ - br_ssl_engine_close(&backend->ctx.eng); - backend->sent_shutdown = TRUE; - } - - result = bearssl_run_until(cf, data, BR_SSL_CLOSED); - if(result == CURLE_OK) { - *done = TRUE; - } - else if(result == CURLE_AGAIN) { - CURL_TRC_CF(data, cf, "shutdown EAGAIN, io_need=%x", connssl->io_need); - result = CURLE_OK; - } - else - CURL_TRC_CF(data, cf, "shutdown error: %d", result); - - cf->shutdown = (result || *done); - return result; -} - -static void bearssl_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct bearssl_ssl_backend_data *backend = - (struct bearssl_ssl_backend_data *)connssl->backend; - size_t i; - - (void)data; - DEBUGASSERT(backend); - - backend->active = FALSE; - if(backend->anchors) { - for(i = 0; i < backend->anchors_len; ++i) - free(backend->anchors[i].dn.data); - Curl_safefree(backend->anchors); - } -} - -static CURLcode bearssl_sha256sum(const unsigned char *input, - size_t inputlen, - unsigned char *sha256sum, - size_t sha256len UNUSED_PARAM) -{ - br_sha256_context ctx; - - br_sha256_init(&ctx); - br_sha256_update(&ctx, input, inputlen); - br_sha256_out(&ctx, sha256sum); - return CURLE_OK; -} - -const struct Curl_ssl Curl_ssl_bearssl = { - { CURLSSLBACKEND_BEARSSL, "bearssl" }, /* info */ - - SSLSUPP_CAINFO_BLOB | - SSLSUPP_SSL_CTX | - SSLSUPP_HTTPS_PROXY | - SSLSUPP_CIPHER_LIST, - - sizeof(struct bearssl_ssl_backend_data), - - NULL, /* init */ - NULL, /* cleanup */ - bearssl_version, /* version */ - bearssl_shutdown, /* shutdown */ - bearssl_data_pending, /* data_pending */ - bearssl_random, /* random */ - NULL, /* cert_status_request */ - bearssl_connect, /* connect */ - Curl_ssl_adjust_pollset, /* adjust_pollset */ - bearssl_get_internals, /* get_internals */ - bearssl_close, /* close_one */ - NULL, /* close_all */ - NULL, /* set_engine */ - NULL, /* set_engine_default */ - NULL, /* engines_list */ - NULL, /* false_start */ - bearssl_sha256sum, /* sha256sum */ - bearssl_recv, /* recv decrypted data */ - bearssl_send, /* send data to encrypt */ - NULL, /* get_channel_binding */ -}; - -#endif /* USE_BEARSSL */ diff --git a/lib/vtls/cipher_suite.c b/lib/vtls/cipher_suite.c index d058bd3f0e..cefc92c580 100644 --- a/lib/vtls/cipher_suite.c +++ b/lib/vtls/cipher_suite.c @@ -23,11 +23,9 @@ ***************************************************************************/ #include "../curl_setup.h" -#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \ - defined(USE_BEARSSL) || defined(USE_RUSTLS) +#if defined(USE_MBEDTLS) || defined(USE_RUSTLS) #include "cipher_suite.h" #include "../curl_printf.h" -#include "../strcase.h" #include /* @@ -90,21 +88,6 @@ static const char *cs_txt = "CAMELLIA128" "\0" "CAMELLIA256" "\0" #endif -#if defined(USE_SECTRANSP) - "40" "\0" - "ADH" "\0" - "AECDH" "\0" - "anon" "\0" - "DES40" "\0" - "DH" "\0" - "DSS" "\0" - "EDH" "\0" - "EXP" "\0" - "EXPORT" "\0" - "IDEA" "\0" - "RC2" "\0" - "RC4" "\0" -#endif ; /* Indexes of above cs_txt */ enum { @@ -145,21 +128,6 @@ enum { CS_TXT_IDX_CAMELLIA, CS_TXT_IDX_CAMELLIA128, CS_TXT_IDX_CAMELLIA256, -#endif -#if defined(USE_SECTRANSP) - CS_TXT_IDX_40, - CS_TXT_IDX_ADH, - CS_TXT_IDX_AECDH, - CS_TXT_IDX_anon, - CS_TXT_IDX_DES40, - CS_TXT_IDX_DH, - CS_TXT_IDX_DSS, - CS_TXT_IDX_EDH, - CS_TXT_IDX_EXP, - CS_TXT_IDX_EXPORT, - CS_TXT_IDX_IDEA, - CS_TXT_IDX_RC2, - CS_TXT_IDX_RC4, #endif CS_TXT_LEN, }; @@ -192,7 +160,7 @@ struct cs_entry { /* !checksrc! disable COMMANOSPACE all */ static const struct cs_entry cs_list [] = { /* TLS 1.3 ciphers */ -#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || defined(USE_RUSTLS) +#if defined(USE_MBEDTLS) || defined(USE_RUSTLS) CS_ENTRY(0x1301, TLS,AES,128,GCM,SHA256,,,), CS_ENTRY(0x1302, TLS,AES,256,GCM,SHA384,,,), CS_ENTRY(0x1303, TLS,CHACHA20,POLY1305,SHA256,,,,), @@ -212,7 +180,7 @@ static const struct cs_entry cs_list [] = { CS_ENTRY(0xCCA8, ECDHE,RSA,CHACHA20,POLY1305,,,,), CS_ENTRY(0xCCA9, TLS,ECDHE,ECDSA,WITH,CHACHA20,POLY1305,SHA256,), CS_ENTRY(0xCCA9, ECDHE,ECDSA,CHACHA20,POLY1305,,,,), -#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || defined(USE_BEARSSL) +#if defined(USE_MBEDTLS) CS_ENTRY(0x002F, TLS,RSA,WITH,AES,128,CBC,SHA,), CS_ENTRY(0x002F, AES128,SHA,,,,,,), CS_ENTRY(0x0035, TLS,RSA,WITH,AES,256,CBC,SHA,), @@ -266,7 +234,7 @@ static const struct cs_entry cs_list [] = { CS_ENTRY(0xC032, TLS,ECDH,RSA,WITH,AES,256,GCM,SHA384), CS_ENTRY(0xC032, ECDH,RSA,AES256,GCM,SHA384,,,), #endif -#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) +#if defined(USE_MBEDTLS) CS_ENTRY(0x0001, TLS,RSA,WITH,NULL,MD5,,,), CS_ENTRY(0x0001, NULL,MD5,,,,,,), CS_ENTRY(0x0002, TLS,RSA,WITH,NULL,SHA,,,), @@ -354,19 +322,7 @@ static const struct cs_entry cs_list [] = { CS_ENTRY(0xCCAB, TLS,PSK,WITH,CHACHA20,POLY1305,SHA256,,), CS_ENTRY(0xCCAB, PSK,CHACHA20,POLY1305,,,,,), #endif -#if defined(USE_SECTRANSP) || defined(USE_BEARSSL) - CS_ENTRY(0x000A, TLS,RSA,WITH,3DES,EDE,CBC,SHA,), - CS_ENTRY(0x000A, DES,CBC3,SHA,,,,,), - CS_ENTRY(0xC003, TLS,ECDH,ECDSA,WITH,3DES,EDE,CBC,SHA), - CS_ENTRY(0xC003, ECDH,ECDSA,DES,CBC3,SHA,,,), - CS_ENTRY(0xC008, TLS,ECDHE,ECDSA,WITH,3DES,EDE,CBC,SHA), - CS_ENTRY(0xC008, ECDHE,ECDSA,DES,CBC3,SHA,,,), - CS_ENTRY(0xC00D, TLS,ECDH,RSA,WITH,3DES,EDE,CBC,SHA), - CS_ENTRY(0xC00D, ECDH,RSA,DES,CBC3,SHA,,,), - CS_ENTRY(0xC012, TLS,ECDHE,RSA,WITH,3DES,EDE,CBC,SHA), - CS_ENTRY(0xC012, ECDHE,RSA,DES,CBC3,SHA,,,), -#endif -#if defined(USE_MBEDTLS) || defined(USE_BEARSSL) +#if defined(USE_MBEDTLS) CS_ENTRY(0xC09C, TLS,RSA,WITH,AES,128,CCM,,), CS_ENTRY(0xC09C, AES128,CCM,,,,,,), CS_ENTRY(0xC09D, TLS,RSA,WITH,AES,256,CCM,,), @@ -384,141 +340,6 @@ static const struct cs_entry cs_list [] = { CS_ENTRY(0xC0AF, TLS,ECDHE,ECDSA,WITH,AES,256,CCM,8), CS_ENTRY(0xC0AF, ECDHE,ECDSA,AES256,CCM8,,,,), #endif -#if defined(USE_SECTRANSP) - /* entries marked bc are backward compatible aliases for old OpenSSL names */ - CS_ENTRY(0x0003, TLS,RSA,EXPORT,WITH,RC4,40,MD5,), - CS_ENTRY(0x0003, EXP,RC4,MD5,,,,,), - CS_ENTRY(0x0004, TLS,RSA,WITH,RC4,128,MD5,,), - CS_ENTRY(0x0004, RC4,MD5,,,,,,), - CS_ENTRY(0x0005, TLS,RSA,WITH,RC4,128,SHA,,), - CS_ENTRY(0x0005, RC4,SHA,,,,,,), - CS_ENTRY(0x0006, TLS,RSA,EXPORT,WITH,RC2,CBC,40,MD5), - CS_ENTRY(0x0006, EXP,RC2,CBC,MD5,,,,), - CS_ENTRY(0x0007, TLS,RSA,WITH,IDEA,CBC,SHA,,), - CS_ENTRY(0x0007, IDEA,CBC,SHA,,,,,), - CS_ENTRY(0x0008, TLS,RSA,EXPORT,WITH,DES40,CBC,SHA,), - CS_ENTRY(0x0008, EXP,DES,CBC,SHA,,,,), - CS_ENTRY(0x0009, TLS,RSA,WITH,DES,CBC,SHA,,), - CS_ENTRY(0x0009, DES,CBC,SHA,,,,,), - CS_ENTRY(0x000B, TLS,DH,DSS,EXPORT,WITH,DES40,CBC,SHA), - CS_ENTRY(0x000B, EXP,DH,DSS,DES,CBC,SHA,,), - CS_ENTRY(0x000C, TLS,DH,DSS,WITH,DES,CBC,SHA,), - CS_ENTRY(0x000C, DH,DSS,DES,CBC,SHA,,,), - CS_ENTRY(0x000D, TLS,DH,DSS,WITH,3DES,EDE,CBC,SHA), - CS_ENTRY(0x000D, DH,DSS,DES,CBC3,SHA,,,), - CS_ENTRY(0x000E, TLS,DH,RSA,EXPORT,WITH,DES40,CBC,SHA), - CS_ENTRY(0x000E, EXP,DH,RSA,DES,CBC,SHA,,), - CS_ENTRY(0x000F, TLS,DH,RSA,WITH,DES,CBC,SHA,), - CS_ENTRY(0x000F, DH,RSA,DES,CBC,SHA,,,), - CS_ENTRY(0x0010, TLS,DH,RSA,WITH,3DES,EDE,CBC,SHA), - CS_ENTRY(0x0010, DH,RSA,DES,CBC3,SHA,,,), - CS_ENTRY(0x0011, TLS,DHE,DSS,EXPORT,WITH,DES40,CBC,SHA), - CS_ENTRY(0x0011, EXP,DHE,DSS,DES,CBC,SHA,,), - CS_ENTRY(0x0011, EXP,EDH,DSS,DES,CBC,SHA,,), /* bc */ - CS_ENTRY(0x0012, TLS,DHE,DSS,WITH,DES,CBC,SHA,), - CS_ENTRY(0x0012, DHE,DSS,DES,CBC,SHA,,,), - CS_ENTRY(0x0012, EDH,DSS,DES,CBC,SHA,,,), /* bc */ - CS_ENTRY(0x0013, TLS,DHE,DSS,WITH,3DES,EDE,CBC,SHA), - CS_ENTRY(0x0013, DHE,DSS,DES,CBC3,SHA,,,), - CS_ENTRY(0x0013, EDH,DSS,DES,CBC3,SHA,,,), /* bc */ - CS_ENTRY(0x0014, TLS,DHE,RSA,EXPORT,WITH,DES40,CBC,SHA), - CS_ENTRY(0x0014, EXP,DHE,RSA,DES,CBC,SHA,,), - CS_ENTRY(0x0014, EXP,EDH,RSA,DES,CBC,SHA,,), /* bc */ - CS_ENTRY(0x0015, TLS,DHE,RSA,WITH,DES,CBC,SHA,), - CS_ENTRY(0x0015, DHE,RSA,DES,CBC,SHA,,,), - CS_ENTRY(0x0015, EDH,RSA,DES,CBC,SHA,,,), /* bc */ - CS_ENTRY(0x0016, TLS,DHE,RSA,WITH,3DES,EDE,CBC,SHA), - CS_ENTRY(0x0016, DHE,RSA,DES,CBC3,SHA,,,), - CS_ENTRY(0x0016, EDH,RSA,DES,CBC3,SHA,,,), /* bc */ - CS_ENTRY(0x0017, TLS,DH,anon,EXPORT,WITH,RC4,40,MD5), - CS_ENTRY(0x0017, EXP,ADH,RC4,MD5,,,,), - CS_ENTRY(0x0018, TLS,DH,anon,WITH,RC4,128,MD5,), - CS_ENTRY(0x0018, ADH,RC4,MD5,,,,,), - CS_ENTRY(0x0019, TLS,DH,anon,EXPORT,WITH,DES40,CBC,SHA), - CS_ENTRY(0x0019, EXP,ADH,DES,CBC,SHA,,,), - CS_ENTRY(0x001A, TLS,DH,anon,WITH,DES,CBC,SHA,), - CS_ENTRY(0x001A, ADH,DES,CBC,SHA,,,,), - CS_ENTRY(0x001B, TLS,DH,anon,WITH,3DES,EDE,CBC,SHA), - CS_ENTRY(0x001B, ADH,DES,CBC3,SHA,,,,), - CS_ENTRY(0x0030, TLS,DH,DSS,WITH,AES,128,CBC,SHA), - CS_ENTRY(0x0030, DH,DSS,AES128,SHA,,,,), - CS_ENTRY(0x0031, TLS,DH,RSA,WITH,AES,128,CBC,SHA), - CS_ENTRY(0x0031, DH,RSA,AES128,SHA,,,,), - CS_ENTRY(0x0032, TLS,DHE,DSS,WITH,AES,128,CBC,SHA), - CS_ENTRY(0x0032, DHE,DSS,AES128,SHA,,,,), - CS_ENTRY(0x0034, TLS,DH,anon,WITH,AES,128,CBC,SHA), - CS_ENTRY(0x0034, ADH,AES128,SHA,,,,,), - CS_ENTRY(0x0036, TLS,DH,DSS,WITH,AES,256,CBC,SHA), - CS_ENTRY(0x0036, DH,DSS,AES256,SHA,,,,), - CS_ENTRY(0x0037, TLS,DH,RSA,WITH,AES,256,CBC,SHA), - CS_ENTRY(0x0037, DH,RSA,AES256,SHA,,,,), - CS_ENTRY(0x0038, TLS,DHE,DSS,WITH,AES,256,CBC,SHA), - CS_ENTRY(0x0038, DHE,DSS,AES256,SHA,,,,), - CS_ENTRY(0x003A, TLS,DH,anon,WITH,AES,256,CBC,SHA), - CS_ENTRY(0x003A, ADH,AES256,SHA,,,,,), - CS_ENTRY(0x003E, TLS,DH,DSS,WITH,AES,128,CBC,SHA256), - CS_ENTRY(0x003E, DH,DSS,AES128,SHA256,,,,), - CS_ENTRY(0x003F, TLS,DH,RSA,WITH,AES,128,CBC,SHA256), - CS_ENTRY(0x003F, DH,RSA,AES128,SHA256,,,,), - CS_ENTRY(0x0040, TLS,DHE,DSS,WITH,AES,128,CBC,SHA256), - CS_ENTRY(0x0040, DHE,DSS,AES128,SHA256,,,,), - CS_ENTRY(0x0068, TLS,DH,DSS,WITH,AES,256,CBC,SHA256), - CS_ENTRY(0x0068, DH,DSS,AES256,SHA256,,,,), - CS_ENTRY(0x0069, TLS,DH,RSA,WITH,AES,256,CBC,SHA256), - CS_ENTRY(0x0069, DH,RSA,AES256,SHA256,,,,), - CS_ENTRY(0x006A, TLS,DHE,DSS,WITH,AES,256,CBC,SHA256), - CS_ENTRY(0x006A, DHE,DSS,AES256,SHA256,,,,), - CS_ENTRY(0x006C, TLS,DH,anon,WITH,AES,128,CBC,SHA256), - CS_ENTRY(0x006C, ADH,AES128,SHA256,,,,,), - CS_ENTRY(0x006D, TLS,DH,anon,WITH,AES,256,CBC,SHA256), - CS_ENTRY(0x006D, ADH,AES256,SHA256,,,,,), - CS_ENTRY(0x008A, TLS,PSK,WITH,RC4,128,SHA,,), - CS_ENTRY(0x008A, PSK,RC4,SHA,,,,,), - CS_ENTRY(0x008B, TLS,PSK,WITH,3DES,EDE,CBC,SHA,), - CS_ENTRY(0x008B, PSK,3DES,EDE,CBC,SHA,,,), - CS_ENTRY(0x008E, TLS,DHE,PSK,WITH,RC4,128,SHA,), - CS_ENTRY(0x008E, DHE,PSK,RC4,SHA,,,,), - CS_ENTRY(0x008F, TLS,DHE,PSK,WITH,3DES,EDE,CBC,SHA), - CS_ENTRY(0x008F, DHE,PSK,3DES,EDE,CBC,SHA,,), - CS_ENTRY(0x0092, TLS,RSA,PSK,WITH,RC4,128,SHA,), - CS_ENTRY(0x0092, RSA,PSK,RC4,SHA,,,,), - CS_ENTRY(0x0093, TLS,RSA,PSK,WITH,3DES,EDE,CBC,SHA), - CS_ENTRY(0x0093, RSA,PSK,3DES,EDE,CBC,SHA,,), - CS_ENTRY(0x00A0, TLS,DH,RSA,WITH,AES,128,GCM,SHA256), - CS_ENTRY(0x00A0, DH,RSA,AES128,GCM,SHA256,,,), - CS_ENTRY(0x00A1, TLS,DH,RSA,WITH,AES,256,GCM,SHA384), - CS_ENTRY(0x00A1, DH,RSA,AES256,GCM,SHA384,,,), - CS_ENTRY(0x00A2, TLS,DHE,DSS,WITH,AES,128,GCM,SHA256), - CS_ENTRY(0x00A2, DHE,DSS,AES128,GCM,SHA256,,,), - CS_ENTRY(0x00A3, TLS,DHE,DSS,WITH,AES,256,GCM,SHA384), - CS_ENTRY(0x00A3, DHE,DSS,AES256,GCM,SHA384,,,), - CS_ENTRY(0x00A4, TLS,DH,DSS,WITH,AES,128,GCM,SHA256), - CS_ENTRY(0x00A4, DH,DSS,AES128,GCM,SHA256,,,), - CS_ENTRY(0x00A5, TLS,DH,DSS,WITH,AES,256,GCM,SHA384), - CS_ENTRY(0x00A5, DH,DSS,AES256,GCM,SHA384,,,), - CS_ENTRY(0x00A6, TLS,DH,anon,WITH,AES,128,GCM,SHA256), - CS_ENTRY(0x00A6, ADH,AES128,GCM,SHA256,,,,), - CS_ENTRY(0x00A7, TLS,DH,anon,WITH,AES,256,GCM,SHA384), - CS_ENTRY(0x00A7, ADH,AES256,GCM,SHA384,,,,), - CS_ENTRY(0xC002, TLS,ECDH,ECDSA,WITH,RC4,128,SHA,), - CS_ENTRY(0xC002, ECDH,ECDSA,RC4,SHA,,,,), - CS_ENTRY(0xC007, TLS,ECDHE,ECDSA,WITH,RC4,128,SHA,), - CS_ENTRY(0xC007, ECDHE,ECDSA,RC4,SHA,,,,), - CS_ENTRY(0xC00C, TLS,ECDH,RSA,WITH,RC4,128,SHA,), - CS_ENTRY(0xC00C, ECDH,RSA,RC4,SHA,,,,), - CS_ENTRY(0xC011, TLS,ECDHE,RSA,WITH,RC4,128,SHA,), - CS_ENTRY(0xC011, ECDHE,RSA,RC4,SHA,,,,), - CS_ENTRY(0xC015, TLS,ECDH,anon,WITH,NULL,SHA,,), - CS_ENTRY(0xC015, AECDH,NULL,SHA,,,,,), - CS_ENTRY(0xC016, TLS,ECDH,anon,WITH,RC4,128,SHA,), - CS_ENTRY(0xC016, AECDH,RC4,SHA,,,,,), - CS_ENTRY(0xC017, TLS,ECDH,anon,WITH,3DES,EDE,CBC,SHA), - CS_ENTRY(0xC017, AECDH,DES,CBC3,SHA,,,,), - CS_ENTRY(0xC018, TLS,ECDH,anon,WITH,AES,128,CBC,SHA), - CS_ENTRY(0xC018, AECDH,AES128,SHA,,,,,), - CS_ENTRY(0xC019, TLS,ECDH,anon,WITH,AES,256,CBC,SHA), - CS_ENTRY(0xC019, AECDH,AES256,SHA,,,,,), -#endif #if defined(USE_MBEDTLS) /* entries marked ns are "non-standard", they are not in OpenSSL */ CS_ENTRY(0x0041, TLS,RSA,WITH,CAMELLIA,128,CBC,SHA,), @@ -739,7 +560,7 @@ static int cs_str_to_zip(const char *cs_str, size_t cs_len, size_t len; /* split the cipher string by '-' or '_' */ - if(strncasecompare(cs_str, "TLS", 3)) + if(curl_strnequal(cs_str, "TLS", 3)) separator = '_'; do { @@ -754,7 +575,7 @@ static int cs_str_to_zip(const char *cs_str, size_t cs_len, /* lookup index for the part (skip empty string at 0) */ for(idx = 1, entry = cs_txt + 1; idx < CS_TXT_LEN; idx++) { size_t elen = strlen(entry); - if(elen == len && strncasecompare(entry, cur, len)) + if(elen == len && curl_strnequal(entry, cur, len)) break; entry += elen + 1; } @@ -887,5 +708,4 @@ int Curl_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size, return r; } -#endif /* defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \ - defined(USE_BEARSSL) || defined(USE_RUSTLS) */ +#endif /* defined(USE_MBEDTLS) || defined(USE_RUSTLS) */ diff --git a/lib/vtls/cipher_suite.h b/lib/vtls/cipher_suite.h index cd556db10f..a1dcbc524f 100644 --- a/lib/vtls/cipher_suite.h +++ b/lib/vtls/cipher_suite.h @@ -26,8 +26,7 @@ #include "../curl_setup.h" -#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \ - defined(USE_BEARSSL) || defined(USE_RUSTLS) +#if defined(USE_MBEDTLS) || defined(USE_RUSTLS) #include /* Lookup IANA id for cipher suite string, returns 0 if not recognized */ @@ -43,6 +42,5 @@ uint16_t Curl_cipher_suite_walk_str(const char **str, const char **end); int Curl_cipher_suite_get_str(uint16_t id, char *buf, size_t buf_size, bool prefer_rfc); -#endif /* defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \ - defined(USE_BEARSSL) || defined(USE_RUSTLS) */ +#endif /* defined(USE_MBEDTLS) || defined(USE_RUSTLS) */ #endif /* HEADER_CURL_CIPHER_SUITE_H */ diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c index 74c36fed94..f5e02975a0 100644 --- a/lib/vtls/gtls.c +++ b/lib/vtls/gtls.c @@ -53,7 +53,6 @@ #include "../connect.h" /* for the connect timeout */ #include "../progress.h" #include "../select.h" -#include "../strcase.h" #include "../strdup.h" #include "../curlx/warnless.h" #include "x509asn1.h" @@ -91,21 +90,21 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen) struct gtls_ssl_backend_data *backend = (struct gtls_ssl_backend_data *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nwritten; + size_t nwritten; CURLcode result; DEBUGASSERT(data); - nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &result); - CURL_TRC_CF(data, cf, "gtls_push(len=%zu) -> %zd, err=%d", - blen, nwritten, result); + result = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &nwritten); + CURL_TRC_CF(data, cf, "gtls_push(len=%zu) -> %d, %zu", + blen, result, nwritten); backend->gtls.io_result = result; - if(nwritten < 0) { + if(result) { /* !checksrc! disable ERRNOVAR 1 */ gnutls_transport_set_errno(backend->gtls.session, (CURLE_AGAIN == result) ? EAGAIN : EINVAL); - nwritten = -1; + return -1; } - return nwritten; + return (ssize_t)nwritten; } static ssize_t gtls_pull(void *s, void *buf, size_t blen) @@ -115,7 +114,7 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen) struct gtls_ssl_backend_data *backend = (struct gtls_ssl_backend_data *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nread; + size_t nread; CURLcode result; DEBUGASSERT(data); @@ -129,19 +128,19 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen) } } - nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); - CURL_TRC_CF(data, cf, "glts_pull(len=%zu) -> %zd, err=%d", - blen, nread, result); + result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread); + CURL_TRC_CF(data, cf, "glts_pull(len=%zu) -> %d, %zd", + blen, result, nread); backend->gtls.io_result = result; - if(nread < 0) { + if(result) { /* !checksrc! disable ERRNOVAR 1 */ gnutls_transport_set_errno(backend->gtls.session, (CURLE_AGAIN == result) ? EAGAIN : EINVAL); - nread = -1; + return -1; } else if(nread == 0) connssl->peer_closed = TRUE; - return nread; + return (ssize_t)nread; } /* gtls_init() @@ -303,9 +302,9 @@ static gnutls_x509_crt_fmt_t gnutls_do_file_type(const char *type) { if(!type || !type[0]) return GNUTLS_X509_FMT_PEM; - if(strcasecompare(type, "PEM")) + if(curl_strequal(type, "PEM")) return GNUTLS_X509_FMT_PEM; - if(strcasecompare(type, "DER")) + if(curl_strequal(type, "DER")) return GNUTLS_X509_FMT_DER; return GNUTLS_X509_FMT_PEM; /* default to PEM */ } @@ -784,9 +783,9 @@ static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype, } static CURLcode gtls_set_priority(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct gtls_ctx *gtls, - const char *priority) + struct Curl_easy *data, + struct gtls_ctx *gtls, + const char *priority) { struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); struct dynbuf buf; @@ -982,7 +981,7 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf, if(result) return result; } - if(ssl_config->cert_type && strcasecompare(ssl_config->cert_type, "P12")) { + if(ssl_config->cert_type && curl_strequal(ssl_config->cert_type, "P12")) { rc = gnutls_certificate_set_x509_simple_pkcs12_file( gtls->shared_creds->creds, config->clientcert, GNUTLS_X509_FMT_DER, ssl_config->key_passwd ? ssl_config->key_passwd : ""); @@ -1080,7 +1079,7 @@ static CURLcode gtls_on_session_reuse(struct Curl_cfilter *cf, connssl->earlydata_max = gnutls_record_get_max_early_data_size(backend->gtls.session); if((!connssl->earlydata_max || connssl->earlydata_max == 0xFFFFFFFFUL)) { - /* Seems to be GnuTLS way to signal no EarlyData in session */ + /* Seems to be no GnuTLS way to signal no EarlyData in session */ CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); } else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { @@ -1308,6 +1307,28 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, return result; } +void Curl_gtls_report_handshake(struct Curl_easy *data, + struct gtls_ctx *gctx) +{ +#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(Curl_trc_is_verbose(data)) { + const char *ptr; + gnutls_protocol_t version = gnutls_protocol_get_version(gctx->session); + + /* the name of the cipher suite used, e.g. ECDHE_RSA_AES_256_GCM_SHA384. */ + ptr = gnutls_cipher_suite_get_name(gnutls_kx_get(gctx->session), + gnutls_cipher_get(gctx->session), + gnutls_mac_get(gctx->session)); + + infof(data, "SSL connection using %s / %s", + gnutls_protocol_get_name(version), ptr); + } +#else + (void)data; + (void)gctx; +#endif +} + CURLcode Curl_gtls_verifyserver(struct Curl_easy *data, gnutls_session_t session, @@ -1328,23 +1349,11 @@ Curl_gtls_verifyserver(struct Curl_easy *data, int rc; CURLcode result = CURLE_OK; #ifndef CURL_DISABLE_VERBOSE_STRINGS - const char *ptr; int algo; unsigned int bits; - gnutls_protocol_t version = gnutls_protocol_get_version(session); #endif long * const certverifyresult = &ssl_config->certverifyresult; -#ifndef CURL_DISABLE_VERBOSE_STRINGS - /* the name of the cipher suite used, e.g. ECDHE_RSA_AES_256_GCM_SHA384. */ - ptr = gnutls_cipher_suite_get_name(gnutls_kx_get(session), - gnutls_cipher_get(session), - gnutls_mac_get(session)); - - infof(data, "SSL connection using %s / %s", - gnutls_protocol_get_name(version), ptr); -#endif - /* This function will return the peer's raw certificate (chain) as sent by the peer. These certificates are in raw format (DER encoded for X.509). In case of a X.509 then a certificate list may be present. The @@ -1877,6 +1886,9 @@ static CURLcode gtls_connect_common(struct Curl_cfilter *cf, if(connssl->connecting_state == ssl_connect_3) { gnutls_datum_t proto; int rc; + + Curl_gtls_report_handshake(data, &backend->gtls); + result = gtls_verifyserver(cf, data, backend->gtls.session); if(result) goto out; @@ -1948,48 +1960,49 @@ static bool gtls_data_pending(struct Curl_cfilter *cf, return FALSE; } -static ssize_t gtls_send(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *buf, - size_t blen, - CURLcode *curlcode) +static CURLcode gtls_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *buf, + size_t blen, + size_t *pnwritten) { struct ssl_connect_data *connssl = cf->ctx; struct gtls_ssl_backend_data *backend = (struct gtls_ssl_backend_data *)connssl->backend; - ssize_t rc; - size_t nwritten, total_written = 0; + CURLcode result = CURLE_OK; + ssize_t nwritten; + size_t remain = blen; (void)data; DEBUGASSERT(backend); + *pnwritten = 0; - while(blen) { + while(remain) { backend->gtls.io_result = CURLE_OK; - rc = gnutls_record_send(backend->gtls.session, buf, blen); + nwritten = gnutls_record_send(backend->gtls.session, buf, remain); - if(rc < 0) { - if(total_written && (rc == GNUTLS_E_AGAIN)) { - *curlcode = CURLE_OK; - rc = (ssize_t)total_written; + if(nwritten >= 0) { + *pnwritten += (size_t)nwritten; + DEBUGASSERT((size_t)nwritten <= remain); + buf = (char *)CURL_UNCONST(buf) + (size_t)nwritten; + remain -= (size_t)nwritten; + } + else { + if(*pnwritten && (nwritten == GNUTLS_E_AGAIN)) { + result = CURLE_OK; goto out; } - *curlcode = (rc == GNUTLS_E_AGAIN) ? + result = (nwritten == GNUTLS_E_AGAIN) ? CURLE_AGAIN : (backend->gtls.io_result ? backend->gtls.io_result : CURLE_SEND_ERROR); - - rc = -1; goto out; } - nwritten = (size_t)rc; - total_written += nwritten; - DEBUGASSERT(nwritten <= blen); - buf = (char *)CURL_UNCONST(buf) + nwritten; - blen -= nwritten; } - rc = total_written; out: - return rc; + CURL_TRC_CF(data, cf, "gtls_send(len=%zu) -> %d, %zu", + blen, result, *pnwritten); + return result; } /* @@ -2095,50 +2108,49 @@ static void gtls_close(struct Curl_cfilter *cf, #endif } -static ssize_t gtls_recv(struct Curl_cfilter *cf, - struct Curl_easy *data, - char *buf, - size_t buffersize, - CURLcode *curlcode) +static CURLcode gtls_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t blen, + size_t *pnread) { struct ssl_connect_data *connssl = cf->ctx; struct gtls_ssl_backend_data *backend = (struct gtls_ssl_backend_data *)connssl->backend; - ssize_t ret; + CURLcode result = CURLE_OK; + ssize_t nread; (void)data; DEBUGASSERT(backend); - ret = gnutls_record_recv(backend->gtls.session, buf, buffersize); - if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) { - *curlcode = CURLE_AGAIN; - ret = -1; - goto out; - } + nread = gnutls_record_recv(backend->gtls.session, buf, blen); - if(ret == GNUTLS_E_REHANDSHAKE) { - /* BLOCKING call, this is bad but a work-around for now. Fixing this "the - proper way" takes a whole lot of work. */ - CURLcode result = handshake(cf, data); - if(result) - *curlcode = result; - else - *curlcode = CURLE_AGAIN; /* then return as if this was a wouldblock */ - ret = -1; - goto out; - } - - if(ret < 0) { - failf(data, "GnuTLS recv error (%d): %s", - (int)ret, gnutls_strerror((int)ret)); - *curlcode = backend->gtls.io_result ? - backend->gtls.io_result : CURLE_RECV_ERROR; - ret = -1; - goto out; + if(nread >= 0) + *pnread = (size_t)nread; + else { + if((nread == GNUTLS_E_AGAIN) || (nread == GNUTLS_E_INTERRUPTED)) { + result = CURLE_AGAIN; + goto out; + } + else if(nread == GNUTLS_E_REHANDSHAKE) { + /* BLOCKING call, this is bad but a work-around for now. Fixing this "the + proper way" takes a whole lot of work. */ + result = handshake(cf, data); + if(!result) + result = CURLE_AGAIN; /* then return as if this was a wouldblock */ + goto out; + } + else { + failf(data, "GnuTLS recv error (%d): %s", + (int)nread, gnutls_strerror((int)nread)); + result = backend->gtls.io_result ? + backend->gtls.io_result : CURLE_RECV_ERROR; + goto out; + } } out: - return ret; + CURL_TRC_CF(data, cf, "gtls_recv(len=%zu) -> 0, %zu", blen, *pnread); + return result; } size_t Curl_gtls_version(char *buffer, size_t size) @@ -2210,7 +2222,6 @@ const struct Curl_ssl Curl_ssl_gnutls = { NULL, /* set_engine */ NULL, /* set_engine_default */ NULL, /* engines_list */ - NULL, /* false_start */ gtls_sha256sum, /* sha256sum */ gtls_recv, /* recv decrypted data */ gtls_send, /* send data to encrypt */ diff --git a/lib/vtls/gtls.h b/lib/vtls/gtls.h index 35af9db139..01f8b43ac8 100644 --- a/lib/vtls/gtls.h +++ b/lib/vtls/gtls.h @@ -117,6 +117,10 @@ CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf, unsigned char *quic_tp, size_t quic_tp_len); +/* Report properties of a successful handshake */ +void Curl_gtls_report_handshake(struct Curl_easy *data, + struct gtls_ctx *gctx); + extern const struct Curl_ssl Curl_ssl_gnutls; #endif /* USE_GNUTLS */ diff --git a/lib/vtls/hostcheck.c b/lib/vtls/hostcheck.c index 8ca69941e8..68af9be57d 100644 --- a/lib/vtls/hostcheck.c +++ b/lib/vtls/hostcheck.c @@ -36,7 +36,6 @@ #endif #include "../curl_memrchr.h" #include "hostcheck.h" -#include "../strcase.h" #include "../hostip.h" #include "../curl_memory.h" @@ -50,7 +49,7 @@ static bool pmatch(const char *hostname, size_t hostlen, { if(hostlen != patternlen) return FALSE; - return strncasecompare(hostname, pattern, hostlen); + return curl_strnequal(hostname, pattern, hostlen); } /* diff --git a/lib/vtls/keylog.c b/lib/vtls/keylog.c index 8487d43e8c..2fd25089d9 100644 --- a/lib/vtls/keylog.c +++ b/lib/vtls/keylog.c @@ -134,14 +134,14 @@ Curl_tls_keylog_write(const char *label, /* Client Random */ for(i = 0; i < CLIENT_RANDOM_SIZE; i++) { - Curl_hexbyte(&line[pos], client_random[i], FALSE); + Curl_hexbyte(&line[pos], client_random[i]); pos += 2; } line[pos++] = ' '; /* Secret */ for(i = 0; i < secretlen; i++) { - Curl_hexbyte(&line[pos], secret[i], FALSE); + Curl_hexbyte(&line[pos], secret[i]); pos += 2; } line[pos++] = '\n'; diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c index 7af207caa7..75190d30f3 100644 --- a/lib/vtls/mbedtls.c +++ b/lib/vtls/mbedtls.c @@ -57,7 +57,6 @@ #endif /* MBEDTLS_VERSION_MAJOR >= 2 */ #include "cipher_suite.h" -#include "../strcase.h" #include "../urldata.h" #include "../sendf.h" #include "../curlx/inet_pton.h" @@ -191,28 +190,27 @@ static int mbedtls_bio_cf_write(void *bio, { struct Curl_cfilter *cf = bio; struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nwritten; + size_t nwritten; CURLcode result; DEBUGASSERT(data); if(!data) return 0; - nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, blen, FALSE, - &result); - CURL_TRC_CF(data, cf, "mbedtls_bio_cf_out_write(len=%zu) -> %zd, err=%d", - blen, nwritten, result); - if(nwritten < 0 && CURLE_AGAIN == result) { - nwritten = MBEDTLS_ERR_SSL_WANT_WRITE; - } - return (int)nwritten; + result = Curl_conn_cf_send(cf->next, data, (const char *)buf, blen, FALSE, + &nwritten); + CURL_TRC_CF(data, cf, "mbedtls_bio_cf_out_write(len=%zu) -> %d, %zu", + blen, result, nwritten); + if(CURLE_AGAIN == result) + return MBEDTLS_ERR_SSL_WANT_WRITE; + return result ? -1 : (int)nwritten; } static int mbedtls_bio_cf_read(void *bio, unsigned char *buf, size_t blen) { struct Curl_cfilter *cf = bio; struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nread; + size_t nread; CURLcode result; DEBUGASSERT(data); @@ -222,13 +220,12 @@ static int mbedtls_bio_cf_read(void *bio, unsigned char *buf, size_t blen) if(!buf) return 0; - nread = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &result); - CURL_TRC_CF(data, cf, "mbedtls_bio_cf_in_read(len=%zu) -> %zd, err=%d", - blen, nread, result); - if(nread < 0 && CURLE_AGAIN == result) { - nread = MBEDTLS_ERR_SSL_WANT_READ; - } - return (int)nread; + result = Curl_conn_cf_recv(cf->next, data, (char *)buf, blen, &nread); + CURL_TRC_CF(data, cf, "mbedtls_bio_cf_in_read(len=%zu) -> %d, %zu", + blen, result, nread); + if(CURLE_AGAIN == result) + return MBEDTLS_ERR_SSL_WANT_READ; + return result ? -1 : (int)nread; } /* @@ -384,7 +381,7 @@ mbed_cipher_suite_walk_str(const char **str, const char **end) size_t len = *end - *str; if(!id) { - if(strncasecompare("TLS_ECJPAKE_WITH_AES_128_CCM_8", *str, len)) + if(curl_strnequal("TLS_ECJPAKE_WITH_AES_128_CCM_8", *str, len)) id = MBEDTLS_TLS_ECJPAKE_WITH_AES_128_CCM_8; } return id; @@ -1205,16 +1202,18 @@ out: return result; } -static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *mem, size_t len, - CURLcode *curlcode) +static CURLcode mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *mem, size_t len, + size_t *pnwritten) { struct ssl_connect_data *connssl = cf->ctx; struct mbed_ssl_backend_data *backend = (struct mbed_ssl_backend_data *)connssl->backend; - int ret = -1; + CURLcode result = CURLE_OK; + int nwritten; (void)data; + *pnwritten = 0; DEBUGASSERT(backend); /* mbedtls is picky when a mbedtls_ssl_write) was previously blocked. * It requires to be called with the same amount of bytes again, or it @@ -1227,28 +1226,29 @@ static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data, len = backend->send_blocked_len; } - ret = mbedtls_ssl_write(&backend->ssl, (const unsigned char *)mem, len); + nwritten = mbedtls_ssl_write(&backend->ssl, (const unsigned char *)mem, len); - if(ret < 0) { + if(nwritten >= 0) { + *pnwritten = (size_t)nwritten; + backend->send_blocked = FALSE; + } + else { CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> -0x%04X", - len, -ret); - *curlcode = ((ret == MBEDTLS_ERR_SSL_WANT_WRITE) + len, -nwritten); + result = ((nwritten == MBEDTLS_ERR_SSL_WANT_WRITE) #ifdef HAS_TLS13_SUPPORT - || (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) + || (nwritten == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET) #endif ) ? CURLE_AGAIN : CURLE_SEND_ERROR; - ret = -1; - if((*curlcode == CURLE_AGAIN) && !backend->send_blocked) { + if((result == CURLE_AGAIN) && !backend->send_blocked) { backend->send_blocked = TRUE; backend->send_blocked_len = len; } } - else { - CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> %d", len, ret); - backend->send_blocked = FALSE; - } - return ret; + CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> %d, %zu", + len, result, *pnwritten); + return result; } static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf, @@ -1367,48 +1367,48 @@ static void mbedtls_close(struct Curl_cfilter *cf, struct Curl_easy *data) } } -static ssize_t mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t buffersize, - CURLcode *curlcode) +static CURLcode mbed_recv(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t buffersize, + size_t *pnread) { struct ssl_connect_data *connssl = cf->ctx; struct mbed_ssl_backend_data *backend = (struct mbed_ssl_backend_data *)connssl->backend; - int ret = -1; + CURLcode result = CURLE_OK; + int nread; (void)data; DEBUGASSERT(backend); + *pnread = 0; - ret = mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, - buffersize); - if(ret <= 0) { + nread = mbedtls_ssl_read(&backend->ssl, (unsigned char *)buf, buffersize); + if(nread > 0) + *pnread = (size_t)nread; + else { CURL_TRC_CF(data, cf, "mbedtls_ssl_read(len=%zu) -> -0x%04X", - buffersize, -ret); - switch(ret) { + buffersize, -nread); + switch(nread) { #ifdef HAS_SESSION_TICKETS case MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET: mbed_new_session(cf, data); FALLTHROUGH(); #endif case MBEDTLS_ERR_SSL_WANT_READ: - *curlcode = CURLE_AGAIN; - ret = -1; + result = CURLE_AGAIN; break; case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: - *curlcode = CURLE_OK; - ret = 0; + result = CURLE_OK; break; default: { char errorbuf[128]; - mbedtls_strerror(ret, errorbuf, sizeof(errorbuf)); - failf(data, "ssl_read returned: (-0x%04X) %s", -ret, errorbuf); - *curlcode = CURLE_RECV_ERROR; - ret = -1; + mbedtls_strerror(nread, errorbuf, sizeof(errorbuf)); + failf(data, "ssl_read returned: (-0x%04X) %s", -nread, errorbuf); + result = CURLE_RECV_ERROR; break; } } } - return (ssize_t)ret; + return result; } static size_t mbedtls_version(char *buffer, size_t size) @@ -1620,7 +1620,6 @@ const struct Curl_ssl Curl_ssl_mbedtls = { NULL, /* set_engine */ NULL, /* set_engine_default */ NULL, /* engines_list */ - NULL, /* false_start */ mbedtls_sha256sum, /* sha256sum */ mbed_recv, /* recv decrypted data */ mbed_send, /* send data to encrypt */ diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 0ce09ca06b..b0f91cf400 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -54,12 +54,12 @@ #include "../connect.h" #include "../slist.h" #include "../select.h" +#include "../curlx/wait.h" #include "vtls.h" #include "vtls_int.h" #include "vtls_scache.h" #include "../vauth/vauth.h" #include "keylog.h" -#include "../strcase.h" #include "hostcheck.h" #include "../multiif.h" #include "../curlx/strparse.h" @@ -122,6 +122,12 @@ static void ossl_provider_cleanup(struct Curl_easy *data); #endif +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && \ + !defined(LIBRESSL_VERSION_NUMBER) && \ + !defined(OPENSSL_IS_BORINGSSL)) + #define HAVE_SSL_CTX_SET_DEFAULT_READ_BUFFER_LEN 1 +#endif + #include "../curlx/warnless.h" /* The last #include files should be: */ @@ -717,22 +723,23 @@ static int ossl_bio_cf_out_write(BIO *bio, const char *buf, int blen) struct ssl_connect_data *connssl = cf->ctx; struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nwritten; - CURLcode result = CURLE_SEND_ERROR; + size_t nwritten; + CURLcode result; DEBUGASSERT(data); if(blen < 0) return 0; - nwritten = Curl_conn_cf_send(cf->next, data, buf, (size_t)blen, FALSE, - &result); - CURL_TRC_CF(data, cf, "ossl_bio_cf_out_write(len=%d) -> %d, err=%d", - blen, (int)nwritten, result); + result = Curl_conn_cf_send(cf->next, data, buf, (size_t)blen, FALSE, + &nwritten); + CURL_TRC_CF(data, cf, "ossl_bio_cf_out_write(len=%d) -> %d, %zu", + blen, result, nwritten); BIO_clear_retry_flags(bio); octx->io_result = result; - if(nwritten < 0) { + if(result) { if(CURLE_AGAIN == result) BIO_set_retry_write(bio); + return -1; } return (int)nwritten; } @@ -743,8 +750,8 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen) struct ssl_connect_data *connssl = cf->ctx; struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nread; - CURLcode result = CURLE_RECV_ERROR; + size_t nread; + CURLcode result, r2; DEBUGASSERT(data); /* OpenSSL catches this case, so should we. */ @@ -753,31 +760,33 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen) if(blen < 0) return 0; - nread = Curl_conn_cf_recv(cf->next, data, buf, (size_t)blen, &result); - CURL_TRC_CF(data, cf, "ossl_bio_cf_in_read(len=%d) -> %d, err=%d", - blen, (int)nread, result); + result = Curl_conn_cf_recv(cf->next, data, buf, (size_t)blen, &nread); + CURL_TRC_CF(data, cf, "ossl_bio_cf_in_read(len=%d) -> %d, %zu", + blen, result, nread); BIO_clear_retry_flags(bio); octx->io_result = result; - if(nread < 0) { + if(result) { if(CURLE_AGAIN == result) BIO_set_retry_read(bio); } - else if(nread == 0) { - connssl->peer_closed = TRUE; + else { + /* feeding data to OpenSSL means SSL_read() might succeed */ + connssl->input_pending = TRUE; + if(nread == 0) + connssl->peer_closed = TRUE; } /* Before returning server replies to the SSL instance, we need * to have setup the x509 store or verification will fail. */ if(!octx->x509_store_setup) { - result = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx); - if(result) { - octx->io_result = result; + r2 = Curl_ssl_setup_x509_store(cf, data, octx->ssl_ctx); + if(r2) { + octx->io_result = r2; return -1; } octx->x509_store_setup = TRUE; } - - return (int)nread; + return result ? -1 : (int)nread; } #if OPENSSL_VERSION_NUMBER < 0x10100000L @@ -995,7 +1004,7 @@ static CURLcode ossl_seed(struct Curl_easy *data) size_t i, i_max; for(i = 0, i_max = len / sizeof(struct curltime); i < i_max; ++i) { struct curltime tv = curlx_now(); - Curl_wait_ms(1); + curlx_wait_ms(1); tv.tv_sec *= (time_t)i + 1; tv.tv_usec *= (int)i + 2; tv.tv_sec ^= ((curlx_now().tv_sec + (time_t)curlx_now().tv_usec) * @@ -1048,15 +1057,15 @@ static int ossl_do_file_type(const char *type) { if(!type || !type[0]) return SSL_FILETYPE_PEM; - if(strcasecompare(type, "PEM")) + if(curl_strequal(type, "PEM")) return SSL_FILETYPE_PEM; - if(strcasecompare(type, "DER")) + if(curl_strequal(type, "DER")) return SSL_FILETYPE_ASN1; - if(strcasecompare(type, "PROV")) + if(curl_strequal(type, "PROV")) return SSL_FILETYPE_PROVIDER; - if(strcasecompare(type, "ENG")) + if(curl_strequal(type, "ENG")) return SSL_FILETYPE_ENGINE; - if(strcasecompare(type, "P12")) + if(curl_strequal(type, "P12")) return SSL_FILETYPE_PKCS12; return -1; } @@ -1109,7 +1118,7 @@ static int ssl_ui_writer(UI *ui, UI_STRING *uis) */ static bool is_pkcs11_uri(const char *string) { - return string && strncasecompare(string, "pkcs11:", 7); + return string && curl_strnequal(string, "pkcs11:", 7); } #endif @@ -1375,7 +1384,7 @@ int cert_stuff(struct Curl_easy *data, { /* Implicitly use pkcs11 provider if none was provided and the * cert_file is a PKCS#11 URI */ - if(!data->state.provider) { + if(!data->state.provider_loaded) { if(is_pkcs11_uri(cert_file)) { if(ossl_set_provider(data, "pkcs11") != CURLE_OK) { return 0; @@ -1383,7 +1392,7 @@ int cert_stuff(struct Curl_easy *data, } } - if(data->state.provider) { + if(data->state.provider_loaded) { /* Load the certificate from the provider */ OSSL_STORE_INFO *info = NULL; X509 *cert = NULL; @@ -1628,7 +1637,7 @@ fail: { /* Implicitly use pkcs11 provider if none was provided and the * key_file is a PKCS#11 URI */ - if(!data->state.provider) { + if(!data->state.provider_loaded) { if(is_pkcs11_uri(key_file)) { if(ossl_set_provider(data, "pkcs11") != CURLE_OK) { return 0; @@ -1636,7 +1645,7 @@ fail: } } - if(data->state.provider) { + if(data->state.provider_loaded) { /* Load the private key from the provider */ EVP_PKEY *priv_key = NULL; OSSL_STORE_CTX *store = NULL; @@ -1898,6 +1907,9 @@ static CURLcode ossl_set_engine(struct Curl_easy *data, const char *name) result = CURLE_SSL_ENGINE_INITFAILED; e = NULL; } + else { + result = CURLE_OK; + } data->state.engine = e; return result; } @@ -2019,6 +2031,14 @@ static CURLcode ossl_set_provider(struct Curl_easy *data, const char *iname) data->state.libctx = libctx; } +#ifndef CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG + /* load the configuration file into the library context before checking the + * provider availability */ + if(!OSSL_LIB_CTX_load_config(data->state.libctx, NULL)) { + infof(data, "Failed to load default openssl config. Proceeding."); + } +#endif + if(OSSL_PROVIDER_available(data->state.libctx, name)) { /* already loaded through the configuration - no action needed */ data->state.provider_loaded = TRUE; @@ -2164,6 +2184,8 @@ static CURLcode ossl_shutdown(struct Curl_cfilter *cf, out: cf->shutdown = (result || *done); + if(cf->shutdown || (connssl->io_need != CURL_SSL_IO_NEED_NONE)) + connssl->input_pending = FALSE; return result; } @@ -2175,6 +2197,7 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data) (void)data; DEBUGASSERT(octx); + connssl->input_pending = FALSE; if(octx->ssl) { SSL_free(octx->ssl); octx->ssl = NULL; @@ -2845,8 +2868,8 @@ ossl_set_ssl_version_min_max(struct Curl_cfilter *cf, SSL_CTX *ctx) long curl_ssl_version_max; /* convert curl min SSL version option to OpenSSL constant */ -#if (defined(OPENSSL_IS_BORINGSSL) || \ - defined(OPENSSL_IS_AWSLC) || \ +#if (defined(OPENSSL_IS_BORINGSSL) || \ + defined(OPENSSL_IS_AWSLC) || \ defined(LIBRESSL_VERSION_NUMBER)) uint16_t ossl_ssl_version_min = 0; uint16_t ossl_ssl_version_max = 0; @@ -4112,6 +4135,21 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, } SSL_CTX_set_options(octx->ssl_ctx, ctx_options); + SSL_CTX_set_read_ahead(octx->ssl_ctx, 1); + + /* Max TLS1.2 record size 0x4000 + 0x800. + OpenSSL supports processing "jumbo TLS record" (8 TLS records) in one go + for some algorithms, so match that here. + Experimentation shows that a slightly larger buffer is needed + to avoid short reads. + + However using a large buffer (8 packets) actually decreases performance. + 4 packets is better. + */ + +#ifdef HAVE_SSL_CTX_SET_DEFAULT_READ_BUFFER_LEN + SSL_CTX_set_default_read_buffer_len(octx->ssl_ctx, 0x401e * 4); +#endif #ifdef SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER /* We do retry writes sometimes from another buffer address */ @@ -4296,6 +4334,38 @@ static CURLcode ossl_on_session_reuse(struct Curl_cfilter *cf, return result; } +void Curl_ossl_report_handshake(struct Curl_easy *data, + struct ossl_ctx *octx) +{ +#ifndef CURL_DISABLE_VERBOSE_STRINGS + if(Curl_trc_is_verbose(data)) { + int psigtype_nid = NID_undef; + const char *negotiated_group_name = NULL; + +#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) + SSL_get_peer_signature_type_nid(octx->ssl, &psigtype_nid); +#if (OPENSSL_VERSION_NUMBER >= 0x30200000L) + negotiated_group_name = SSL_get0_group_name(octx->ssl); +#else + negotiated_group_name = + OBJ_nid2sn(SSL_get_negotiated_group(octx->ssl) & 0x0000FFFF); +#endif +#endif + + /* Informational message */ + infof(data, "SSL connection using %s / %s / %s / %s", + SSL_get_version(octx->ssl), + SSL_get_cipher(octx->ssl), + negotiated_group_name ? negotiated_group_name : "[blank]", + OBJ_nid2sn(psigtype_nid)); + } +#else + (void)data; + (void)octx; +#endif /* CURL_DISABLE_VERBOSE_STRINGS */ + +} + static CURLcode ossl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { @@ -4561,28 +4631,9 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, } } else { - int psigtype_nid = NID_undef; - const char *negotiated_group_name = NULL; - /* we connected fine, we are not waiting for anything else. */ connssl->connecting_state = ssl_connect_3; - -#if (OPENSSL_VERSION_NUMBER >= 0x30000000L) - SSL_get_peer_signature_type_nid(octx->ssl, &psigtype_nid); -#if (OPENSSL_VERSION_NUMBER >= 0x30200000L) - negotiated_group_name = SSL_get0_group_name(octx->ssl); -#else - negotiated_group_name = - OBJ_nid2sn(SSL_get_negotiated_group(octx->ssl) & 0x0000FFFF); -#endif -#endif - - /* Informational message */ - infof(data, "SSL connection using %s / %s / %s / %s", - SSL_get_version(octx->ssl), - SSL_get_cipher(octx->ssl), - negotiated_group_name ? negotiated_group_name : "[blank]", - OBJ_nid2sn(psigtype_nid)); + Curl_ossl_report_handshake(data, octx); #ifdef USE_ECH_OPENSSL # if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC) @@ -4639,10 +4690,10 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf, infof(data, "ECH: ech-hard failed"); return CURLE_SSL_CONNECT_ERROR; } - } - else { + } + else { infof(data, "ECH: result: status is not attempted"); - } + } # endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */ #endif /* USE_ECH_OPENSSL */ @@ -5195,20 +5246,15 @@ static bool ossl_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) { struct ssl_connect_data *connssl = cf->ctx; - struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; - (void)data; - DEBUGASSERT(connssl && octx); - if(octx->ssl && SSL_pending(octx->ssl)) - return TRUE; - return FALSE; + return connssl->input_pending; } -static ssize_t ossl_send(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *mem, - size_t len, - CURLcode *curlcode) +static CURLcode ossl_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *mem, + size_t len, + size_t *pnwritten) { /* SSL_write() is said to return 'int' while write() and send() returns 'size_t' */ @@ -5216,39 +5262,39 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, char error_buffer[256]; sslerr_t sslerror; int memlen; - int rc; struct ssl_connect_data *connssl = cf->ctx; struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + CURLcode result = CURLE_OK; + int nwritten; (void)data; DEBUGASSERT(octx); - + *pnwritten = 0; ERR_clear_error(); connssl->io_need = CURL_SSL_IO_NEED_NONE; memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len; - rc = SSL_write(octx->ssl, mem, memlen); + nwritten = SSL_write(octx->ssl, mem, memlen); - if(rc <= 0) { - err = SSL_get_error(octx->ssl, rc); + if(nwritten > 0) + *pnwritten = (size_t)nwritten; + else { + err = SSL_get_error(octx->ssl, nwritten); switch(err) { case SSL_ERROR_WANT_READ: connssl->io_need = CURL_SSL_IO_NEED_RECV; - *curlcode = CURLE_AGAIN; - rc = -1; + result = CURLE_AGAIN; goto out; case SSL_ERROR_WANT_WRITE: - *curlcode = CURLE_AGAIN; - rc = -1; + result = CURLE_AGAIN; goto out; case SSL_ERROR_SYSCALL: { int sockerr = SOCKERRNO; if(octx->io_result == CURLE_AGAIN) { - *curlcode = CURLE_AGAIN; - rc = -1; + result = CURLE_AGAIN; goto out; } sslerror = ERR_get_error(); @@ -5262,8 +5308,7 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d", error_buffer, sockerr); - *curlcode = CURLE_SEND_ERROR; - rc = -1; + result = CURLE_SEND_ERROR; goto out; } case SSL_ERROR_SSL: { @@ -5272,49 +5317,50 @@ static ssize_t ossl_send(struct Curl_cfilter *cf, sslerror = ERR_get_error(); failf(data, "SSL_write() error: %s", ossl_strerror(sslerror, error_buffer, sizeof(error_buffer))); - *curlcode = CURLE_SEND_ERROR; - rc = -1; + result = CURLE_SEND_ERROR; goto out; } default: /* a true error */ failf(data, OSSL_PACKAGE " SSL_write: %s, errno %d", SSL_ERROR_to_str(err), SOCKERRNO); - *curlcode = CURLE_SEND_ERROR; - rc = -1; + result = CURLE_SEND_ERROR; goto out; } } - *curlcode = CURLE_OK; out: - return (ssize_t)rc; /* number of bytes */ + return result; } -static ssize_t ossl_recv(struct Curl_cfilter *cf, - struct Curl_easy *data, /* transfer */ - char *buf, /* store read data here */ - size_t buffersize, /* max amount to read */ - CURLcode *curlcode) +static CURLcode ossl_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, /* transfer */ + char *buf, /* store read data here */ + size_t buffersize, /* max amount to read */ + size_t *pnread) { char error_buffer[256]; unsigned long sslerror; - ssize_t nread; int buffsize; struct connectdata *conn = cf->conn; struct ssl_connect_data *connssl = cf->ctx; struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend; + CURLcode result = CURLE_OK; + int nread; (void)data; DEBUGASSERT(octx); + *pnread = 0; ERR_clear_error(); connssl->io_need = CURL_SSL_IO_NEED_NONE; buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize; - nread = (ssize_t)SSL_read(octx->ssl, buf, buffsize); + nread = SSL_read(octx->ssl, buf, buffsize); - if(nread <= 0) { + if(nread > 0) + *pnread = (size_t)nread; + else { /* failed SSL_read */ int err = SSL_get_error(octx->ssl, (int)nread); @@ -5329,21 +5375,18 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, connclose(conn, "TLS close_notify"); break; case SSL_ERROR_WANT_READ: - *curlcode = CURLE_AGAIN; - nread = -1; + result = CURLE_AGAIN; goto out; case SSL_ERROR_WANT_WRITE: connssl->io_need = CURL_SSL_IO_NEED_SEND; - *curlcode = CURLE_AGAIN; - nread = -1; + result = CURLE_AGAIN; goto out; default: /* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return value/errno" */ /* https://docs.openssl.org/master/man3/ERR_get_error/ */ if(octx->io_result == CURLE_AGAIN) { - *curlcode = CURLE_AGAIN; - nread = -1; + result = CURLE_AGAIN; goto out; } sslerror = ERR_get_error(); @@ -5360,40 +5403,50 @@ static ssize_t ossl_recv(struct Curl_cfilter *cf, SSL_ERROR_to_str(err)); failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d", error_buffer, sockerr); - *curlcode = CURLE_RECV_ERROR; - nread = -1; + result = CURLE_RECV_ERROR; goto out; } - /* For debug builds be a little stricter and error on any - SSL_ERROR_SYSCALL. For example a server may have closed the connection - abruptly without a close_notify alert. For compatibility with older - peers we do not do this by default. #4624 - - We can use this to gauge how many users may be affected, and - if it goes ok eventually transition to allow in dev and release with - the newest OpenSSL: #if (OPENSSL_VERSION_NUMBER >= 0x10101000L) */ -#ifdef DEBUGBUILD - if(err == SSL_ERROR_SYSCALL) { - int sockerr = SOCKERRNO; - if(sockerr) - Curl_strerror(sockerr, error_buffer, sizeof(error_buffer)); + else if(err == SSL_ERROR_SYSCALL) { + if(octx->io_result) { + /* logging handling in underlying filter already */ + result = octx->io_result; + } + else if(connssl->peer_closed) { + failf(data, "Connection closed abruptly"); + result = CURLE_RECV_ERROR; + } else { - msnprintf(error_buffer, sizeof(error_buffer), - "Connection closed abruptly"); + /* We should no longer get here nowadays. But handle + * the error in case of some weirdness in the OSSL stack */ + int sockerr = SOCKERRNO; + if(sockerr) + Curl_strerror(sockerr, error_buffer, sizeof(error_buffer)); + else { + msnprintf(error_buffer, sizeof(error_buffer), + "Connection closed abruptly"); + } + failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d", + error_buffer, sockerr); + result = CURLE_RECV_ERROR; } - failf(data, OSSL_PACKAGE " SSL_read: %s, errno %d" - " (Fatal because this is a curl debug build)", - error_buffer, sockerr); - *curlcode = CURLE_RECV_ERROR; - nread = -1; goto out; } -#endif } } out: - return nread; + if((!result && !*pnread) || (result == CURLE_AGAIN)) { + /* This happens when: + * - we read an EOF + * - OpenSSLs buffers are empty, there is no more data + * - OpenSSL read is blocked on writing something first + * - an incomplete TLS packet is buffered that cannot be read + * until more data arrives */ + connssl->input_pending = FALSE; + } + CURL_TRC_CF(data, cf, "ossl_recv(len=%zu) -> %d, %zu (in_pending=%d)", + buffersize, result, *pnread, connssl->input_pending); + return result; } static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex, @@ -5487,7 +5540,7 @@ size_t Curl_ossl_version(char *buffer, size_t size) size_t count; const char *ver = OpenSSL_version(OPENSSL_VERSION); const char expected[] = OSSL_PACKAGE " "; /* ie "LibreSSL " */ - if(strncasecompare(ver, expected, sizeof(expected) - 1)) { + if(curl_strnequal(ver, expected, sizeof(expected) - 1)) { ver += sizeof(expected) - 1; } count = msnprintf(buffer, size, "%s/%s", OSSL_PACKAGE, ver); @@ -5645,7 +5698,6 @@ const struct Curl_ssl Curl_ssl_openssl = { ossl_set_engine, /* set_engine or provider */ ossl_set_engine_default, /* set_engine_default */ ossl_engines_list, /* engines_list */ - NULL, /* false_start */ #ifndef OPENSSL_NO_SHA256 ossl_sha256sum, /* sha256sum */ #else diff --git a/lib/vtls/openssl.h b/lib/vtls/openssl.h index 8d063e25ac..1338eafb58 100644 --- a/lib/vtls/openssl.h +++ b/lib/vtls/openssl.h @@ -142,5 +142,9 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf, struct ossl_ctx *octx, struct ssl_peer *peer); +/* Report properties of a successful handshake */ +void Curl_ossl_report_handshake(struct Curl_easy *data, + struct ossl_ctx *octx); + #endif /* USE_OPENSSL */ #endif /* HEADER_CURL_SSLUSE_H */ diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c index cb9fd6230a..4e1b78a759 100644 --- a/lib/vtls/rustls.c +++ b/lib/vtls/rustls.c @@ -43,6 +43,10 @@ #include "cipher_suite.h" #include "x509asn1.h" +/* The last #include files should be: */ +#include "../curl_memory.h" +#include "../memdebug.h" + struct rustls_ssl_backend_data { const struct rustls_client_config *config; @@ -101,9 +105,11 @@ read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n) struct ssl_connect_data *const connssl = io_ctx->cf->ctx; CURLcode result; int ret = 0; - ssize_t nread = Curl_conn_cf_recv(io_ctx->cf->next, io_ctx->data, - (char *)buf, len, &result); - if(nread < 0) { + size_t nread; + + result = Curl_conn_cf_recv(io_ctx->cf->next, io_ctx->data, + (char *)buf, len, &nread); + if(result) { nread = 0; /* !checksrc! disable ERRNOVAR 4 */ if(CURLE_AGAIN == result) @@ -114,8 +120,8 @@ read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n) else if(nread == 0) connssl->peer_closed = TRUE; *out_n = (uintptr_t)nread; - CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next recv(len=%zu) -> %zd, %d", - len, nread, result); + CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next recv(len=%zu) -> %d, %zu", + len, result, nread); return ret; } @@ -125,10 +131,11 @@ write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n) const struct io_ctx *io_ctx = userdata; CURLcode result; int ret = 0; - ssize_t nwritten = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data, - (const char *)buf, len, FALSE, - &result); - if(nwritten < 0) { + size_t nwritten; + + result = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data, + (const char *)buf, len, FALSE, &nwritten); + if(result) { nwritten = 0; if(CURLE_AGAIN == result) ret = EAGAIN; @@ -136,8 +143,8 @@ write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n) ret = EINVAL; } *out_n = (uintptr_t)nwritten; - CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %zd, %d", - len, nwritten, result); + CURL_TRC_CF(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %d, %zu", + len, result, nwritten); return ret; } @@ -192,37 +199,37 @@ static ssize_t tls_recv_more(struct Curl_cfilter *cf, * buffer, and process packets, but will not consume bytes from Rustls' * plaintext output buffer. */ -static ssize_t +static CURLcode cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *plainbuf, size_t plainlen, CURLcode *err) + char *plainbuf, size_t plainlen, size_t *pnread) { const struct ssl_connect_data *const connssl = cf->ctx; struct rustls_ssl_backend_data *const backend = (struct rustls_ssl_backend_data *)connssl->backend; struct rustls_connection *rconn = NULL; + CURLcode result = CURLE_OK; size_t n = 0; - size_t plain_bytes_copied = 0; rustls_result rresult = 0; - ssize_t nread; bool eof = FALSE; DEBUGASSERT(backend); + *pnread = 0; rconn = backend->conn; - while(plain_bytes_copied < plainlen) { + while(*pnread < plainlen) { if(!backend->data_in_pending) { - if(tls_recv_more(cf, data, err) < 0) { - if(*err != CURLE_AGAIN) { - nread = -1; + if(tls_recv_more(cf, data, &result) < 0) { + if(result != CURLE_AGAIN) { goto out; } + result = CURLE_OK; break; } } rresult = rustls_connection_read(rconn, - (uint8_t *)plainbuf + plain_bytes_copied, - plainlen - plain_bytes_copied, + (uint8_t *)plainbuf + *pnread, + plainlen - *pnread, &n); if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) { backend->data_in_pending = FALSE; @@ -230,15 +237,13 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, else if(rresult == RUSTLS_RESULT_UNEXPECTED_EOF) { failf(data, "rustls: peer closed TCP connection " "without first closing TLS connection"); - *err = CURLE_RECV_ERROR; - nread = -1; + result = CURLE_RECV_ERROR; goto out; } else if(rresult != RUSTLS_RESULT_OK) { /* n always equals 0 in this case, do not need to check it */ rustls_failf(data, rresult, "rustls_connection_read"); - *err = CURLE_RECV_ERROR; - nread = -1; + result = CURLE_RECV_ERROR; goto out; } else if(n == 0) { @@ -249,27 +254,18 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, break; } else { - plain_bytes_copied += n; + *pnread += n; } } - if(plain_bytes_copied) { - *err = CURLE_OK; - nread = (ssize_t)plain_bytes_copied; - } - else if(eof) { - *err = CURLE_OK; - nread = 0; - } - else { - *err = CURLE_AGAIN; - nread = -1; + if(!eof && !*pnread) { + result = CURLE_AGAIN; } out: - CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", - plainlen, nread, *err); - return nread; + CURL_TRC_CF(data, cf, "rustls_recv(len=%zu) -> %d, %zu", + plainlen, result, *pnread); + return result; } static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data, @@ -317,9 +313,9 @@ static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data, * In that case, it will not read anything into Rustls' plaintext input buffer. * It will only drain Rustls' plaintext output buffer into the socket. */ -static ssize_t +static CURLcode cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *plainbuf, size_t plainlen, CURLcode *err) + const void *plainbuf, size_t plainlen, size_t *pnwritten) { const struct ssl_connect_data *const connssl = cf->ctx; struct rustls_ssl_backend_data *const backend = @@ -327,10 +323,11 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, struct rustls_connection *rconn = NULL; size_t plainwritten = 0; const unsigned char *buf = plainbuf; + CURLcode result = CURLE_OK; size_t blen = plainlen; - ssize_t nwritten = 0; DEBUGASSERT(backend); + *pnwritten = 0; rconn = backend->conn; DEBUGASSERT(rconn); @@ -341,18 +338,18 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, * if successful, deduct the previous plain bytes from the current * send. */ if(backend->plain_out_buffered) { - *err = cr_flush_out(cf, data, rconn); + result = cr_flush_out(cf, data, rconn); CURL_TRC_CF(data, cf, "cf_send: flushing %zu previously added bytes -> %d", - backend->plain_out_buffered, *err); - if(*err) - return -1; + backend->plain_out_buffered, result); + if(result) + return result; if(blen > backend->plain_out_buffered) { blen -= backend->plain_out_buffered; buf += backend->plain_out_buffered; } else blen = 0; - nwritten += (ssize_t)backend->plain_out_buffered; + *pnwritten += (ssize_t)backend->plain_out_buffered; backend->plain_out_buffered = 0; } @@ -362,37 +359,35 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, rresult = rustls_connection_write(rconn, buf, blen, &plainwritten); if(rresult != RUSTLS_RESULT_OK) { rustls_failf(data, rresult, "rustls_connection_write"); - *err = CURLE_WRITE_ERROR; - return -1; + result = CURLE_WRITE_ERROR; + goto out; } else if(plainwritten == 0) { failf(data, "rustls_connection_write: EOF"); - *err = CURLE_WRITE_ERROR; - return -1; + result = CURLE_WRITE_ERROR; + goto out; } } - *err = cr_flush_out(cf, data, rconn); - if(*err) { - if(CURLE_AGAIN == *err) { + result = cr_flush_out(cf, data, rconn); + if(result) { + if(CURLE_AGAIN == result) { /* The TLS bytes may have been partially written, but we fail the * complete send() and remember how much we already added to Rustls. */ - CURL_TRC_CF(data, cf, "cf_send: EAGAIN, remember we added %zu plain" - " bytes already to Rustls", blen); backend->plain_out_buffered = plainwritten; - if(nwritten) { - *err = CURLE_OK; - return (ssize_t)nwritten; + if(*pnwritten) { + result = CURLE_OK; } } - return -1; + goto out; } else - nwritten += (ssize_t)plainwritten; + *pnwritten += (ssize_t)plainwritten; - CURL_TRC_CF(data, cf, "cf_send(len=%zu) -> %d, %zd", - plainlen, *err, nwritten); - return nwritten; +out: + CURL_TRC_CF(data, cf, "rustls_send(len=%zu) -> %d, %zd", + plainlen, result, *pnwritten); + return result; } /* A server certificate verify callback for Rustls that always returns @@ -864,7 +859,7 @@ init_config_builder_client_auth(struct Curl_easy *data, } else if(!conn_config->clientcert && ssl_config->key) { failf(data, "rustls: must provide certificate with key '%s'", - conn_config->clientcert); + ssl_config->key); return CURLE_SSL_CERTPROBLEM; } @@ -1181,8 +1176,9 @@ cr_connect(struct Curl_cfilter *cf, * send its FINISHED message off. We attempt to let it write * one more time. Oh my. */ + size_t nwritten; cr_set_negotiated_alpn(cf, data, rconn); - cr_send(cf, data, NULL, 0, &tmperr); + tmperr = cr_send(cf, data, NULL, 0, &nwritten); if(tmperr == CURLE_AGAIN) { connssl->io_need = CURL_SSL_IO_NEED_SEND; return CURLE_OK; @@ -1194,17 +1190,23 @@ cr_connect(struct Curl_cfilter *cf, { const uint16_t proto = rustls_connection_get_protocol_version(rconn); - const uint16_t cipher = - rustls_connection_get_negotiated_ciphersuite(rconn); - char buf[64] = ""; + const rustls_str ciphersuite_name = + rustls_connection_get_negotiated_ciphersuite_name(rconn); + const rustls_str kex_group_name = + rustls_connection_get_negotiated_key_exchange_group_name(rconn); const char *ver = "TLS version unknown"; if(proto == RUSTLS_TLS_VERSION_TLSV1_3) ver = "TLSv1.3"; if(proto == RUSTLS_TLS_VERSION_TLSV1_2) ver = "TLSv1.2"; - Curl_cipher_suite_get_str(cipher, buf, sizeof(buf), TRUE); - infof(data, "rustls: handshake complete, %s, cipher: %s", - ver, buf); + infof(data, + "rustls: handshake complete, %s, ciphersuite: %.*s, " + "key exchange group: %.*s", + ver, + (int) ciphersuite_name.len, + ciphersuite_name.data, + (int) kex_group_name.len, + kex_group_name.data); } if(data->set.ssl.certinfo) { size_t num_certs = 0; @@ -1255,8 +1257,9 @@ cr_connect(struct Curl_cfilter *cf, DEBUGASSERT(wants_read || wants_write); if(wants_write) { + size_t nwritten; CURL_TRC_CF(data, cf, "rustls_connection wants us to write_tls."); - cr_send(cf, data, NULL, 0, &tmperr); + tmperr = cr_send(cf, data, NULL, 0, &nwritten); if(tmperr == CURLE_AGAIN) { CURL_TRC_CF(data, cf, "writing would block"); connssl->io_need = CURL_SSL_IO_NEED_SEND; @@ -1309,8 +1312,7 @@ cr_shutdown(struct Curl_cfilter *cf, struct rustls_ssl_backend_data *backend = (struct rustls_ssl_backend_data *)connssl->backend; CURLcode result = CURLE_OK; - ssize_t nwritten, nread; - size_t i; + size_t i, nread, nwritten; DEBUGASSERT(backend); if(!backend->conn || cf->shutdown) { @@ -1329,8 +1331,8 @@ cr_shutdown(struct Curl_cfilter *cf, } } - nwritten = cr_send(cf, data, NULL, 0, &result); - if(nwritten < 0) { + result = cr_send(cf, data, NULL, 0, &nwritten); + if(result) { if(result == CURLE_AGAIN) { connssl->io_need = CURL_SSL_IO_NEED_SEND; result = CURLE_OK; @@ -1343,26 +1345,23 @@ cr_shutdown(struct Curl_cfilter *cf, for(i = 0; i < 10; ++i) { char buf[1024]; - nread = cr_recv(cf, data, buf, (int)sizeof(buf), &result); - if(nread <= 0) + result = cr_recv(cf, data, buf, (int)sizeof(buf), &nread); + if(result) break; } - if(nread > 0) { - /* still data coming in? */ + if(result == CURLE_AGAIN) { + connssl->io_need = CURL_SSL_IO_NEED_RECV; + result = CURLE_OK; + } + else if(result) { + DEBUGASSERT(result); + CURL_TRC_CF(data, cf, "shutdown, error: %d", result); } else if(nread == 0) { /* We got the close notify alert and are done. */ *done = TRUE; } - else if(result == CURLE_AGAIN) { - connssl->io_need = CURL_SSL_IO_NEED_RECV; - result = CURLE_OK; - } - else { - DEBUGASSERT(result); - CURL_TRC_CF(data, cf, "shutdown, error: %d", result); - } out: cf->shutdown = (result || *done); @@ -1434,7 +1433,6 @@ const struct Curl_ssl Curl_ssl_rustls = { NULL, /* set_engine */ NULL, /* set_engine_default */ NULL, /* engines_list */ - NULL, /* false_start */ NULL, /* sha256sum */ cr_recv, /* recv decrypted data */ cr_send, /* send data to encrypt */ diff --git a/lib/vtls/schannel.c b/lib/vtls/schannel.c index bea8eef8c0..2ac61ba794 100644 --- a/lib/vtls/schannel.c +++ b/lib/vtls/schannel.c @@ -42,9 +42,9 @@ #include "vtls.h" #include "vtls_int.h" #include "vtls_scache.h" -#include "../strcase.h" #include "../sendf.h" #include "../connect.h" /* for the connect timeout */ +#include "../strdup.h" #include "../strerror.h" #include "../select.h" /* for the socket readiness */ #include "../curlx/inet_pton.h" /* for IP addr SNI check */ @@ -598,7 +598,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, } if((fInCert || blob) && (data->set.ssl.cert_type) && - (!strcasecompare(data->set.ssl.cert_type, "P12"))) { + (!curl_strequal(data->set.ssl.cert_type, "P12"))) { failf(data, "schannel: certificate format compatibility error " " for %s", blob ? "(memory blob)" : data->set.ssl.primary.clientcert); @@ -897,7 +897,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf, static CURLcode schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) { - ssize_t written = -1; + size_t written = 0; struct ssl_connect_data *connssl = cf->ctx; struct schannel_ssl_backend_data *backend = (struct schannel_ssl_backend_data *)connssl->backend; @@ -1128,18 +1128,18 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data) "sending %lu bytes.", outbuf.cbBuffer)); /* send initial handshake data which is now stored in output buffer */ - written = Curl_conn_cf_send(cf->next, data, - outbuf.pvBuffer, outbuf.cbBuffer, FALSE, - &result); + result = Curl_conn_cf_send(cf->next, data, + outbuf.pvBuffer, outbuf.cbBuffer, FALSE, + &written); Curl_pSecFn->FreeContextBuffer(outbuf.pvBuffer); - if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) { + if((result != CURLE_OK) || (outbuf.cbBuffer != written)) { failf(data, "schannel: failed to send initial handshake data: " - "sent %zd of %lu bytes", written, outbuf.cbBuffer); + "sent %zu of %lu bytes", written, outbuf.cbBuffer); return CURLE_SSL_CONNECT_ERROR; } DEBUGF(infof(data, "schannel: sent initial handshake data: " - "sent %zd bytes", written)); + "sent %zu bytes", written)); backend->recv_unrecoverable_err = CURLE_OK; backend->recv_sspi_close_notify = FALSE; @@ -1161,7 +1161,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) (struct schannel_ssl_backend_data *)connssl->backend; struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); int i; - ssize_t nread = -1, written = -1; + size_t nread = 0, written = 0; unsigned char *reallocated_buffer; SecBuffer outbuf[3]; SecBufferDesc outbuf_desc; @@ -1229,12 +1229,12 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) for(;;) { if(doread) { /* read encrypted handshake data from socket */ - nread = Curl_conn_cf_recv(cf->next, data, - (char *) (backend->encdata_buffer + - backend->encdata_offset), - backend->encdata_length - - backend->encdata_offset, - &result); + result = Curl_conn_cf_recv(cf->next, data, + (char *) (backend->encdata_buffer + + backend->encdata_offset), + backend->encdata_length - + backend->encdata_offset, + &nread); if(result == CURLE_AGAIN) { connssl->io_need = CURL_SSL_IO_NEED_RECV; DEBUGF(infof(data, "schannel: failed to receive handshake, " @@ -1250,7 +1250,7 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) /* increase encrypted data buffer offset */ backend->encdata_offset += nread; backend->encdata_is_incomplete = FALSE; - SCH_DEV(infof(data, "schannel: encrypted data got %zd", nread)); + SCH_DEV(infof(data, "schannel: encrypted data got %zu", nread)); } SCH_DEV(infof(data, @@ -1317,13 +1317,13 @@ schannel_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data) "sending %lu bytes.", outbuf[i].cbBuffer)); /* send handshake token to server */ - written = Curl_conn_cf_send(cf->next, data, - outbuf[i].pvBuffer, outbuf[i].cbBuffer, - FALSE, &result); + result = Curl_conn_cf_send(cf->next, data, + outbuf[i].pvBuffer, outbuf[i].cbBuffer, + FALSE, &written); if((result != CURLE_OK) || - (outbuf[i].cbBuffer != (size_t) written)) { + (outbuf[i].cbBuffer != written)) { failf(data, "schannel: failed to send next handshake data: " - "sent %zd of %lu bytes", written, outbuf[i].cbBuffer); + "sent %zu of %lu bytes", written, outbuf[i].cbBuffer); return CURLE_SSL_CONNECT_ERROR; } } @@ -1714,22 +1714,22 @@ static CURLcode schannel_connect(struct Curl_cfilter *cf, return CURLE_OK; } -static ssize_t +static CURLcode schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *buf, size_t len, CURLcode *err) + const void *buf, size_t len, size_t *pnwritten) { - ssize_t written = -1; size_t data_len = 0; unsigned char *ptr = NULL; struct ssl_connect_data *connssl = cf->ctx; SecBuffer outbuf[4]; SecBufferDesc outbuf_desc; SECURITY_STATUS sspi_status = SEC_E_OK; - CURLcode result; + CURLcode result = CURLE_OK; struct schannel_ssl_backend_data *backend = (struct schannel_ssl_backend_data *)connssl->backend; DEBUGASSERT(backend); + *pnwritten = 0; /* check if the maximum stream sizes were queried */ if(backend->stream_sizes.cbMaximumMessage == 0) { @@ -1738,8 +1738,7 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, SECPKG_ATTR_STREAM_SIZES, &backend->stream_sizes); if(sspi_status != SEC_E_OK) { - *err = CURLE_SEND_ERROR; - return -1; + return CURLE_SEND_ERROR; } } @@ -1753,8 +1752,7 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, backend->stream_sizes.cbTrailer; ptr = (unsigned char *) malloc(data_len); if(!ptr) { - *err = CURLE_OUT_OF_MEMORY; - return -1; + return CURLE_OUT_OF_MEMORY; } /* setup output buffers (header, data, trailer, empty) */ @@ -1777,7 +1775,6 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, /* check if the message was encrypted */ if(sspi_status == SEC_E_OK) { - written = 0; /* send the encrypted message including header, data and trailer */ len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer; @@ -1799,16 +1796,15 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, */ /* send entire message or fail */ - while(len > (size_t)written) { - ssize_t this_write = 0; + while(len > *pnwritten) { + size_t this_write = 0; int what; timediff_t timeout_ms = Curl_timeleft(data, NULL, FALSE); if(timeout_ms < 0) { /* we already got the timeout */ failf(data, "schannel: timed out sending data " - "(bytes sent: %zd)", written); - *err = CURLE_OPERATION_TIMEDOUT; - written = -1; + "(bytes sent: %zu)", *pnwritten); + result = CURLE_OPERATION_TIMEDOUT; break; } else if(!timeout_ms) @@ -1817,56 +1813,52 @@ schannel_send(struct Curl_cfilter *cf, struct Curl_easy *data, if(what < 0) { /* fatal error */ failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); - *err = CURLE_SEND_ERROR; - written = -1; + result = CURLE_SEND_ERROR; break; } else if(0 == what) { failf(data, "schannel: timed out sending data " - "(bytes sent: %zd)", written); - *err = CURLE_OPERATION_TIMEDOUT; - written = -1; + "(bytes sent: %zu)", *pnwritten); + result = CURLE_OPERATION_TIMEDOUT; break; } /* socket is writable */ - this_write = Curl_conn_cf_send(cf->next, data, - ptr + written, len - written, - FALSE, &result); + result = Curl_conn_cf_send(cf->next, data, + ptr + *pnwritten, len - *pnwritten, + FALSE, &this_write); if(result == CURLE_AGAIN) continue; else if(result != CURLE_OK) { - *err = result; - written = -1; break; } - written += this_write; + *pnwritten += this_write; } } else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) { - *err = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; } else{ - *err = CURLE_SEND_ERROR; + result = CURLE_SEND_ERROR; } Curl_safefree(ptr); - if(len == (size_t)written) + if(len == *pnwritten) /* Encrypted message including header, data and trailer entirely sent. The return value is the number of unencrypted bytes that were sent. */ - written = outbuf[1].cbBuffer; + *pnwritten = outbuf[1].cbBuffer; - return written; + return result; } -static ssize_t +static CURLcode schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *err) + char *buf, size_t len, size_t *pnread) { size_t size = 0; - ssize_t nread = -1; + size_t nread = 0; struct ssl_connect_data *connssl = cf->ctx; unsigned char *reallocated_buffer; size_t reallocated_length; @@ -1879,8 +1871,10 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE; struct schannel_ssl_backend_data *backend = (struct schannel_ssl_backend_data *)connssl->backend; + CURLcode result = CURLE_OK; DEBUGASSERT(backend); + *pnread = 0; /**************************************************************************** * Do not return or set backend->recv_unrecoverable_err unless in the @@ -1899,7 +1893,6 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, */ SCH_DEV(infof(data, "schannel: client wants to read %zu bytes", len)); - *err = CURLE_OK; if(len && len <= backend->decdata_offset) { SCH_DEV(infof(data, @@ -1907,7 +1900,7 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, goto cleanup; } else if(backend->recv_unrecoverable_err) { - *err = backend->recv_unrecoverable_err; + result = backend->recv_unrecoverable_err; infof(data, "schannel: an unrecoverable error occurred in a prior call"); goto cleanup; } @@ -1933,7 +1926,7 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, reallocated_buffer = realloc(backend->encdata_buffer, reallocated_length); if(!reallocated_buffer) { - *err = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; failf(data, "schannel: unable to re-allocate memory"); goto cleanup; } @@ -1950,26 +1943,26 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, backend->encdata_offset, backend->encdata_length)); /* read encrypted data from socket */ - nread = Curl_conn_cf_recv(cf->next, data, - (char *)(backend->encdata_buffer + - backend->encdata_offset), - size, err); - if(*err) { - if(*err == CURLE_AGAIN) + result = Curl_conn_cf_recv(cf->next, data, + (char *)(backend->encdata_buffer + + backend->encdata_offset), + size, &nread); + if(result) { + if(result == CURLE_AGAIN) SCH_DEV(infof(data, "schannel: recv returned CURLE_AGAIN")); - else if(*err == CURLE_RECV_ERROR) + else if(result == CURLE_RECV_ERROR) infof(data, "schannel: recv returned CURLE_RECV_ERROR"); else - infof(data, "schannel: recv returned error %d", *err); + infof(data, "schannel: recv returned error %d", result); } else if(nread == 0) { backend->recv_connection_closed = TRUE; DEBUGF(infof(data, "schannel: server closed the connection")); } - else if(nread > 0) { - backend->encdata_offset += (size_t)nread; + else { + backend->encdata_offset += nread; backend->encdata_is_incomplete = FALSE; - SCH_DEV(infof(data, "schannel: encrypted data got %zd", nread)); + SCH_DEV(infof(data, "schannel: encrypted data got %zu", nread)); } } @@ -2019,7 +2012,7 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, reallocated_buffer = realloc(backend->decdata_buffer, reallocated_length); if(!reallocated_buffer) { - *err = CURLE_OUT_OF_MEMORY; + result = CURLE_OUT_OF_MEMORY; failf(data, "schannel: unable to re-allocate memory"); goto cleanup; } @@ -2070,7 +2063,7 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, /* check if server wants to renegotiate the connection context */ if(sspi_status == SEC_I_RENEGOTIATE) { infof(data, "schannel: remote party requests renegotiation"); - if(*err && *err != CURLE_AGAIN) { + if(result && result != CURLE_AGAIN) { infof(data, "schannel: cannot renegotiate, an error is pending"); goto cleanup; } @@ -2081,9 +2074,9 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, connssl->connecting_state = ssl_connect_2; connssl->io_need = CURL_SSL_IO_NEED_SEND; backend->recv_renegotiating = TRUE; - *err = schannel_connect(cf, data, &done); + result = schannel_connect(cf, data, &done); backend->recv_renegotiating = FALSE; - if(*err) { + if(result) { infof(data, "schannel: renegotiation failed"); goto cleanup; } @@ -2102,8 +2095,8 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, /* We received the close notify just fine, any error we got * from the lower filters afterwards (e.g. the socket), is not * an error on the TLS data stream. That one ended here. */ - if(*err == CURLE_RECV_ERROR) - *err = CURLE_OK; + if(result == CURLE_RECV_ERROR) + result = CURLE_OK; infof(data, "schannel: server close notification received (close_notify)"); goto cleanup; @@ -2111,8 +2104,8 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, } else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { backend->encdata_is_incomplete = TRUE; - if(!*err) - *err = CURLE_AGAIN; + if(!result) + result = CURLE_AGAIN; SCH_DEV(infof(data, "schannel: failed to decrypt data, need more data")); goto cleanup; } @@ -2122,7 +2115,7 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data, failf(data, "schannel: failed to read data from server: %s", Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); #endif - *err = CURLE_RECV_ERROR; + result = CURLE_RECV_ERROR; goto cleanup; } } @@ -2142,28 +2135,16 @@ cleanup: The behavior here is a matter of debate. We do not want to be vulnerable to a truncation attack however there is some browser precedent for ignoring the close_notify for compatibility reasons. - - Additionally, Windows 2000 (v5.0) is a special case since it seems it - does not return close_notify. In that case if the connection was closed we - assume it was graceful (close_notify) since there does not seem to be a - way to tell. */ if(len && !backend->decdata_offset && backend->recv_connection_closed && !backend->recv_sspi_close_notify) { - bool isWin2k = curlx_verify_windows_version(5, 0, 0, PLATFORM_WINNT, - VERSION_EQUAL); - - if(isWin2k && sspi_status == SEC_E_OK) - backend->recv_sspi_close_notify = TRUE; - else { - *err = CURLE_RECV_ERROR; - failf(data, "schannel: server closed abruptly (missing close_notify)"); - } + result = CURLE_RECV_ERROR; + failf(data, "schannel: server closed abruptly (missing close_notify)"); } /* Any error other than CURLE_AGAIN is an unrecoverable error. */ - if(*err && *err != CURLE_AGAIN) - backend->recv_unrecoverable_err = *err; + if(result && result != CURLE_AGAIN) + backend->recv_unrecoverable_err = result; size = len < backend->decdata_offset ? len : backend->decdata_offset; if(size) { @@ -2175,21 +2156,21 @@ cleanup: SCH_DEV(infof(data, "schannel: decrypted data buffer: offset %zu length %zu", backend->decdata_offset, backend->decdata_length)); - *err = CURLE_OK; - return (ssize_t)size; + *pnread = size; + return CURLE_OK; } - if(!*err && !backend->recv_connection_closed) - *err = CURLE_AGAIN; + if(!result && !backend->recv_connection_closed) + result = CURLE_AGAIN; /* it is debatable what to return when !len. We could return whatever error we got from decryption but instead we override here so the return is consistent. */ if(!len) - *err = CURLE_OK; + return CURLE_OK; - return *err ? -1 : 0; + return result; } static bool schannel_data_pending(struct Curl_cfilter *cf, @@ -2291,14 +2272,16 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) { /* send close message which is in output buffer */ - ssize_t written = Curl_conn_cf_send(cf->next, data, - outbuf.pvBuffer, outbuf.cbBuffer, - FALSE, &result); + size_t written; + + result = Curl_conn_cf_send(cf->next, data, + outbuf.pvBuffer, outbuf.cbBuffer, + FALSE, &written); Curl_pSecFn->FreeContextBuffer(outbuf.pvBuffer); if(!result) { - if(written < (ssize_t)outbuf.cbBuffer) { + if(written < outbuf.cbBuffer) { failf(data, "schannel: failed to send close msg: %s" - " (bytes written: %zd)", curl_easy_strerror(result), written); + " (bytes written: %zu)", curl_easy_strerror(result), written); result = CURLE_SEND_ERROR; goto out; } @@ -2329,23 +2312,23 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf, if(backend->cred && backend->ctxt && !backend->recv_sspi_close_notify && !backend->recv_connection_closed) { char buffer[1024]; - ssize_t nread; + size_t nread; - nread = schannel_recv(cf, data, buffer, sizeof(buffer), &result); - if(nread > 0) { - /* still data coming in? */ + result = schannel_recv(cf, data, buffer, sizeof(buffer), &nread); + if(result == CURLE_AGAIN) { + connssl->io_need = CURL_SSL_IO_NEED_RECV; + } + else if(result) { + CURL_TRC_CF(data, cf, "SSL shutdown, error %d", result); + result = CURLE_RECV_ERROR; } else if(nread == 0) { /* We got the close notify alert and are done. */ backend->recv_connection_closed = TRUE; *done = TRUE; } - else if(nread < 0 && result == CURLE_AGAIN) { - connssl->io_need = CURL_SSL_IO_NEED_RECV; - } else { - CURL_TRC_CF(data, cf, "SSL shutdown, error %d", result); - result = CURLE_RECV_ERROR; + /* still data coming in? */ } } @@ -2770,7 +2753,6 @@ const struct Curl_ssl Curl_ssl_schannel = { NULL, /* set_engine */ NULL, /* set_engine_default */ NULL, /* engines_list */ - NULL, /* false_start */ schannel_sha256sum, /* sha256sum */ schannel_recv, /* recv decrypted data */ schannel_send, /* send data to encrypt */ diff --git a/lib/vtls/schannel_int.h b/lib/vtls/schannel_int.h index fe10125456..511a852c7a 100644 --- a/lib/vtls/schannel_int.h +++ b/lib/vtls/schannel_int.h @@ -68,53 +68,51 @@ #endif #ifndef SCH_CREDENTIALS_VERSION - #define SCH_CREDENTIALS_VERSION 0x00000005 -typedef enum _eTlsAlgorithmUsage -{ - TlsParametersCngAlgUsageKeyExchange, - TlsParametersCngAlgUsageSignature, - TlsParametersCngAlgUsageCipher, - TlsParametersCngAlgUsageDigest, - TlsParametersCngAlgUsageCertSig +typedef enum _eTlsAlgorithmUsage { + TlsParametersCngAlgUsageKeyExchange, + TlsParametersCngAlgUsageSignature, + TlsParametersCngAlgUsageCipher, + TlsParametersCngAlgUsageDigest, + TlsParametersCngAlgUsageCertSig } eTlsAlgorithmUsage; -typedef struct _CRYPTO_SETTINGS -{ - eTlsAlgorithmUsage eAlgorithmUsage; - UNICODE_STRING strCngAlgId; - DWORD cChainingModes; - PUNICODE_STRING rgstrChainingModes; - DWORD dwMinBitLength; - DWORD dwMaxBitLength; +/* !checksrc! disable TYPEDEFSTRUCT 1 */ +typedef struct _CRYPTO_SETTINGS { + eTlsAlgorithmUsage eAlgorithmUsage; + UNICODE_STRING strCngAlgId; + DWORD cChainingModes; + PUNICODE_STRING rgstrChainingModes; + DWORD dwMinBitLength; + DWORD dwMaxBitLength; } CRYPTO_SETTINGS, * PCRYPTO_SETTINGS; -typedef struct _TLS_PARAMETERS -{ - DWORD cAlpnIds; - PUNICODE_STRING rgstrAlpnIds; - DWORD grbitDisabledProtocols; - DWORD cDisabledCrypto; - PCRYPTO_SETTINGS pDisabledCrypto; - DWORD dwFlags; +/* !checksrc! disable TYPEDEFSTRUCT 1 */ +typedef struct _TLS_PARAMETERS { + DWORD cAlpnIds; + PUNICODE_STRING rgstrAlpnIds; + DWORD grbitDisabledProtocols; + DWORD cDisabledCrypto; + PCRYPTO_SETTINGS pDisabledCrypto; + DWORD dwFlags; } TLS_PARAMETERS, * PTLS_PARAMETERS; -typedef struct _SCH_CREDENTIALS -{ - DWORD dwVersion; - DWORD dwCredFormat; - DWORD cCreds; - PCCERT_CONTEXT* paCred; - HCERTSTORE hRootStore; +/* !checksrc! disable TYPEDEFSTRUCT 1 */ +typedef struct _SCH_CREDENTIALS { + DWORD dwVersion; + DWORD dwCredFormat; + DWORD cCreds; + PCCERT_CONTEXT* paCred; + HCERTSTORE hRootStore; - DWORD cMappers; - struct _HMAPPER **aphMappers; + DWORD cMappers; + struct _HMAPPER **aphMappers; - DWORD dwSessionLifespan; - DWORD dwFlags; - DWORD cTlsParameters; - PTLS_PARAMETERS pTlsParameters; + DWORD dwSessionLifespan; + DWORD dwFlags; + DWORD cTlsParameters; + PTLS_PARAMETERS pTlsParameters; } SCH_CREDENTIALS, * PSCH_CREDENTIALS; #define SCH_CRED_MAX_SUPPORTED_PARAMETERS 16 diff --git a/lib/vtls/schannel_verify.c b/lib/vtls/schannel_verify.c index f843342b9c..7db13f2a25 100644 --- a/lib/vtls/schannel_verify.c +++ b/lib/vtls/schannel_verify.c @@ -78,6 +78,28 @@ #define BEGIN_CERT "-----BEGIN CERTIFICATE-----" #define END_CERT "\n-----END CERTIFICATE-----" +struct cert_chain_engine_config_win8 { + DWORD cbSize; + HCERTSTORE hRestrictedRoot; + HCERTSTORE hRestrictedTrust; + HCERTSTORE hRestrictedOther; + DWORD cAdditionalStore; + HCERTSTORE *rghAdditionalStore; + DWORD dwFlags; + DWORD dwUrlRetrievalTimeout; + DWORD MaximumCachedCertificates; + DWORD CycleDetectionModulus; + HCERTSTORE hExclusiveRoot; + HCERTSTORE hExclusiveTrustedPeople; + DWORD dwExclusiveFlags; +}; + +/* Not defined before mingw-w64 4.0.0 */ +#ifndef CERT_CHAIN_EXCLUSIVE_ENABLE_CA_FLAG +#define CERT_CHAIN_EXCLUSIVE_ENABLE_CA_FLAG 0x00000001 +#endif + +/* Legacy structure to supply size to Win7 clients */ struct cert_chain_engine_config_win7 { DWORD cbSize; HCERTSTORE hRestrictedRoot; @@ -838,13 +860,22 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf, } if(result == CURLE_OK) { - struct cert_chain_engine_config_win7 engine_config; + struct cert_chain_engine_config_win8 engine_config; BOOL create_engine_result; memset(&engine_config, 0, sizeof(engine_config)); - engine_config.cbSize = sizeof(engine_config); engine_config.hExclusiveRoot = trust_store; + /* Win8/Server2012 allows us to match partial chains */ + if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT, + VERSION_GREATER_THAN_EQUAL) && + !ssl_config->no_partialchain) { + engine_config.cbSize = sizeof(engine_config); + engine_config.dwExclusiveFlags = CERT_CHAIN_EXCLUSIVE_ENABLE_CA_FLAG; + } + else + engine_config.cbSize = sizeof(struct cert_chain_engine_config_win7); + /* CertCreateCertificateChainEngine will check the expected size of the * CERT_CHAIN_ENGINE_CONFIG structure and fail if the specified size * does not match the expected size. When this occurs, it indicates that diff --git a/lib/vtls/sectransp.c b/lib/vtls/sectransp.c deleted file mode 100644 index 2ae2ef35a2..0000000000 --- a/lib/vtls/sectransp.c +++ /dev/null @@ -1,2708 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) Daniel Stenberg, , et al. - * Copyright (C) Nick Zitzmann, . - * - * 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 - * - ***************************************************************************/ - -/* - * Source file for all iOS and macOS Secure Transport-specific code for the - * TLS/SSL layer. No code but vtls.c should ever call or use these functions. - */ - -#include "../curl_setup.h" - -#ifdef USE_SECTRANSP - -#include "../urldata.h" /* for the Curl_easy definition */ -#include "../curlx/base64.h" -#include "../curlx/strparse.h" -#include "../multiif.h" -#include "../strcase.h" -#include "x509asn1.h" -#include "vtls_scache.h" -#include "../strerror.h" -#include "cipher_suite.h" - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunreachable-code" -#endif /* __clang__ */ - -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Waddress" -#endif - -#if defined(__GNUC__) && defined(__APPLE__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" -#endif - -#include - -#include -/* For some reason, when building for iOS, the omnibus header above does - * not include SecureTransport.h as of iOS SDK 5.1. */ -#include -#include -#include - -/* The Security framework has changed greatly between iOS and different macOS - versions, and we will try to support as many of them as we can (back to - Leopard and iOS 5) by using macros and weak-linking. - - In general, you want to build this using the most recent OS SDK, since some - features require curl to be built against the latest SDK. TLS 1.1 and 1.2 - support, for instance, require the macOS 10.8 SDK or later. TLS 1.3 - requires the macOS 10.13 or iOS 11 SDK or later. */ -#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) - -#if MAC_OS_X_VERSION_MAX_ALLOWED < 1050 -#error "The Secure Transport backend requires Leopard or later." -#endif /* MAC_OS_X_VERSION_MAX_ALLOWED < 1050 */ - -#define CURL_BUILD_IOS 0 -#define CURL_BUILD_IOS_7 0 -#define CURL_BUILD_IOS_9 0 -#define CURL_BUILD_IOS_11 0 -#define CURL_BUILD_IOS_13 0 -#define CURL_BUILD_MAC 1 -/* This is the maximum API level we are allowed to use when building: */ -#define CURL_BUILD_MAC_10_5 MAC_OS_X_VERSION_MAX_ALLOWED >= 1050 -#define CURL_BUILD_MAC_10_6 MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 -#define CURL_BUILD_MAC_10_7 MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 -#define CURL_BUILD_MAC_10_8 MAC_OS_X_VERSION_MAX_ALLOWED >= 1080 -#define CURL_BUILD_MAC_10_9 MAC_OS_X_VERSION_MAX_ALLOWED >= 1090 -#define CURL_BUILD_MAC_10_11 MAC_OS_X_VERSION_MAX_ALLOWED >= 101100 -#define CURL_BUILD_MAC_10_13 MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 -#define CURL_BUILD_MAC_10_15 MAC_OS_X_VERSION_MAX_ALLOWED >= 101500 -/* These macros mean "the following code is present to allow runtime backward - compatibility with at least this cat or earlier": - (You set this at build-time using the compiler command line option - "-mmacosx-version-min.") */ -#define CURL_SUPPORT_MAC_10_5 MAC_OS_X_VERSION_MIN_REQUIRED <= 1050 -#define CURL_SUPPORT_MAC_10_6 MAC_OS_X_VERSION_MIN_REQUIRED <= 1060 -#define CURL_SUPPORT_MAC_10_7 MAC_OS_X_VERSION_MIN_REQUIRED <= 1070 -#define CURL_SUPPORT_MAC_10_8 MAC_OS_X_VERSION_MIN_REQUIRED <= 1080 -#define CURL_SUPPORT_MAC_10_9 MAC_OS_X_VERSION_MIN_REQUIRED <= 1090 - -#elif TARGET_OS_EMBEDDED || TARGET_OS_IPHONE -#define CURL_BUILD_IOS 1 -#define CURL_BUILD_IOS_7 __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 -#define CURL_BUILD_IOS_9 __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000 -#define CURL_BUILD_IOS_11 __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 -#define CURL_BUILD_IOS_13 __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 -#define CURL_BUILD_MAC 0 -#define CURL_BUILD_MAC_10_5 0 -#define CURL_BUILD_MAC_10_6 0 -#define CURL_BUILD_MAC_10_7 0 -#define CURL_BUILD_MAC_10_8 0 -#define CURL_BUILD_MAC_10_9 0 -#define CURL_BUILD_MAC_10_11 0 -#define CURL_BUILD_MAC_10_13 0 -#define CURL_BUILD_MAC_10_15 0 -#define CURL_SUPPORT_MAC_10_5 0 -#define CURL_SUPPORT_MAC_10_6 0 -#define CURL_SUPPORT_MAC_10_7 0 -#define CURL_SUPPORT_MAC_10_8 0 -#define CURL_SUPPORT_MAC_10_9 0 - -#else -#error "The Secure Transport backend requires iOS or macOS." -#endif /* (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) */ - -#if CURL_BUILD_MAC -#include -#endif /* CURL_BUILD_MAC */ - -#include "../sendf.h" -#include "../curlx/inet_pton.h" -#include "../connect.h" -#include "../select.h" -#include "vtls.h" -#include "vtls_int.h" -#include "sectransp.h" -#include "../curl_printf.h" -#include "../strdup.h" - -#include "../curl_memory.h" -/* The last #include file should be: */ -#include "../memdebug.h" - - -/* From MacTypes.h (which we cannot include because it is not present in - iOS: */ -#define ioErr -36 -#define paramErr -50 - -struct st_ssl_backend_data { - SSLContextRef ssl_ctx; - bool ssl_direction; /* true if writing, false if reading */ - size_t ssl_write_buffered_length; - BIT(sent_shutdown); -}; - -/* Create the list of default ciphers to use by making an intersection of the - * ciphers supported by Secure Transport and the list below, using the order - * of the former. - * This list is based on TLS recommendations by Mozilla, balancing between - * security and wide compatibility: "Most ciphers that are not clearly broken - * and dangerous to use are supported" - */ -static const uint16_t default_ciphers[] = { - TLS_RSA_WITH_3DES_EDE_CBC_SHA, /* 0x000A */ - TLS_RSA_WITH_AES_128_CBC_SHA, /* 0x002F */ - TLS_RSA_WITH_AES_256_CBC_SHA, /* 0x0035 */ - -#if CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, /* 0xC009 */ - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, /* 0xC00A */ - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* 0xC013 */ - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, /* 0xC014 */ -#endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */ - -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - TLS_RSA_WITH_AES_128_CBC_SHA256, /* 0x003C */ - TLS_RSA_WITH_AES_256_CBC_SHA256, /* 0x003D */ - TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, /* 0x0067 */ - TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, /* 0x006B */ - TLS_RSA_WITH_AES_128_GCM_SHA256, /* 0x009C */ - TLS_RSA_WITH_AES_256_GCM_SHA384, /* 0x009D */ - TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, /* 0x009E */ - TLS_DHE_RSA_WITH_AES_256_GCM_SHA384, /* 0x009F */ - TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, /* 0xC023 */ - TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, /* 0xC024 */ - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, /* 0xC027 */ - TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, /* 0xC028 */ - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, /* 0xC02B */ - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, /* 0xC02C */ - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* 0xC02F */ - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, /* 0xC030 */ -#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ - -#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA8 */ - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */ - - /* TLSv1.3 is not supported by Secure Transport, but there is also other - * code referencing TLSv1.3, like: kTLSProtocol13 ? */ - TLS_AES_128_GCM_SHA256, /* 0x1301 */ - TLS_AES_256_GCM_SHA384, /* 0x1302 */ - TLS_CHACHA20_POLY1305_SHA256, /* 0x1303 */ -#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ -}; - -/* pinned public key support tests */ - -/* version 1 supports macOS 10.12+ and iOS 10+ */ -#if ((TARGET_OS_IPHONE && __IPHONE_OS_VERSION_MIN_REQUIRED >= 100000) || \ - (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101200)) -#define SECTRANSP_PINNEDPUBKEY_V1 1 -#endif - -/* version 2 supports macOS 10.7+ */ -#if (!TARGET_OS_IPHONE && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070) -#define SECTRANSP_PINNEDPUBKEY_V2 1 -#endif - -#if defined(SECTRANSP_PINNEDPUBKEY_V1) || defined(SECTRANSP_PINNEDPUBKEY_V2) -/* this backend supports CURLOPT_PINNEDPUBLICKEY */ -#define SECTRANSP_PINNEDPUBKEY 1 -#endif /* SECTRANSP_PINNEDPUBKEY */ - -#ifdef SECTRANSP_PINNEDPUBKEY -/* both new and old APIs return rsa keys missing the spki header (not DER) */ -static const unsigned char rsa4096SpkiHeader[] = { - 0x30, 0x82, 0x02, 0x22, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, - 0x00, 0x03, 0x82, 0x02, 0x0f, 0x00}; - -static const unsigned char rsa2048SpkiHeader[] = { - 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, - 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, - 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, - 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00}; -#ifdef SECTRANSP_PINNEDPUBKEY_V1 -/* the *new* version does not return DER encoded ecdsa certs like the old... */ -static const unsigned char ecDsaSecp256r1SpkiHeader[] = { - 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, - 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, - 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, - 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, - 0x42, 0x00}; - -static const unsigned char ecDsaSecp384r1SpkiHeader[] = { - 0x30, 0x76, 0x30, 0x10, 0x06, 0x07, - 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, - 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, - 0x00, 0x22, 0x03, 0x62, 0x00}; -#endif /* SECTRANSP_PINNEDPUBKEY_V1 */ -#endif /* SECTRANSP_PINNEDPUBKEY */ - -static OSStatus sectransp_bio_cf_in_read(SSLConnectionRef connection, - void *buf, - size_t *dataLength) /* IN/OUT */ -{ - const struct Curl_cfilter *cf = (const struct Curl_cfilter *)connection; - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nread; - CURLcode result; - OSStatus rtn = noErr; - - DEBUGASSERT(data); - nread = Curl_conn_cf_recv(cf->next, data, buf, *dataLength, &result); - CURL_TRC_CF(data, cf, "bio_read(len=%zu) -> %zd, result=%d", - *dataLength, nread, result); - if(nread < 0) { - switch(result) { - case CURLE_OK: - case CURLE_AGAIN: - rtn = errSSLWouldBlock; - backend->ssl_direction = FALSE; - break; - default: - rtn = ioErr; - break; - } - nread = 0; - } - else if(nread == 0) { - rtn = errSSLClosedGraceful; - } - else if((size_t)nread < *dataLength) { - rtn = errSSLWouldBlock; - } - *dataLength = nread; - return rtn; -} - -static OSStatus sectransp_bio_cf_out_write(SSLConnectionRef connection, - const void *buf, - size_t *dataLength) /* IN/OUT */ -{ - const struct Curl_cfilter *cf = (const struct Curl_cfilter *)connection; - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nwritten; - CURLcode result; - OSStatus rtn = noErr; - - DEBUGASSERT(data); - nwritten = Curl_conn_cf_send(cf->next, data, buf, *dataLength, FALSE, - &result); - CURL_TRC_CF(data, cf, "bio_send(len=%zu) -> %zd, result=%d", - *dataLength, nwritten, result); - if(nwritten <= 0) { - if(result == CURLE_AGAIN) { - rtn = errSSLWouldBlock; - backend->ssl_direction = TRUE; - } - else { - rtn = ioErr; - } - nwritten = 0; - } - else if((size_t)nwritten < *dataLength) { - rtn = errSSLWouldBlock; - } - *dataLength = nwritten; - return rtn; -} - -#if CURL_BUILD_MAC -CF_INLINE void GetDarwinVersionNumber(int *major, int *minor) -{ - int mib[2]; - size_t os_version_len; - char buf[256]; - - /* Get the Darwin kernel version from the kernel using sysctl(): */ - mib[0] = CTL_KERN; - mib[1] = KERN_OSRELEASE; - if(sysctl(mib, 2, NULL, &os_version_len, NULL, 0) == -1) - return; - if(os_version_len < sizeof(buf)) { - if(sysctl(mib, 2, buf, &os_version_len, NULL, 0) != -1) { - const char *os = buf; - curl_off_t fnum; - curl_off_t snum; - /* Parse the version: */ - if(!curlx_str_number(&os, &fnum, INT_MAX) && - !curlx_str_single(&os, '.') && - !curlx_str_number(&os, &snum, INT_MAX)) { - *major = (int)fnum; - *minor = (int)snum; - } - } - } -} -#endif /* CURL_BUILD_MAC */ - -/* Apple provides a myriad of ways of getting information about a certificate - into a string. Some are not available under iOS or newer cats. Here's a - unified function for getting a string describing the certificate that ought - to work in all cats starting with Leopard. */ -CF_INLINE CFStringRef getsubject(SecCertificateRef cert) -{ - CFStringRef server_cert_summary = CFSTR("(null)"); - -#if CURL_BUILD_IOS - /* iOS: There is only one way to do this. */ - server_cert_summary = SecCertificateCopySubjectSummary(cert); -#else -#if CURL_BUILD_MAC_10_7 - /* Lion & later: Get the long description if we can. */ - if(&SecCertificateCopyLongDescription) - server_cert_summary = - SecCertificateCopyLongDescription(NULL, cert, NULL); - else -#endif /* CURL_BUILD_MAC_10_7 */ -#if CURL_BUILD_MAC_10_6 - /* Snow Leopard: Get the certificate summary. */ - if(&SecCertificateCopySubjectSummary) - server_cert_summary = SecCertificateCopySubjectSummary(cert); - else -#endif /* CURL_BUILD_MAC_10_6 */ - /* Leopard is as far back as we go... */ - (void)SecCertificateCopyCommonName(cert, &server_cert_summary); -#endif /* CURL_BUILD_IOS */ - return server_cert_summary; -} - -static CURLcode CopyCertSubject(struct Curl_easy *data, - SecCertificateRef cert, char **certp) -{ - CFStringRef c = getsubject(cert); - CURLcode result = CURLE_OK; - const char *direct; - char *cbuf = NULL; - *certp = NULL; - - if(!c) { - failf(data, "SSL: invalid CA certificate subject"); - return CURLE_PEER_FAILED_VERIFICATION; - } - - /* If the subject is already available as UTF-8 encoded (ie 'direct') then - use that, else convert it. */ - direct = CFStringGetCStringPtr(c, kCFStringEncodingUTF8); - if(direct) { - *certp = strdup(direct); - if(!*certp) { - failf(data, "SSL: out of memory"); - result = CURLE_OUT_OF_MEMORY; - } - } - else { - size_t cbuf_size = ((size_t)CFStringGetLength(c) * 4) + 1; - cbuf = calloc(1, cbuf_size); - if(cbuf) { - if(!CFStringGetCString(c, cbuf, (CFIndex)cbuf_size, - kCFStringEncodingUTF8)) { - failf(data, "SSL: invalid CA certificate subject"); - result = CURLE_PEER_FAILED_VERIFICATION; - } - else - /* pass back the buffer */ - *certp = cbuf; - } - else { - failf(data, "SSL: could not allocate %zu bytes of memory", cbuf_size); - result = CURLE_OUT_OF_MEMORY; - } - } - if(result) - free(cbuf); - CFRelease(c); - return result; -} - -#if CURL_SUPPORT_MAC_10_6 -/* The SecKeychainSearch API was deprecated in Lion, and using it will raise - deprecation warnings, so let's not compile this unless it is necessary: */ -static OSStatus CopyIdentityWithLabelOldSchool(char *label, - SecIdentityRef *out_c_a_k) -{ - OSStatus status = errSecItemNotFound; - SecKeychainAttributeList attr_list; - SecKeychainAttribute attr; - SecKeychainSearchRef search = NULL; - SecCertificateRef cert = NULL; - - /* Set up the attribute list: */ - attr_list.count = 1L; - attr_list.attr = &attr; - - /* Set up our lone search criterion: */ - attr.tag = kSecLabelItemAttr; - attr.data = label; - attr.length = (UInt32)strlen(label); - - /* Start searching: */ - status = SecKeychainSearchCreateFromAttributes(NULL, - kSecCertificateItemClass, - &attr_list, - &search); - if(status == noErr) { - status = SecKeychainSearchCopyNext(search, - (SecKeychainItemRef *)&cert); - if(status == noErr && cert) { - /* If we found a certificate, does it have a private key? */ - status = SecIdentityCreateWithCertificate(NULL, cert, out_c_a_k); - CFRelease(cert); - } - } - - if(search) - CFRelease(search); - return status; -} -#endif /* CURL_SUPPORT_MAC_10_6 */ - -static OSStatus CopyIdentityWithLabel(char *label, - SecIdentityRef *out_cert_and_key) -{ - OSStatus status = errSecItemNotFound; - -#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS - CFArrayRef keys_list; - CFIndex keys_list_count; - CFIndex i; - - /* SecItemCopyMatching() was introduced in iOS and Snow Leopard. - kSecClassIdentity was introduced in Lion. If both exist, let's use them - to find the certificate. */ - if(&SecItemCopyMatching && kSecClassIdentity) { - CFTypeRef keys[5]; - CFTypeRef values[5]; - CFDictionaryRef query_dict; - CFStringRef label_cf = CFStringCreateWithCString(NULL, label, - kCFStringEncodingUTF8); - - /* Set up our search criteria and expected results: */ - values[0] = kSecClassIdentity; /* we want a certificate and a key */ - keys[0] = kSecClass; - values[1] = kCFBooleanTrue; /* we want a reference */ - keys[1] = kSecReturnRef; - values[2] = kSecMatchLimitAll; /* kSecMatchLimitOne would be better if the - * label matching below worked correctly */ - keys[2] = kSecMatchLimit; - /* identity searches need a SecPolicyRef in order to work */ - values[3] = SecPolicyCreateSSL(FALSE, NULL); - keys[3] = kSecMatchPolicy; - /* match the name of the certificate (does not work in macOS 10.12.1) */ - values[4] = label_cf; - keys[4] = kSecAttrLabel; - query_dict = CFDictionaryCreate(NULL, (const void **)keys, - (const void **)values, 5L, - &kCFCopyStringDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); - CFRelease(values[3]); - - /* Do we have a match? */ - status = SecItemCopyMatching(query_dict, (CFTypeRef *) &keys_list); - - /* Because kSecAttrLabel matching does not work with kSecClassIdentity, - * we need to find the correct identity ourselves */ - if(status == noErr) { - keys_list_count = CFArrayGetCount(keys_list); - *out_cert_and_key = NULL; - status = 1; - for(i = 0; i < keys_list_count; i++) { - OSStatus err = noErr; - SecCertificateRef cert = NULL; - const void *item = CFArrayGetValueAtIndex(keys_list, i); - SecIdentityRef identity = (SecIdentityRef)CURL_UNCONST(item); - err = SecIdentityCopyCertificate(identity, &cert); - if(err == noErr) { - CFStringRef common_name = NULL; - OSStatus copy_status = noErr; -#if CURL_BUILD_IOS - common_name = SecCertificateCopySubjectSummary(cert); -#elif CURL_BUILD_MAC_10_7 - copy_status = SecCertificateCopyCommonName(cert, &common_name); -#endif - if(copy_status == noErr && - CFStringCompare(common_name, label_cf, 0) == kCFCompareEqualTo) { - CFRelease(cert); - CFRelease(common_name); - CFRetain(identity); - *out_cert_and_key = identity; - status = noErr; - break; - } - if(common_name) - CFRelease(common_name); - } - CFRelease(cert); - } - } - - if(keys_list) - CFRelease(keys_list); - CFRelease(query_dict); - CFRelease(label_cf); - } - else { -#if CURL_SUPPORT_MAC_10_6 - /* On Leopard and Snow Leopard, fall back to SecKeychainSearch. */ - status = CopyIdentityWithLabelOldSchool(label, out_cert_and_key); -#endif /* CURL_SUPPORT_MAC_10_6 */ - } -#elif CURL_SUPPORT_MAC_10_6 - /* For developers building on older cats, we have no choice but to fall back - to SecKeychainSearch. */ - status = CopyIdentityWithLabelOldSchool(label, out_cert_and_key); -#endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */ - return status; -} - -static OSStatus CopyIdentityFromPKCS12File(const char *cPath, - const struct curl_blob *blob, - const char *cPassword, - SecIdentityRef *out_cert_and_key) -{ - OSStatus status = errSecItemNotFound; - CFURLRef pkcs_url = NULL; - CFStringRef password = cPassword ? CFStringCreateWithCString(NULL, - cPassword, kCFStringEncodingUTF8) : NULL; - CFDataRef pkcs_data = NULL; - - /* We can import P12 files on iOS or macOS 10.7 or later: */ - /* These constants are documented as having first appeared in 10.6 but they - raise linker errors when used on that cat for some reason. */ -#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS - bool resource_imported; - - if(blob) { - pkcs_data = CFDataCreate(kCFAllocatorDefault, - (const unsigned char *)blob->data, - (CFIndex)blob->len); - status = (pkcs_data != NULL) ? errSecSuccess : errSecAllocate; - resource_imported = (pkcs_data != NULL); - } - else { - pkcs_url = - CFURLCreateFromFileSystemRepresentation(NULL, - (const UInt8 *)cPath, - (CFIndex)strlen(cPath), FALSE); - resource_imported = - CFURLCreateDataAndPropertiesFromResource(NULL, - pkcs_url, &pkcs_data, - NULL, NULL, &status); - } - - if(resource_imported) { - CFArrayRef items = NULL; - - /* On iOS SecPKCS12Import will never add the client certificate to the - * Keychain. - * - * It gives us back a SecIdentityRef that we can use directly. */ -#if CURL_BUILD_IOS - const void *cKeys[] = {kSecImportExportPassphrase}; - const void *cValues[] = {password}; - CFDictionaryRef options = CFDictionaryCreate(NULL, cKeys, cValues, - password ? 1L : 0L, NULL, NULL); - - if(options) { - status = SecPKCS12Import(pkcs_data, options, &items); - CFRelease(options); - } - - - /* On macOS SecPKCS12Import will always add the client certificate to - * the Keychain. - * - * As this does not match iOS, and apps may not want to see their client - * certificate saved in the user's keychain, we use SecItemImport - * with a NULL keychain to avoid importing it. - * - * This returns a SecCertificateRef from which we can construct a - * SecIdentityRef. - */ -#elif CURL_BUILD_MAC_10_7 - SecItemImportExportKeyParameters keyParams; - SecExternalFormat inputFormat = kSecFormatPKCS12; - SecExternalItemType inputType = kSecItemTypeCertificate; - - memset(&keyParams, 0x00, sizeof(keyParams)); - keyParams.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; - keyParams.passphrase = password; - - status = SecItemImport(pkcs_data, NULL, &inputFormat, &inputType, - 0, &keyParams, NULL, &items); -#endif - - - /* Extract the SecIdentityRef */ - if(status == errSecSuccess && items && CFArrayGetCount(items)) { - CFIndex i, count; - count = CFArrayGetCount(items); - - for(i = 0; i < count; i++) { - const CFTypeRef item = CFArrayGetValueAtIndex(items, i); - CFTypeID itemID = CFGetTypeID(item); - - if(itemID == CFDictionaryGetTypeID()) { - const CFTypeRef identity = CFDictionaryGetValue( - (CFDictionaryRef)item, - kSecImportItemIdentity); - CFRetain(identity); - *out_cert_and_key = (SecIdentityRef)CURL_UNCONST(identity); - break; - } -#if CURL_BUILD_MAC_10_7 - else if(itemID == SecCertificateGetTypeID()) { - status = SecIdentityCreateWithCertificate(NULL, - (SecCertificateRef)CURL_UNCONST(item), - out_cert_and_key); - break; - } -#endif - } - } - - if(items) - CFRelease(items); - CFRelease(pkcs_data); - } -#endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */ - if(password) - CFRelease(password); - if(pkcs_url) - CFRelease(pkcs_url); - return status; -} - -/* This code was borrowed from nss.c, with some modifications: - * Determine whether the nickname passed in is a filename that needs to - * be loaded as a PEM or a nickname. - * - * returns 1 for a file - * returns 0 for not a file - */ -CF_INLINE bool is_file(const char *filename) -{ - struct_stat st; - - if(!filename) - return FALSE; - - if(stat(filename, &st) == 0) - return S_ISREG(st.st_mode); - return FALSE; -} - -static CURLcode -sectransp_set_ssl_version_min_max(struct Curl_easy *data, - struct st_ssl_backend_data *backend, - struct ssl_primary_config *conn_config) -{ -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - OSStatus err; - SSLProtocol ver_min; - SSLProtocol ver_max; - -#if CURL_SUPPORT_MAC_10_7 - if(!&SSLSetProtocolVersionMax) - goto legacy; -#endif - - switch(conn_config->version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - case CURL_SSLVERSION_TLSv1_0: - ver_min = kTLSProtocol1; - break; - case CURL_SSLVERSION_TLSv1_1: - ver_min = kTLSProtocol11; - break; - case CURL_SSLVERSION_TLSv1_2: - ver_min = kTLSProtocol12; - break; - case CURL_SSLVERSION_TLSv1_3: - default: - failf(data, "SSL: unsupported minimum TLS version value"); - return CURLE_SSL_CONNECT_ERROR; - } - - switch(conn_config->version_max) { - case CURL_SSLVERSION_MAX_DEFAULT: - case CURL_SSLVERSION_MAX_NONE: - case CURL_SSLVERSION_MAX_TLSv1_3: - case CURL_SSLVERSION_MAX_TLSv1_2: - ver_max = kTLSProtocol12; - break; - case CURL_SSLVERSION_MAX_TLSv1_1: - ver_max = kTLSProtocol11; - break; - case CURL_SSLVERSION_MAX_TLSv1_0: - ver_max = kTLSProtocol1; - break; - default: - failf(data, "SSL: unsupported maximum TLS version value"); - return CURLE_SSL_CONNECT_ERROR; - } - - err = SSLSetProtocolVersionMin(backend->ssl_ctx, ver_min); - if(err != noErr) { - failf(data, "SSL: failed to set minimum TLS version"); - return CURLE_SSL_CONNECT_ERROR; - } - err = SSLSetProtocolVersionMax(backend->ssl_ctx, ver_max); - if(err != noErr) { - failf(data, "SSL: failed to set maximum TLS version"); - return CURLE_SSL_CONNECT_ERROR; - } - - return CURLE_OK; -#endif -#if CURL_SUPPORT_MAC_10_7 - goto legacy; -legacy: - switch(conn_config->version) { - case CURL_SSLVERSION_DEFAULT: - case CURL_SSLVERSION_TLSv1: - case CURL_SSLVERSION_TLSv1_0: - break; - default: - failf(data, "SSL: unsupported minimum TLS version value"); - return CURLE_SSL_CONNECT_ERROR; - } - - /* only TLS 1.0 is supported, disable SSL 3.0 and SSL 2.0 */ - SSLSetProtocolVersionEnabled(backend->ssl_ctx, kSSLProtocolAll, FALSE); - SSLSetProtocolVersionEnabled(backend->ssl_ctx, kTLSProtocol1, TRUE); - - return CURLE_OK; -#endif -} - -static int sectransp_cipher_suite_get_str(uint16_t id, char *buf, - size_t buf_size, bool prefer_rfc) -{ - /* are these fortezza suites even supported ? */ - if(id == SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA) - msnprintf(buf, buf_size, "%s", "SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA"); - else if(id == SSL_FORTEZZA_DMS_WITH_NULL_SHA) - msnprintf(buf, buf_size, "%s", "SSL_FORTEZZA_DMS_WITH_NULL_SHA"); - /* can TLS_EMPTY_RENEGOTIATION_INFO_SCSV even be set ? */ - else if(id == TLS_EMPTY_RENEGOTIATION_INFO_SCSV) - msnprintf(buf, buf_size, "%s", "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"); - /* do we still need to support these SSL2-only ciphers ? */ - else if(id == SSL_RSA_WITH_RC2_CBC_MD5) - msnprintf(buf, buf_size, "%s", "SSL_RSA_WITH_RC2_CBC_MD5"); - else if(id == SSL_RSA_WITH_IDEA_CBC_MD5) - msnprintf(buf, buf_size, "%s", "SSL_RSA_WITH_IDEA_CBC_MD5"); - else if(id == SSL_RSA_WITH_DES_CBC_MD5) - msnprintf(buf, buf_size, "%s", "SSL_RSA_WITH_DES_CBC_MD5"); - else if(id == SSL_RSA_WITH_3DES_EDE_CBC_MD5) - msnprintf(buf, buf_size, "%s", "SSL_RSA_WITH_3DES_EDE_CBC_MD5"); - else - return Curl_cipher_suite_get_str(id, buf, buf_size, prefer_rfc); - return 0; -} - -static uint16_t sectransp_cipher_suite_walk_str(const char **str, - const char **end) -{ - uint16_t id = Curl_cipher_suite_walk_str(str, end); - size_t len = *end - *str; - - if(!id) { - /* are these fortezza suites even supported ? */ - if(strncasecompare("SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA", *str, len)) - id = SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA; - else if(strncasecompare("SSL_FORTEZZA_DMS_WITH_NULL_SHA", *str, len)) - id = SSL_FORTEZZA_DMS_WITH_NULL_SHA; - /* can TLS_EMPTY_RENEGOTIATION_INFO_SCSV even be set ? */ - else if(strncasecompare("TLS_EMPTY_RENEGOTIATION_INFO_SCSV", *str, len)) - id = TLS_EMPTY_RENEGOTIATION_INFO_SCSV; - /* do we still need to support these SSL2-only ciphers ? */ - else if(strncasecompare("SSL_RSA_WITH_RC2_CBC_MD5", *str, len)) - id = SSL_RSA_WITH_RC2_CBC_MD5; - else if(strncasecompare("SSL_RSA_WITH_IDEA_CBC_MD5", *str, len)) - id = SSL_RSA_WITH_IDEA_CBC_MD5; - else if(strncasecompare("SSL_RSA_WITH_DES_CBC_MD5", *str, len)) - id = SSL_RSA_WITH_DES_CBC_MD5; - else if(strncasecompare("SSL_RSA_WITH_3DES_EDE_CBC_MD5", *str, len)) - id = SSL_RSA_WITH_3DES_EDE_CBC_MD5; - } - return id; -} - -/* allocated memory must be freed */ -static SSLCipherSuite * sectransp_get_supported_ciphers(SSLContextRef ssl_ctx, - size_t *len) -{ - SSLCipherSuite *ciphers = NULL; - OSStatus err = noErr; - *len = 0; - - err = SSLGetNumberSupportedCiphers(ssl_ctx, len); - if(err != noErr) - goto failed; - - ciphers = malloc(*len * sizeof(SSLCipherSuite)); - if(!ciphers) - goto failed; - - err = SSLGetSupportedCiphers(ssl_ctx, ciphers, len); - if(err != noErr) - goto failed; - -#if CURL_BUILD_MAC - { - int maj = 0, min = 0; - GetDarwinVersionNumber(&maj, &min); - /* There is a known bug in early versions of Mountain Lion where ST's ECC - ciphers (cipher suite 0xC001 through 0xC032) simply do not work. - Work around the problem here by disabling those ciphers if we are - running in an affected version of macOS. */ - if(maj == 12 && min <= 3) { - size_t i = 0, j = 0; - for(; i < *len; i++) { - if(ciphers[i] >= 0xC001 && ciphers[i] <= 0xC032) - continue; - ciphers[j++] = ciphers[i]; - } - *len = j; - } - } -#endif - - return ciphers; -failed: - *len = 0; - Curl_safefree(ciphers); - return NULL; -} - -static CURLcode sectransp_set_default_ciphers(struct Curl_easy *data, - SSLContextRef ssl_ctx) -{ - CURLcode ret = CURLE_SSL_CIPHER; - size_t count = 0, i, j; - OSStatus err; - size_t supported_len; - SSLCipherSuite *ciphers = NULL; - - ciphers = sectransp_get_supported_ciphers(ssl_ctx, &supported_len); - if(!ciphers) { - failf(data, "SSL: Failed to get supported ciphers"); - goto failed; - } - - /* Intersect the ciphers supported by Secure Transport with the default - * ciphers, using the order of the former. */ - for(i = 0; i < supported_len; i++) { - for(j = 0; j < CURL_ARRAYSIZE(default_ciphers); j++) { - if(default_ciphers[j] == ciphers[i]) { - ciphers[count++] = ciphers[i]; - break; - } - } - } - - if(count == 0) { - failf(data, "SSL: no supported default ciphers"); - goto failed; - } - - err = SSLSetEnabledCiphers(ssl_ctx, ciphers, count); - if(err != noErr) { - failf(data, "SSL: SSLSetEnabledCiphers() failed: OSStatus %d", err); - goto failed; - } - - ret = CURLE_OK; -failed: - Curl_safefree(ciphers); - return ret; -} - -static CURLcode sectransp_set_selected_ciphers(struct Curl_easy *data, - SSLContextRef ssl_ctx, - const char *ciphers) -{ - CURLcode ret = CURLE_SSL_CIPHER; - size_t count = 0, i; - const char *ptr, *end; - OSStatus err; - size_t supported_len; - SSLCipherSuite *supported = NULL; - SSLCipherSuite *selected = NULL; - - supported = sectransp_get_supported_ciphers(ssl_ctx, &supported_len); - if(!supported) { - failf(data, "SSL: Failed to get supported ciphers"); - goto failed; - } - - selected = malloc(supported_len * sizeof(SSLCipherSuite)); - if(!selected) { - failf(data, "SSL: Failed to allocate memory"); - goto failed; - } - - for(ptr = ciphers; ptr[0] != '\0' && count < supported_len; ptr = end) { - uint16_t id = sectransp_cipher_suite_walk_str(&ptr, &end); - - /* Check if cipher is supported */ - if(id) { - for(i = 0; i < supported_len && supported[i] != id; i++); - if(i == supported_len) - id = 0; - } - if(!id) { - if(ptr[0] != '\0') - infof(data, "SSL: unknown cipher in list: \"%.*s\"", (int) (end - ptr), - ptr); - continue; - } - - /* No duplicates allowed (so selected cannot overflow) */ - for(i = 0; i < count && selected[i] != id; i++); - if(i < count) { - infof(data, "SSL: duplicate cipher in list: \"%.*s\"", (int) (end - ptr), - ptr); - continue; - } - - selected[count++] = id; - } - - if(count == 0) { - failf(data, "SSL: no supported cipher in list"); - goto failed; - } - - err = SSLSetEnabledCiphers(ssl_ctx, selected, count); - if(err != noErr) { - failf(data, "SSL: SSLSetEnabledCiphers() failed: OSStatus %d", err); - goto failed; - } - - ret = CURLE_OK; -failed: - Curl_safefree(supported); - Curl_safefree(selected); - return ret; -} - -static void sectransp_session_free(void *sessionid) -{ - /* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a - cached session ID inside the Security framework. There is a private - function that does this, but I do not want to have to explain to you why I - got your application rejected from the App Store due to the use of a - private API, so the best we can do is free up our own char array that we - created way back in sectransp_connect_step1... */ - Curl_safefree(sessionid); -} - -static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - const struct curl_blob *ssl_cablob = conn_config->ca_info_blob; - const char * const ssl_cafile = - /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ - (ssl_cablob ? NULL : conn_config->CAfile); - const bool verifypeer = conn_config->verifypeer; - char * const ssl_cert = ssl_config->primary.clientcert; - const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob; - char *ciphers; - OSStatus err = noErr; - CURLcode result; -#if CURL_BUILD_MAC - int darwinver_maj = 0, darwinver_min = 0; - - DEBUGASSERT(backend); - - CURL_TRC_CF(data, cf, "connect_step1"); - GetDarwinVersionNumber(&darwinver_maj, &darwinver_min); -#endif /* CURL_BUILD_MAC */ - -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - if(&SSLCreateContext) { /* use the newer API if available */ - if(backend->ssl_ctx) - CFRelease(backend->ssl_ctx); - backend->ssl_ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType); - if(!backend->ssl_ctx) { - failf(data, "SSL: could not create a context"); - return CURLE_OUT_OF_MEMORY; - } - } - else { - /* The old ST API does not exist under iOS, so do not compile it: */ -#if CURL_SUPPORT_MAC_10_8 - if(backend->ssl_ctx) - (void)SSLDisposeContext(backend->ssl_ctx); - err = SSLNewContext(FALSE, &(backend->ssl_ctx)); - if(err != noErr) { - failf(data, "SSL: could not create a context: OSStatus %d", err); - return CURLE_OUT_OF_MEMORY; - } -#endif /* CURL_SUPPORT_MAC_10_8 */ - } -#else - if(backend->ssl_ctx) - (void)SSLDisposeContext(backend->ssl_ctx); - err = SSLNewContext(FALSE, &(backend->ssl_ctx)); - if(err != noErr) { - failf(data, "SSL: could not create a context: OSStatus %d", err); - return CURLE_OUT_OF_MEMORY; - } -#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ - backend->ssl_write_buffered_length = 0UL; /* reset buffered write length */ - - result = sectransp_set_ssl_version_min_max(data, backend, conn_config); - if(result != CURLE_OK) - return result; - - if(connssl->alpn) { -#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 -#ifdef HAVE_BUILTIN_AVAILABLE - if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) { -#else - if(&SSLSetALPNProtocols && &SSLCopyALPNProtocols) { -#endif - struct alpn_proto_buf proto; - size_t i; - CFStringRef cstr; - CFMutableArrayRef alpnArr = CFArrayCreateMutable(NULL, 0, - &kCFTypeArrayCallBacks); - for(i = 0; i < connssl->alpn->count; ++i) { - cstr = CFStringCreateWithCString(NULL, connssl->alpn->entries[i], - kCFStringEncodingUTF8); - if(!cstr) - return CURLE_OUT_OF_MEMORY; - CFArrayAppendValue(alpnArr, cstr); - CFRelease(cstr); - } - err = SSLSetALPNProtocols(backend->ssl_ctx, alpnArr); - if(err != noErr) - infof(data, "WARNING: failed to set ALPN protocols; OSStatus %d", - err); - CFRelease(alpnArr); - Curl_alpn_to_proto_str(&proto, connssl->alpn); - infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); - } -#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ - } - - if(ssl_config->key) { - infof(data, "WARNING: SSL: CURLOPT_SSLKEY is ignored by Secure " - "Transport. The private key must be in the Keychain."); - } - - if(ssl_cert || ssl_cert_blob) { - bool is_cert_data = ssl_cert_blob != NULL; - bool is_cert_file = (!is_cert_data) && is_file(ssl_cert); - SecIdentityRef cert_and_key = NULL; - - /* User wants to authenticate with a client cert. Look for it. Assume that - the user wants to use an identity loaded from the Keychain. If not, try - it as a file on disk */ - - if(!is_cert_data) - err = CopyIdentityWithLabel(ssl_cert, &cert_and_key); - else - err = !noErr; - if((err != noErr) && (is_cert_file || is_cert_data)) { - if(!ssl_config->cert_type) - infof(data, "SSL: Certificate type not set, assuming " - "PKCS#12 format."); - else if(!strcasecompare(ssl_config->cert_type, "P12")) { - failf(data, "SSL: The Security framework only supports " - "loading identities that are in PKCS#12 format."); - return CURLE_SSL_CERTPROBLEM; - } - - err = CopyIdentityFromPKCS12File(ssl_cert, ssl_cert_blob, - ssl_config->key_passwd, - &cert_and_key); - } - - if(err == noErr && cert_and_key) { - SecCertificateRef cert = NULL; - CFTypeRef certs_c[1]; - CFArrayRef certs; - - /* If we found one, print it out: */ - err = SecIdentityCopyCertificate(cert_and_key, &cert); - if(err == noErr) { - char *certp; - result = CopyCertSubject(data, cert, &certp); - if(!result) { - infof(data, "Client certificate: %s", certp); - free(certp); - } - - CFRelease(cert); - if(result == CURLE_PEER_FAILED_VERIFICATION) - return CURLE_SSL_CERTPROBLEM; - if(result) - return result; - } - certs_c[0] = cert_and_key; - certs = CFArrayCreate(NULL, (const void **)certs_c, 1L, - &kCFTypeArrayCallBacks); - err = SSLSetCertificate(backend->ssl_ctx, certs); - if(certs) - CFRelease(certs); - if(err != noErr) { - failf(data, "SSL: SSLSetCertificate() failed: OSStatus %d", err); - return CURLE_SSL_CERTPROBLEM; - } - CFRelease(cert_and_key); - } - else { - const char *cert_showfilename_error = - is_cert_data ? "(memory blob)" : ssl_cert; - - switch(err) { - case errSecAuthFailed: case -25264: /* errSecPkcs12VerifyFailure */ - failf(data, "SSL: Incorrect password for the certificate \"%s\" " - "and its private key.", cert_showfilename_error); - break; - case -26275: /* errSecDecode */ case -25257: /* errSecUnknownFormat */ - failf(data, "SSL: Couldn't make sense of the data in the " - "certificate \"%s\" and its private key.", - cert_showfilename_error); - break; - case -25260: /* errSecPassphraseRequired */ - failf(data, "SSL The certificate \"%s\" requires a password.", - cert_showfilename_error); - break; - case errSecItemNotFound: - failf(data, "SSL: cannot find the certificate \"%s\" and its private " - "key in the Keychain.", cert_showfilename_error); - break; - default: - failf(data, "SSL: cannot load the certificate \"%s\" and its private " - "key: OSStatus %d", cert_showfilename_error, err); - break; - } - return CURLE_SSL_CERTPROBLEM; - } - } - - /* SSL always tries to verify the peer, this only says whether it should - * fail to connect if the verification fails, or if it should continue - * anyway. In the latter case the result of the verification is checked with - * SSL_get_verify_result() below. */ -#if CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS - /* Snow Leopard introduced the SSLSetSessionOption() function, but due to - a library bug with the way the kSSLSessionOptionBreakOnServerAuth flag - works, it does not work as expected under Snow Leopard, Lion or - Mountain Lion. - So we need to call SSLSetEnableCertVerify() on those older cats in order - to disable certificate validation if the user turned that off. - (Secure Transport always validates the certificate chain by default.) - Note: - Darwin 11.x.x is Lion (10.7) - Darwin 12.x.x is Mountain Lion (10.8) - Darwin 13.x.x is Mavericks (10.9) - Darwin 14.x.x is Yosemite (10.10) - Darwin 15.x.x is El Capitan (10.11) - */ -#if CURL_BUILD_MAC - if(&SSLSetSessionOption && darwinver_maj >= 13) { -#else - if(&SSLSetSessionOption) { -#endif /* CURL_BUILD_MAC */ - bool break_on_auth = !conn_config->verifypeer || - ssl_cafile || ssl_cablob; - err = SSLSetSessionOption(backend->ssl_ctx, - kSSLSessionOptionBreakOnServerAuth, - break_on_auth); - if(err != noErr) { - failf(data, "SSL: SSLSetSessionOption() failed: OSStatus %d", err); - return CURLE_SSL_CONNECT_ERROR; - } - } - else { -#if CURL_SUPPORT_MAC_10_8 - err = SSLSetEnableCertVerify(backend->ssl_ctx, - conn_config->verifypeer ? true : FALSE); - if(err != noErr) { - failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err); - return CURLE_SSL_CONNECT_ERROR; - } -#endif /* CURL_SUPPORT_MAC_10_8 */ - } -#else - err = SSLSetEnableCertVerify(backend->ssl_ctx, - conn_config->verifypeer ? true : FALSE); - if(err != noErr) { - failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err); - return CURLE_SSL_CONNECT_ERROR; - } -#endif /* CURL_BUILD_MAC_10_6 || CURL_BUILD_IOS */ - - if((ssl_cafile || ssl_cablob) && verifypeer) { - bool is_cert_data = ssl_cablob != NULL; - bool is_cert_file = (!is_cert_data) && is_file(ssl_cafile); - - if(!(is_cert_file || is_cert_data)) { - failf(data, "SSL: cannot load CA certificate file %s", - ssl_cafile ? ssl_cafile : "(blob memory)"); - return CURLE_SSL_CACERT_BADFILE; - } - } - - /* Configure hostname check. SNI is used if available. - * Both hostname check and SNI require SSLSetPeerDomainName(). - * Also: the verifyhost setting influences SNI usage */ - if(conn_config->verifyhost) { - char *server = connssl->peer.sni ? - connssl->peer.sni : connssl->peer.hostname; - err = SSLSetPeerDomainName(backend->ssl_ctx, server, strlen(server)); - - if(err != noErr) { - failf(data, "SSL: SSLSetPeerDomainName() failed: OSStatus %d", - err); - return CURLE_SSL_CONNECT_ERROR; - } - - if(connssl->peer.type != CURL_SSL_PEER_DNS) { - infof(data, "WARNING: using IP address, SNI is being disabled by " - "the OS."); - } - } - else { - infof(data, "WARNING: disabling hostname validation also disables SNI."); - } - - ciphers = conn_config->cipher_list; - if(ciphers) { - result = sectransp_set_selected_ciphers(data, backend->ssl_ctx, ciphers); - } - else { - result = sectransp_set_default_ciphers(data, backend->ssl_ctx); - } - if(result != CURLE_OK) { - failf(data, "SSL: Unable to set ciphers for SSL/TLS handshake. " - "Error code: %d", (int)result); - return CURLE_SSL_CIPHER; - } - -#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 - /* We want to enable 1/n-1 when using a CBC cipher unless the user - specifically does not want us doing that: */ - if(&SSLSetSessionOption) { - SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionSendOneByteRecord, - !ssl_config->enable_beast); - SSLSetSessionOption(backend->ssl_ctx, kSSLSessionOptionFalseStart, - ssl_config->falsestart); /* false start support */ - } -#endif /* CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 */ - - /* Check if there is a cached ID we can/should use here! */ - if(ssl_config->primary.cache_session) { - char *ssl_sessionid; - size_t ssl_sessionid_len; - - Curl_ssl_scache_lock(data); - ssl_sessionid = Curl_ssl_scache_get_obj(cf, data, - connssl->peer.scache_key); - if(ssl_sessionid) { - /* we got a session id, use it! */ - err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, - strlen(ssl_sessionid)); - Curl_ssl_scache_unlock(data); - if(err != noErr) { - failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err); - return CURLE_SSL_CONNECT_ERROR; - } - else - infof(data, "SSL reusing session ID"); - } - /* If there is not one, then let's make one up! This has to be done prior - to starting the handshake. */ - else { - ssl_sessionid = - aprintf("%s:%d:%d:%s:%d", - ssl_cafile ? ssl_cafile : "(blob memory)", - verifypeer, conn_config->verifyhost, connssl->peer.hostname, - connssl->peer.port); - ssl_sessionid_len = strlen(ssl_sessionid); - - err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len); - if(err != noErr) { - Curl_ssl_scache_unlock(data); - failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err); - return CURLE_SSL_CONNECT_ERROR; - } - - /* This is all a bit weird, as we have not handshaked yet. - * I hope this backend will go away soon. */ - result = Curl_ssl_scache_add_obj(cf, data, connssl->peer.scache_key, - (void *)ssl_sessionid, - sectransp_session_free); - Curl_ssl_scache_unlock(data); - if(result) - return result; - } - } - - err = SSLSetIOFuncs(backend->ssl_ctx, - sectransp_bio_cf_in_read, - sectransp_bio_cf_out_write); - if(err != noErr) { - failf(data, "SSL: SSLSetIOFuncs() failed: OSStatus %d", err); - return CURLE_SSL_CONNECT_ERROR; - } - - err = SSLSetConnection(backend->ssl_ctx, cf); - if(err != noErr) { - failf(data, "SSL: SSLSetConnection() failed: %d", err); - return CURLE_SSL_CONNECT_ERROR; - } - - connssl->connecting_state = ssl_connect_2; - return CURLE_OK; -} - -static long pem_to_der(const char *in, unsigned char **out, size_t *outlen) -{ - char *sep_start, *sep_end, *cert_start, *cert_end; - size_t i, j, err; - size_t len; - char *b64; - - /* Jump through the separators at the beginning of the certificate. */ - sep_start = strstr(in, "-----"); - if(!sep_start) - return 0; - cert_start = strstr(sep_start + 1, "-----"); - if(!cert_start) - return -1; - - cert_start += 5; - - /* Find separator after the end of the certificate. */ - cert_end = strstr(cert_start, "-----"); - if(!cert_end) - return -1; - - sep_end = strstr(cert_end + 1, "-----"); - if(!sep_end) - return -1; - sep_end += 5; - - len = cert_end - cert_start; - b64 = malloc(len + 1); - if(!b64) - return -1; - - /* Create base64 string without linefeeds. */ - for(i = 0, j = 0; i < len; i++) { - if(cert_start[i] != '\r' && cert_start[i] != '\n') - b64[j++] = cert_start[i]; - } - b64[j] = '\0'; - - err = curlx_base64_decode((const char *)b64, out, outlen); - free(b64); - if(err) { - free(*out); - return -1; - } - - return sep_end - in; -} - -#define MAX_CERTS_SIZE (50*1024*1024) /* arbitrary - to catch mistakes */ - -static int read_cert(const char *file, unsigned char **out, size_t *outlen) -{ - int fd; - ssize_t n; - unsigned char buf[512]; - struct dynbuf certs; - - curlx_dyn_init(&certs, MAX_CERTS_SIZE); - - fd = open(file, 0); - if(fd < 0) - return -1; - - for(;;) { - n = read(fd, buf, sizeof(buf)); - if(!n) - break; - if(n < 0) { - close(fd); - curlx_dyn_free(&certs); - return -1; - } - if(curlx_dyn_addn(&certs, buf, n)) { - close(fd); - return -1; - } - } - close(fd); - - *out = curlx_dyn_uptr(&certs); - *outlen = curlx_dyn_len(&certs); - - return 0; -} - -static CURLcode append_cert_to_array(struct Curl_easy *data, - const unsigned char *buf, size_t buflen, - CFMutableArrayRef array) -{ - char *certp; - CURLcode result; - SecCertificateRef cacert; - CFDataRef certdata; - - certdata = CFDataCreate(kCFAllocatorDefault, buf, (CFIndex)buflen); - if(!certdata) { - failf(data, "SSL: failed to allocate array for CA certificate"); - return CURLE_OUT_OF_MEMORY; - } - - cacert = SecCertificateCreateWithData(kCFAllocatorDefault, certdata); - CFRelease(certdata); - if(!cacert) { - failf(data, "SSL: failed to create SecCertificate from CA certificate"); - return CURLE_SSL_CACERT_BADFILE; - } - - /* Check if cacert is valid. */ - result = CopyCertSubject(data, cacert, &certp); - switch(result) { - case CURLE_OK: - break; - case CURLE_PEER_FAILED_VERIFICATION: - CFRelease(cacert); - return CURLE_SSL_CACERT_BADFILE; - case CURLE_OUT_OF_MEMORY: - default: - CFRelease(cacert); - return result; - } - free(certp); - - CFArrayAppendValue(array, cacert); - CFRelease(cacert); - - return CURLE_OK; -} - -static CURLcode verify_cert_buf(struct Curl_cfilter *cf, - struct Curl_easy *data, - const unsigned char *certbuf, size_t buflen, - SSLContextRef ctx) -{ - int n = 0; - CURLcode rc; - long res; - unsigned char *der; - size_t derlen, offset = 0; - OSStatus ret; - SecTrustResultType trust_eval; - CFMutableArrayRef array = NULL; - SecTrustRef trust = NULL; - CURLcode result = CURLE_PEER_FAILED_VERIFICATION; - (void)cf; - /* - * Certbuf now contains the contents of the certificate file, which can be - * - a single DER certificate, - * - a single PEM certificate or - * - a bunch of PEM certificates (certificate bundle). - * - * Go through certbuf, and convert any PEM certificate in it into DER - * format. - */ - array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); - if(!array) { - failf(data, "SSL: out of memory creating CA certificate array"); - result = CURLE_OUT_OF_MEMORY; - goto out; - } - - while(offset < buflen) { - n++; - - /* - * Check if the certificate is in PEM format, and convert it to DER. If - * this fails, we assume the certificate is in DER format. - */ - res = pem_to_der((const char *)certbuf + offset, &der, &derlen); - if(res < 0) { - failf(data, "SSL: invalid CA certificate #%d (offset %zu) in bundle", - n, offset); - result = CURLE_SSL_CACERT_BADFILE; - goto out; - } - offset += res; - - if(res == 0 && offset == 0) { - /* This is not a PEM file, probably a certificate in DER format. */ - rc = append_cert_to_array(data, certbuf, buflen, array); - if(rc != CURLE_OK) { - CURL_TRC_CF(data, cf, "append_cert for CA failed"); - result = rc; - goto out; - } - break; - } - else if(res == 0) { - /* No more certificates in the bundle. */ - break; - } - - rc = append_cert_to_array(data, der, derlen, array); - free(der); - if(rc != CURLE_OK) { - CURL_TRC_CF(data, cf, "append_cert for CA failed"); - result = rc; - goto out; - } - } - - ret = SSLCopyPeerTrust(ctx, &trust); - if(!trust) { - failf(data, "SSL: error getting certificate chain"); - goto out; - } - else if(ret != noErr) { - failf(data, "SSLCopyPeerTrust() returned error %d", ret); - goto out; - } - - CURL_TRC_CF(data, cf, "setting %d trust anchors", n); - ret = SecTrustSetAnchorCertificates(trust, array); - if(ret != noErr) { - failf(data, "SecTrustSetAnchorCertificates() returned error %d", ret); - goto out; - } - ret = SecTrustSetAnchorCertificatesOnly(trust, TRUE); - if(ret != noErr) { - failf(data, "SecTrustSetAnchorCertificatesOnly() returned error %d", ret); - goto out; - } - - trust_eval = 0; - ret = SecTrustEvaluate(trust, &trust_eval); - if(ret != noErr) { - failf(data, "SecTrustEvaluate() returned error %d", ret); - goto out; - } - - switch(trust_eval) { - case kSecTrustResultUnspecified: - /* what does this really mean? */ - CURL_TRC_CF(data, cf, "trust result: Unspecified"); - result = CURLE_OK; - goto out; - case kSecTrustResultProceed: - CURL_TRC_CF(data, cf, "trust result: Proceed"); - result = CURLE_OK; - goto out; - - case kSecTrustResultRecoverableTrustFailure: - failf(data, "SSL: peer not verified: RecoverableTrustFailure"); - goto out; - case kSecTrustResultDeny: - failf(data, "SSL: peer not verified: Deny"); - goto out; - default: - failf(data, "SSL: perr not verified: result=%d", trust_eval); - goto out; - } - -out: - if(trust) - CFRelease(trust); - if(array) - CFRelease(array); - return result; -} - -static CURLcode verify_cert(struct Curl_cfilter *cf, - struct Curl_easy *data, const char *cafile, - const struct curl_blob *ca_info_blob, - SSLContextRef ctx) -{ - CURLcode result; - unsigned char *certbuf; - size_t buflen; - bool free_certbuf = FALSE; - - if(ca_info_blob) { - CURL_TRC_CF(data, cf, "verify_peer, CA from config blob"); - certbuf = ca_info_blob->data; - buflen = ca_info_blob->len; - } - else if(cafile) { - CURL_TRC_CF(data, cf, "verify_peer, CA from file '%s'", cafile); - if(read_cert(cafile, &certbuf, &buflen) < 0) { - failf(data, "SSL: failed to read or invalid CA certificate"); - return CURLE_SSL_CACERT_BADFILE; - } - free_certbuf = TRUE; - } - else - return CURLE_SSL_CACERT_BADFILE; - - result = verify_cert_buf(cf, data, certbuf, buflen, ctx); - if(free_certbuf) - free(certbuf); - return result; -} - - -#ifdef SECTRANSP_PINNEDPUBKEY -static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data, - SSLContextRef ctx, - const char *pinnedpubkey) -{ /* Scratch */ - size_t pubkeylen, realpubkeylen, spkiHeaderLength = 24; - const unsigned char *pubkey = NULL; - unsigned char *realpubkey = NULL; - const unsigned char *spkiHeader = NULL; - CFDataRef publicKeyBits = NULL; - - /* Result is returned to caller */ - CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; - - /* if a path was not specified, do not pin */ - if(!pinnedpubkey) - return CURLE_OK; - - - if(!ctx) - return result; - - do { - SecTrustRef trust; - OSStatus ret; - SecKeyRef keyRef; - - ret = SSLCopyPeerTrust(ctx, &trust); - if(ret != noErr || !trust) - break; - - keyRef = SecTrustCopyPublicKey(trust); - CFRelease(trust); - if(!keyRef) - break; - -#ifdef SECTRANSP_PINNEDPUBKEY_V1 - - publicKeyBits = SecKeyCopyExternalRepresentation(keyRef, NULL); - CFRelease(keyRef); - if(!publicKeyBits) - break; - -#elif SECTRANSP_PINNEDPUBKEY_V2 - - { - OSStatus success; - success = SecItemExport(keyRef, kSecFormatOpenSSL, 0, NULL, - &publicKeyBits); - CFRelease(keyRef); - if(success != errSecSuccess || !publicKeyBits) - break; - } - -#endif /* SECTRANSP_PINNEDPUBKEY_V2 */ - - pubkeylen = (size_t)CFDataGetLength(publicKeyBits); - pubkey = (const unsigned char *)CFDataGetBytePtr(publicKeyBits); - - switch(pubkeylen) { - case 526: - /* 4096 bit RSA pubkeylen == 526 */ - spkiHeader = rsa4096SpkiHeader; - break; - case 270: - /* 2048 bit RSA pubkeylen == 270 */ - spkiHeader = rsa2048SpkiHeader; - break; -#ifdef SECTRANSP_PINNEDPUBKEY_V1 - case 65: - /* ecDSA secp256r1 pubkeylen == 65 */ - spkiHeader = ecDsaSecp256r1SpkiHeader; - spkiHeaderLength = 26; - break; - case 97: - /* ecDSA secp384r1 pubkeylen == 97 */ - spkiHeader = ecDsaSecp384r1SpkiHeader; - spkiHeaderLength = 23; - break; - default: - infof(data, "SSL: unhandled public key length: %zu", pubkeylen); -#elif SECTRANSP_PINNEDPUBKEY_V2 - default: - /* ecDSA secp256r1 pubkeylen == 91 header already included? - * ecDSA secp384r1 header already included too - * we assume rest of algorithms do same, so do nothing - */ - result = Curl_pin_peer_pubkey(data, pinnedpubkey, pubkey, - pubkeylen); -#endif /* SECTRANSP_PINNEDPUBKEY_V2 */ - continue; /* break from loop */ - } - - realpubkeylen = pubkeylen + spkiHeaderLength; - realpubkey = malloc(realpubkeylen); - if(!realpubkey) - break; - - memcpy(realpubkey, spkiHeader, spkiHeaderLength); - memcpy(realpubkey + spkiHeaderLength, pubkey, pubkeylen); - - result = Curl_pin_peer_pubkey(data, pinnedpubkey, realpubkey, - realpubkeylen); - - } while(0); - - Curl_safefree(realpubkey); - if(publicKeyBits) - CFRelease(publicKeyBits); - - return result; -} -#endif /* SECTRANSP_PINNEDPUBKEY */ - -static CURLcode sectransp_connect_step2(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - OSStatus err; - SSLCipherSuite cipher; - SSLProtocol protocol = 0; - - DEBUGASSERT(ssl_connect_2 == connssl->connecting_state); - DEBUGASSERT(backend); - CURL_TRC_CF(data, cf, "connect_step2"); - - /* Here goes nothing: */ -check_handshake: - connssl->io_need = CURL_SSL_IO_NEED_NONE; - err = SSLHandshake(backend->ssl_ctx); - - if(err != noErr) { - switch(err) { - case errSSLWouldBlock: /* they are not done with us yet */ - connssl->io_need = backend->ssl_direction ? - CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV; - return CURLE_OK; - - /* The below is errSSLServerAuthCompleted; it is not defined in - Leopard's headers */ - case -9841: - if((conn_config->CAfile || conn_config->ca_info_blob) && - conn_config->verifypeer) { - CURLcode result = verify_cert(cf, data, conn_config->CAfile, - conn_config->ca_info_blob, - backend->ssl_ctx); - if(result) - return result; - } - /* the documentation says we need to call SSLHandshake() again */ - goto check_handshake; - - /* Problem with encrypt / decrypt */ - case errSSLPeerDecodeError: - failf(data, "Decode failed"); - break; - case errSSLDecryptionFail: - case errSSLPeerDecryptionFail: - failf(data, "Decryption failed"); - break; - case errSSLPeerDecryptError: - failf(data, "A decryption error occurred"); - break; - case errSSLBadCipherSuite: - failf(data, "A bad SSL cipher suite was encountered"); - break; - case errSSLCrypto: - failf(data, "An underlying cryptographic error was encountered"); - break; -#if CURL_BUILD_MAC_10_11 || CURL_BUILD_IOS_9 - case errSSLWeakPeerEphemeralDHKey: - failf(data, "Indicates a weak ephemeral Diffie-Hellman key"); - break; -#endif - - /* Problem with the message record validation */ - case errSSLBadRecordMac: - case errSSLPeerBadRecordMac: - failf(data, "A record with a bad message authentication code (MAC) " - "was encountered"); - break; - case errSSLRecordOverflow: - case errSSLPeerRecordOverflow: - failf(data, "A record overflow occurred"); - break; - - /* Problem with zlib decompression */ - case errSSLPeerDecompressFail: - failf(data, "Decompression failed"); - break; - - /* Problem with access */ - case errSSLPeerAccessDenied: - failf(data, "Access was denied"); - break; - case errSSLPeerInsufficientSecurity: - failf(data, "There is insufficient security for this operation"); - break; - - /* These are all certificate problems with the server: */ - case errSSLXCertChainInvalid: - failf(data, "SSL certificate problem: Invalid certificate chain"); - return CURLE_PEER_FAILED_VERIFICATION; - case errSSLUnknownRootCert: - failf(data, "SSL certificate problem: Untrusted root certificate"); - return CURLE_PEER_FAILED_VERIFICATION; - case errSSLNoRootCert: - failf(data, "SSL certificate problem: No root certificate"); - return CURLE_PEER_FAILED_VERIFICATION; - case errSSLCertNotYetValid: - failf(data, "SSL certificate problem: The certificate chain had a " - "certificate that is not yet valid"); - return CURLE_PEER_FAILED_VERIFICATION; - case errSSLCertExpired: - case errSSLPeerCertExpired: - failf(data, "SSL certificate problem: Certificate chain had an " - "expired certificate"); - return CURLE_PEER_FAILED_VERIFICATION; - case errSSLBadCert: - case errSSLPeerBadCert: - failf(data, "SSL certificate problem: Couldn't understand the server " - "certificate format"); - return CURLE_PEER_FAILED_VERIFICATION; - case errSSLPeerUnsupportedCert: - failf(data, "SSL certificate problem: An unsupported certificate " - "format was encountered"); - return CURLE_PEER_FAILED_VERIFICATION; - case errSSLPeerCertRevoked: - failf(data, "SSL certificate problem: The certificate was revoked"); - return CURLE_PEER_FAILED_VERIFICATION; - case errSSLPeerCertUnknown: - failf(data, "SSL certificate problem: The certificate is unknown"); - return CURLE_PEER_FAILED_VERIFICATION; - - /* These are all certificate problems with the client: */ - case errSecAuthFailed: - failf(data, "SSL authentication failed"); - break; - case errSSLPeerHandshakeFail: - failf(data, "SSL peer handshake failed, the server most likely " - "requires a client certificate to connect"); - break; - case errSSLPeerUnknownCA: - failf(data, "SSL server rejected the client certificate due to " - "the certificate being signed by an unknown certificate " - "authority"); - break; - - /* This error is raised if the server's cert did not match the server's - hostname: */ - case errSSLHostNameMismatch: - failf(data, "SSL certificate peer verification failed, the " - "certificate did not match \"%s\"\n", connssl->peer.dispname); - return CURLE_PEER_FAILED_VERIFICATION; - - /* Problem with SSL / TLS negotiation */ - case errSSLNegotiation: - failf(data, "Could not negotiate an SSL cipher suite with the server"); - break; - case errSSLBadConfiguration: - failf(data, "A configuration error occurred"); - break; - case errSSLProtocol: - failf(data, "SSL protocol error"); - break; - case errSSLPeerProtocolVersion: - failf(data, "A bad protocol version was encountered"); - break; - case errSSLPeerNoRenegotiation: - failf(data, "No renegotiation is allowed"); - break; - - /* Generic handshake errors: */ - case errSSLConnectionRefused: - failf(data, "Server dropped the connection during the SSL handshake"); - break; - case errSSLClosedAbort: - failf(data, "Server aborted the SSL handshake"); - break; - case errSSLClosedGraceful: - failf(data, "The connection closed gracefully"); - break; - case errSSLClosedNoNotify: - failf(data, "The server closed the session with no notification"); - break; - /* Sometimes paramErr happens with buggy ciphers: */ - case paramErr: - case errSSLInternal: - case errSSLPeerInternalError: - failf(data, "Internal SSL engine error encountered during the " - "SSL handshake"); - break; - case errSSLFatalAlert: - failf(data, "Fatal SSL engine error encountered during the SSL " - "handshake"); - break; - /* Unclassified error */ - case errSSLBufferOverflow: - failf(data, "An insufficient buffer was provided"); - break; - case errSSLIllegalParam: - failf(data, "An illegal parameter was encountered"); - break; - case errSSLModuleAttach: - failf(data, "Module attach failure"); - break; - case errSSLSessionNotFound: - failf(data, "An attempt to restore an unknown session failed"); - break; - case errSSLPeerExportRestriction: - failf(data, "An export restriction occurred"); - break; - case errSSLPeerUserCancelled: - failf(data, "The user canceled the operation"); - break; - case errSSLPeerUnexpectedMsg: - failf(data, "Peer rejected unexpected message"); - break; -#if CURL_BUILD_MAC_10_11 || CURL_BUILD_IOS_9 - /* Treating non-fatal error as fatal like before */ - case errSSLClientHelloReceived: - failf(data, "A non-fatal result for providing a server name " - "indication"); - break; -#endif - - /* Error codes defined in the enum but should never be returned. - We list them here just in case. */ -#if CURL_BUILD_MAC_10_6 - /* Only returned when kSSLSessionOptionBreakOnCertRequested is set */ - case errSSLClientCertRequested: - failf(data, "Server requested a client certificate during the " - "handshake"); - return CURLE_SSL_CLIENTCERT; -#endif -#if CURL_BUILD_MAC_10_9 - /* Alias for errSSLLast, end of error range */ - case errSSLUnexpectedRecord: - failf(data, "Unexpected (skipped) record in DTLS"); - break; -#endif - default: - /* May also return codes listed in Security Framework Result Codes */ - failf(data, "Unknown SSL protocol error in connection to %s:%d", - connssl->peer.hostname, err); - break; - } - return CURLE_SSL_CONNECT_ERROR; - } - else { - char cipher_str[64]; - /* we have been connected fine, we are not waiting for anything else. */ - connssl->connecting_state = ssl_connect_3; - -#ifdef SECTRANSP_PINNEDPUBKEY - if(data->set.str[STRING_SSL_PINNEDPUBLICKEY]) { - CURLcode result = - pkp_pin_peer_pubkey(data, backend->ssl_ctx, - data->set.str[STRING_SSL_PINNEDPUBLICKEY]); - if(result) { - failf(data, "SSL: public key does not match pinned public key"); - return result; - } - } -#endif /* SECTRANSP_PINNEDPUBKEY */ - - /* Informational message */ - (void)SSLGetNegotiatedCipher(backend->ssl_ctx, &cipher); - (void)SSLGetNegotiatedProtocolVersion(backend->ssl_ctx, &protocol); - - sectransp_cipher_suite_get_str((uint16_t) cipher, cipher_str, - sizeof(cipher_str), TRUE); - switch(protocol) { - case kSSLProtocol2: - infof(data, "SSL 2.0 connection using %s", cipher_str); - break; - case kSSLProtocol3: - infof(data, "SSL 3.0 connection using %s", cipher_str); - break; - case kTLSProtocol1: - infof(data, "TLS 1.0 connection using %s", cipher_str); - break; -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - case kTLSProtocol11: - infof(data, "TLS 1.1 connection using %s", cipher_str); - break; - case kTLSProtocol12: - infof(data, "TLS 1.2 connection using %s", cipher_str); - break; -#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ -#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 - case kTLSProtocol13: - infof(data, "TLS 1.3 connection using %s", cipher_str); - break; -#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ - default: - infof(data, "Unknown protocol connection"); - break; - } - - if(connssl->alpn) { -#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 -#ifdef HAVE_BUILTIN_AVAILABLE - if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) { -#else - if(&SSLSetALPNProtocols && &SSLCopyALPNProtocols) { -#endif - CFArrayRef alpnArr = NULL; - CFStringRef chosenProtocol = NULL; - err = SSLCopyALPNProtocols(backend->ssl_ctx, &alpnArr); - - if(err == noErr && alpnArr && CFArrayGetCount(alpnArr) >= 1) - chosenProtocol = CFArrayGetValueAtIndex(alpnArr, 0); - -#ifdef USE_HTTP2 - if(chosenProtocol && - !CFStringCompare(chosenProtocol, CFSTR(ALPN_H2), 0)) { - cf->conn->alpn = CURL_HTTP_VERSION_2; - } - else -#endif - if(chosenProtocol && - !CFStringCompare(chosenProtocol, CFSTR(ALPN_HTTP_1_1), 0)) { - cf->conn->alpn = CURL_HTTP_VERSION_1_1; - } - else - infof(data, VTLS_INFOF_NO_ALPN); - - /* chosenProtocol is a reference to the string within alpnArr - and does not need to be freed separately */ - if(alpnArr) - CFRelease(alpnArr); - } -#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */ - } - - return CURLE_OK; - } -} - -static CURLcode -add_cert_to_certinfo(struct Curl_easy *data, - const SecCertificateRef server_cert, - int idx) -{ - CURLcode result = CURLE_OK; - const char *beg; - const char *end; - CFDataRef cert_data = SecCertificateCopyData(server_cert); - - if(!cert_data) - return CURLE_PEER_FAILED_VERIFICATION; - - beg = (const char *)CFDataGetBytePtr(cert_data); - end = beg + CFDataGetLength(cert_data); - result = Curl_extract_certinfo(data, idx, beg, end); - CFRelease(cert_data); - return result; -} - -static CURLcode -collect_server_cert_single(struct Curl_cfilter *cf, struct Curl_easy *data, - const SecCertificateRef server_cert, - CFIndex idx) -{ - CURLcode result = CURLE_OK; - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); -#ifndef CURL_DISABLE_VERBOSE_STRINGS - if(data->set.verbose) { - char *certp; - result = CopyCertSubject(data, server_cert, &certp); - if(!result) { - infof(data, "Server certificate: %s", certp); - free(certp); - } - } -#endif - if(ssl_config->certinfo) - result = add_cert_to_certinfo(data, server_cert, (int)idx); - return result; -} - -/* This should be called during step3 of the connection at the earliest */ -static CURLcode collect_server_cert(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ -#ifndef CURL_DISABLE_VERBOSE_STRINGS - const bool show_verbose_server_cert = data->set.verbose; -#else - const bool show_verbose_server_cert = FALSE; -#endif - struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data); - CURLcode result = ssl_config->certinfo ? - CURLE_PEER_FAILED_VERIFICATION : CURLE_OK; - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - CFArrayRef server_certs = NULL; - SecCertificateRef server_cert; - OSStatus err; - CFIndex i, count; - SecTrustRef trust = NULL; - - DEBUGASSERT(backend); - - if(!show_verbose_server_cert && !ssl_config->certinfo) - return CURLE_OK; - - if(!backend->ssl_ctx) - return result; - -#if CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS -#if CURL_BUILD_IOS -#pragma unused(server_certs) - err = SSLCopyPeerTrust(backend->ssl_ctx, &trust); - /* For some reason, SSLCopyPeerTrust() can return noErr and yet return - a null trust, so be on guard for that: */ - if(err == noErr && trust) { - count = SecTrustGetCertificateCount(trust); - if(ssl_config->certinfo) - result = Curl_ssl_init_certinfo(data, (int)count); - for(i = 0L ; !result && (i < count) ; i++) { - server_cert = SecTrustGetCertificateAtIndex(trust, i); - result = collect_server_cert_single(cf, data, server_cert, i); - } - CFRelease(trust); - } -#else - /* SSLCopyPeerCertificates() is deprecated as of Mountain Lion. - The function SecTrustGetCertificateAtIndex() is officially present - in Lion, but it is unfortunately also present in Snow Leopard as - private API and does not work as expected. So we have to look for - a different symbol to make sure this code is only executed under - Lion or later. */ - if(&SecTrustCopyPublicKey) { -#pragma unused(server_certs) - err = SSLCopyPeerTrust(backend->ssl_ctx, &trust); - /* For some reason, SSLCopyPeerTrust() can return noErr and yet return - a null trust, so be on guard for that: */ - if(err == noErr && trust) { - count = SecTrustGetCertificateCount(trust); - if(ssl_config->certinfo) - result = Curl_ssl_init_certinfo(data, (int)count); - for(i = 0L ; !result && (i < count) ; i++) { - server_cert = SecTrustGetCertificateAtIndex(trust, i); - result = collect_server_cert_single(cf, data, server_cert, i); - } - CFRelease(trust); - } - } - else { -#if CURL_SUPPORT_MAC_10_8 - err = SSLCopyPeerCertificates(backend->ssl_ctx, &server_certs); - /* Just in case SSLCopyPeerCertificates() returns null too... */ - if(err == noErr && server_certs) { - count = CFArrayGetCount(server_certs); - if(ssl_config->certinfo) - result = Curl_ssl_init_certinfo(data, (int)count); - for(i = 0L ; !result && (i < count) ; i++) { - const void *item = CFArrayGetValueAtIndex(server_certs, i); - server_cert = (SecCertificateRef)CURL_UNCONST(item); - result = collect_server_cert_single(cf, data, server_cert, i); - } - CFRelease(server_certs); - } -#endif /* CURL_SUPPORT_MAC_10_8 */ - } -#endif /* CURL_BUILD_IOS */ -#else -#pragma unused(trust) - err = SSLCopyPeerCertificates(backend->ssl_ctx, &server_certs); - if(err == noErr) { - count = CFArrayGetCount(server_certs); - if(ssl_config->certinfo) - result = Curl_ssl_init_certinfo(data, (int)count); - for(i = 0L ; !result && (i < count) ; i++) { - const void *item = CFArrayGetValueAtIndex(server_certs, i); - server_cert = (SecCertificateRef)CURL_UNCONST(item); - result = collect_server_cert_single(cf, data, server_cert, i); - } - CFRelease(server_certs); - } -#endif /* CURL_BUILD_MAC_10_7 || CURL_BUILD_IOS */ - return result; -} - -static CURLcode sectransp_connect_step3(struct Curl_cfilter *cf, - struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - CURLcode result; - - CURL_TRC_CF(data, cf, "connect_step3"); - /* There is no step 3! - * Well, okay, let's collect server certificates, and if verbose mode is on, - * let's print the details of the server certificates. */ - result = collect_server_cert(cf, data); - if(result) - return result; - - connssl->connecting_state = ssl_connect_done; - return CURLE_OK; -} - -static CURLcode sectransp_connect(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool *done) -{ - CURLcode result; - struct ssl_connect_data *connssl = cf->ctx; - - /* check if the connection has already been established */ - if(ssl_connection_complete == connssl->state) { - *done = TRUE; - return CURLE_OK; - } - - *done = FALSE; - connssl->io_need = CURL_SSL_IO_NEED_NONE; - - if(ssl_connect_1 == connssl->connecting_state) { - result = sectransp_connect_step1(cf, data); - if(result) - return result; - } - - if(ssl_connect_2 == connssl->connecting_state) { - result = sectransp_connect_step2(cf, data); - if(result) - return result; - } - - if(ssl_connect_3 == connssl->connecting_state) { - result = sectransp_connect_step3(cf, data); - if(result) - return result; - } - - if(ssl_connect_done == connssl->connecting_state) { - CURL_TRC_CF(data, cf, "connected"); - connssl->state = ssl_connection_complete; - *done = TRUE; - } - - return CURLE_OK; -} - -static ssize_t sectransp_recv(struct Curl_cfilter *cf, - struct Curl_easy *data, - char *buf, - size_t buffersize, - CURLcode *curlcode); - -static CURLcode sectransp_shutdown(struct Curl_cfilter *cf, - struct Curl_easy *data, - bool send_shutdown, bool *done) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - CURLcode result = CURLE_OK; - ssize_t nread = 0; - char buf[1024]; - size_t i; - - DEBUGASSERT(backend); - if(!backend->ssl_ctx || cf->shutdown) { - *done = TRUE; - goto out; - } - - connssl->io_need = CURL_SSL_IO_NEED_NONE; - *done = FALSE; - - if(send_shutdown && !backend->sent_shutdown) { - OSStatus err; - - CURL_TRC_CF(data, cf, "shutdown, send close notify"); - err = SSLClose(backend->ssl_ctx); - switch(err) { - case noErr: - backend->sent_shutdown = TRUE; - break; - case errSSLWouldBlock: - connssl->io_need = CURL_SSL_IO_NEED_SEND; - result = CURLE_OK; - goto out; - default: - CURL_TRC_CF(data, cf, "shutdown, error: %d", (int)err); - result = CURLE_SEND_ERROR; - goto out; - } - } - - for(i = 0; i < 10; ++i) { - if(!backend->sent_shutdown) { - nread = sectransp_recv(cf, data, buf, (int)sizeof(buf), &result); - } - else { - /* We would like to read the close notify from the server using - * Secure Transport, however SSLRead() no longer works after we - * sent the notify from our side. So, we just read from the - * underlying filter and hope it will end. */ - nread = Curl_conn_cf_recv(cf->next, data, buf, sizeof(buf), &result); - } - CURL_TRC_CF(data, cf, "shutdown read -> %zd, %d", nread, result); - if(nread <= 0) - break; - } - - if(nread > 0) { - /* still data coming in? */ - connssl->io_need = CURL_SSL_IO_NEED_RECV; - } - else if(nread == 0) { - /* We got the close notify alert and are done. */ - CURL_TRC_CF(data, cf, "shutdown done"); - *done = TRUE; - } - else if(result == CURLE_AGAIN) { - connssl->io_need = CURL_SSL_IO_NEED_RECV; - result = CURLE_OK; - } - else { - DEBUGASSERT(result); - CURL_TRC_CF(data, cf, "shutdown, error: %d", result); - } - -out: - cf->shutdown = (result || *done); - return result; -} - -static void sectransp_close(struct Curl_cfilter *cf, struct Curl_easy *data) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - - (void) data; - - DEBUGASSERT(backend); - - if(backend->ssl_ctx) { - CURL_TRC_CF(data, cf, "close"); -#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS - if(&SSLCreateContext) - CFRelease(backend->ssl_ctx); -#if CURL_SUPPORT_MAC_10_8 - else - (void)SSLDisposeContext(backend->ssl_ctx); -#endif /* CURL_SUPPORT_MAC_10_8 */ -#else - (void)SSLDisposeContext(backend->ssl_ctx); -#endif /* CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS */ - backend->ssl_ctx = NULL; - } -} - -static size_t sectransp_version(char *buffer, size_t size) -{ - return msnprintf(buffer, size, "SecureTransport"); -} - -static bool sectransp_data_pending(struct Curl_cfilter *cf, - const struct Curl_easy *data) -{ - const struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - OSStatus err; - size_t buffer; - - (void)data; - DEBUGASSERT(backend); - - if(backend->ssl_ctx) { /* SSL is in use */ - CURL_TRC_CF((struct Curl_easy *)CURL_UNCONST(data), cf, "data_pending"); - err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer); - if(err == noErr) - return buffer > 0UL; - return FALSE; - } - else - return FALSE; -} - -static CURLcode sectransp_random(struct Curl_easy *data UNUSED_PARAM, - unsigned char *entropy, size_t length) -{ - /* arc4random_buf() is not available on cats older than Lion, so let's - do this manually for the benefit of the older cats. */ - size_t i; - u_int32_t random_number = 0; - - (void)data; - - for(i = 0 ; i < length ; i++) { - if(i % sizeof(u_int32_t) == 0) - random_number = arc4random(); - entropy[i] = random_number & 0xFF; - random_number >>= 8; - } - i = random_number = 0; - return CURLE_OK; -} - -static CURLcode sectransp_sha256sum(const unsigned char *tmp, /* input */ - size_t tmplen, - unsigned char *sha256sum, /* output */ - size_t sha256len) -{ - (void)sha256len; - assert(sha256len >= CURL_SHA256_DIGEST_LENGTH); - (void)CC_SHA256(tmp, (CC_LONG)tmplen, sha256sum); - return CURLE_OK; -} - -static bool sectransp_false_start(void) -{ -#if CURL_BUILD_MAC_10_9 || CURL_BUILD_IOS_7 - if(&SSLSetSessionOption) - return TRUE; -#endif - return FALSE; -} - -static ssize_t sectransp_send(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *mem, - size_t len, - CURLcode *curlcode) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - size_t processed = 0UL; - OSStatus err; - - DEBUGASSERT(backend); - - /* The SSLWrite() function works a little differently than expected. The - fourth argument (processed) is currently documented in Apple's - documentation as: "On return, the length, in bytes, of the data actually - written." - - Now, one could interpret that as "written to the socket," but actually, - it returns the amount of data that was written to a buffer internal to - the SSLContextRef instead. So it is possible for SSLWrite() to return - errSSLWouldBlock and a number of bytes "written" because those bytes were - encrypted and written to a buffer, not to the socket. - - So if this happens, then we need to keep calling SSLWrite() over and - over again with no new data until it quits returning errSSLWouldBlock. */ - - /* Do we have buffered data to write from the last time we were called? */ - if(backend->ssl_write_buffered_length) { - /* Write the buffered data: */ - err = SSLWrite(backend->ssl_ctx, NULL, 0UL, &processed); - switch(err) { - case noErr: - /* processed is always going to be 0 because we did not write to - the buffer, so return how much was written to the socket */ - processed = backend->ssl_write_buffered_length; - backend->ssl_write_buffered_length = 0UL; - break; - case errSSLWouldBlock: /* argh, try again */ - *curlcode = CURLE_AGAIN; - return -1L; - default: - failf(data, "SSLWrite() returned error %d", err); - *curlcode = CURLE_SEND_ERROR; - return -1L; - } - } - else { - /* We have got new data to write: */ - err = SSLWrite(backend->ssl_ctx, mem, len, &processed); - if(err != noErr) { - switch(err) { - case errSSLWouldBlock: - /* Data was buffered but not sent, we have to tell the caller - to try sending again, and remember how much was buffered */ - backend->ssl_write_buffered_length = len; - *curlcode = CURLE_AGAIN; - return -1L; - default: - failf(data, "SSLWrite() returned error %d", err); - *curlcode = CURLE_SEND_ERROR; - return -1L; - } - } - } - return (ssize_t)processed; -} - -static ssize_t sectransp_recv(struct Curl_cfilter *cf, - struct Curl_easy *data, - char *buf, - size_t buffersize, - CURLcode *curlcode) -{ - struct ssl_connect_data *connssl = cf->ctx; - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); - size_t processed = 0UL; - OSStatus err; - - DEBUGASSERT(backend); - -again: - *curlcode = CURLE_OK; - err = SSLRead(backend->ssl_ctx, buf, buffersize, &processed); - - if(err != noErr) { - switch(err) { - case errSSLWouldBlock: /* return how much we read (if anything) */ - if(processed) { - return (ssize_t)processed; - } - *curlcode = CURLE_AGAIN; - return -1L; - - /* errSSLClosedGraceful - server gracefully shut down the SSL session - errSSLClosedNoNotify - server hung up on us instead of sending a - closure alert notice, read() is returning 0 - Either way, inform the caller that the server disconnected. */ - case errSSLClosedGraceful: - case errSSLClosedNoNotify: - *curlcode = CURLE_OK; - return 0; - - /* The below is errSSLPeerAuthCompleted; it is not defined in - Leopard's headers */ - case -9841: - if((conn_config->CAfile || conn_config->ca_info_blob) && - conn_config->verifypeer) { - CURLcode result = verify_cert(cf, data, conn_config->CAfile, - conn_config->ca_info_blob, - backend->ssl_ctx); - if(result) { - *curlcode = result; - return -1; - } - } - goto again; - default: - failf(data, "SSLRead() return error %d", err); - *curlcode = CURLE_RECV_ERROR; - return -1L; - } - } - return (ssize_t)processed; -} - -static void *sectransp_get_internals(struct ssl_connect_data *connssl, - CURLINFO info UNUSED_PARAM) -{ - struct st_ssl_backend_data *backend = - (struct st_ssl_backend_data *)connssl->backend; - (void)info; - DEBUGASSERT(backend); - return backend->ssl_ctx; -} - -const struct Curl_ssl Curl_ssl_sectransp = { - { CURLSSLBACKEND_SECURETRANSPORT, "secure-transport" }, /* info */ - - SSLSUPP_CAINFO_BLOB | - SSLSUPP_CERTINFO | -#ifdef SECTRANSP_PINNEDPUBKEY - SSLSUPP_PINNEDPUBKEY | -#endif /* SECTRANSP_PINNEDPUBKEY */ - SSLSUPP_HTTPS_PROXY | - SSLSUPP_CIPHER_LIST, - - sizeof(struct st_ssl_backend_data), - - NULL, /* init */ - NULL, /* cleanup */ - sectransp_version, /* version */ - sectransp_shutdown, /* shutdown */ - sectransp_data_pending, /* data_pending */ - sectransp_random, /* random */ - NULL, /* cert_status_request */ - sectransp_connect, /* connect */ - Curl_ssl_adjust_pollset, /* adjust_pollset */ - sectransp_get_internals, /* get_internals */ - sectransp_close, /* close_one */ - NULL, /* close_all */ - NULL, /* set_engine */ - NULL, /* set_engine_default */ - NULL, /* engines_list */ - sectransp_false_start, /* false_start */ - sectransp_sha256sum, /* sha256sum */ - sectransp_recv, /* recv decrypted data */ - sectransp_send, /* send data to encrypt */ - NULL, /* get_channel_binding */ -}; - -#if defined(__GNUC__) && defined(__APPLE__) -#pragma GCC diagnostic pop -#endif - -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#endif /* USE_SECTRANSP */ diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index c6fb60d0a4..db4e57342e 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -43,9 +43,6 @@ #ifdef HAVE_SYS_TYPES_H #include #endif -#ifdef HAVE_SYS_STAT_H -#include -#endif #ifdef HAVE_FCNTL_H #include #endif @@ -61,9 +58,7 @@ #include "gtls.h" /* GnuTLS versions */ #include "wolfssl.h" /* wolfSSL versions */ #include "schannel.h" /* Schannel SSPI version */ -#include "sectransp.h" /* Secure Transport (Darwin) version */ #include "mbedtls.h" /* mbedTLS versions */ -#include "bearssl.h" /* BearSSL versions */ #include "rustls.h" /* Rustls versions */ #include "../slist.h" @@ -212,12 +207,12 @@ match_ssl_primary_config(struct Curl_easy *data, !Curl_timestrcmp(c1->username, c2->username) && !Curl_timestrcmp(c1->password, c2->password) && #endif - strcasecompare(c1->cipher_list, c2->cipher_list) && - strcasecompare(c1->cipher_list13, c2->cipher_list13) && - strcasecompare(c1->curves, c2->curves) && - strcasecompare(c1->signature_algorithms, c2->signature_algorithms) && - strcasecompare(c1->CRLfile, c2->CRLfile) && - strcasecompare(c1->pinned_key, c2->pinned_key)) + curl_strequal(c1->cipher_list, c2->cipher_list) && + curl_strequal(c1->cipher_list13, c2->cipher_list13) && + curl_strequal(c1->curves, c2->curves) && + curl_strequal(c1->signature_algorithms, c2->signature_algorithms) && + curl_strequal(c1->CRLfile, c2->CRLfile) && + curl_strequal(c1->pinned_key, c2->pinned_key)) return TRUE; return FALSE; @@ -491,7 +486,7 @@ static void cf_ctx_free(struct ssl_connect_data *ctx) } CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex, - struct dynbuf *binding) + struct dynbuf *binding) { if(Curl_ssl->get_channel_binding) return Curl_ssl->get_channel_binding(data, sockindex, binding); @@ -505,7 +500,7 @@ void Curl_ssl_close_all(struct Curl_easy *data) } void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, - struct easy_pollset *ps) + struct easy_pollset *ps) { struct ssl_connect_data *connssl = cf->ctx; @@ -878,16 +873,6 @@ bool Curl_ssl_cert_status_request(void) return FALSE; } -/* - * Check whether the SSL backend supports false start. - */ -bool Curl_ssl_false_start(void) -{ - if(Curl_ssl->false_start) - return Curl_ssl->false_start(); - return FALSE; -} - static int multissl_init(void) { if(multissl_setup(NULL)) @@ -906,8 +891,8 @@ static CURLcode multissl_connect(struct Curl_cfilter *cf, } static void multissl_adjust_pollset(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct easy_pollset *ps) + struct Curl_easy *data, + struct easy_pollset *ps) { if(multissl_setup(NULL)) return; @@ -929,23 +914,23 @@ static void multissl_close(struct Curl_cfilter *cf, struct Curl_easy *data) Curl_ssl->close(cf, data); } -static ssize_t multissl_recv_plain(struct Curl_cfilter *cf, - struct Curl_easy *data, - char *buf, size_t len, CURLcode *code) +static CURLcode multissl_recv_plain(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t len, size_t *pnread) { if(multissl_setup(NULL)) return CURLE_FAILED_INIT; - return Curl_ssl->recv_plain(cf, data, buf, len, code); + return Curl_ssl->recv_plain(cf, data, buf, len, pnread); } -static ssize_t multissl_send_plain(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *mem, size_t len, - CURLcode *code) +static CURLcode multissl_send_plain(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *mem, size_t len, + size_t *pnwritten) { if(multissl_setup(NULL)) return CURLE_FAILED_INIT; - return Curl_ssl->send_plain(cf, data, mem, len, code); + return Curl_ssl->send_plain(cf, data, mem, len, pnwritten); } static const struct Curl_ssl Curl_ssl_multi = { @@ -968,7 +953,6 @@ static const struct Curl_ssl Curl_ssl_multi = { NULL, /* set_engine */ NULL, /* set_engine_default */ NULL, /* engines_list */ - NULL, /* false_start */ NULL, /* sha256sum */ multissl_recv_plain, /* recv decrypted data */ multissl_send_plain, /* send data to encrypt */ @@ -988,12 +972,8 @@ const struct Curl_ssl *Curl_ssl = &Curl_ssl_rustls; #elif defined(USE_OPENSSL) &Curl_ssl_openssl; -#elif defined(USE_SECTRANSP) - &Curl_ssl_sectransp; #elif defined(USE_SCHANNEL) &Curl_ssl_schannel; -#elif defined(USE_BEARSSL) - &Curl_ssl_bearssl; #else #error "Missing struct Curl_ssl for selected SSL backend" #endif @@ -1011,15 +991,9 @@ static const struct Curl_ssl *available_backends[] = { #if defined(USE_OPENSSL) &Curl_ssl_openssl, #endif -#if defined(USE_SECTRANSP) - &Curl_ssl_sectransp, -#endif #if defined(USE_SCHANNEL) &Curl_ssl_schannel, #endif -#if defined(USE_BEARSSL) - &Curl_ssl_bearssl, -#endif #if defined(USE_RUSTLS) &Curl_ssl_rustls, #endif @@ -1099,7 +1073,7 @@ static int multissl_setup(const struct Curl_ssl *backend) env = curl_getenv("CURL_SSL_BACKEND"); if(env) { for(i = 0; available_backends[i]; i++) { - if(strcasecompare(env, available_backends[i]->info.name)) { + if(curl_strequal(env, available_backends[i]->info.name)) { Curl_ssl = available_backends[i]; free(env); return 0; @@ -1109,8 +1083,8 @@ static int multissl_setup(const struct Curl_ssl *backend) #ifdef CURL_DEFAULT_SSL_BACKEND for(i = 0; available_backends[i]; i++) { - if(strcasecompare(CURL_DEFAULT_SSL_BACKEND, - available_backends[i]->info.name)) { + if(curl_strequal(CURL_DEFAULT_SSL_BACKEND, + available_backends[i]->info.name)) { Curl_ssl = available_backends[i]; free(env); return 0; @@ -1136,7 +1110,7 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, if(Curl_ssl != &Curl_ssl_multi) return id == Curl_ssl->info.id || - (name && strcasecompare(name, Curl_ssl->info.name)) ? + (name && curl_strequal(name, Curl_ssl->info.name)) ? CURLSSLSET_OK : #if defined(CURL_WITH_MULTI_SSL) CURLSSLSET_TOO_LATE; @@ -1146,7 +1120,7 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name, for(i = 0; available_backends[i]; i++) { if(available_backends[i]->info.id == id || - (name && strcasecompare(available_backends[i]->info.name, name))) { + (name && curl_strequal(available_backends[i]->info.name, name))) { multissl_setup(available_backends[i]); return CURLSSLSET_OK; } @@ -1372,7 +1346,7 @@ static CURLcode ssl_cf_set_earlydata(struct Curl_cfilter *cf, const void *buf, size_t blen) { struct ssl_connect_data *connssl = cf->ctx; - ssize_t nwritten = 0; + size_t nwritten = 0; CURLcode result = CURLE_OK; DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_await); @@ -1380,10 +1354,10 @@ static CURLcode ssl_cf_set_earlydata(struct Curl_cfilter *cf, if(blen) { if(blen > connssl->earlydata_max) blen = connssl->earlydata_max; - nwritten = Curl_bufq_write(&connssl->earlydata, buf, blen, &result); + result = Curl_bufq_write(&connssl->earlydata, buf, blen, &nwritten); CURL_TRC_CF(data, cf, "ssl_cf_set_earlydata(len=%zu) -> %zd", blen, nwritten); - if(nwritten < 0) + if(result) return result; } return CURLE_OK; @@ -1455,29 +1429,26 @@ static bool ssl_cf_data_pending(struct Curl_cfilter *cf, return result; } -static ssize_t ssl_cf_send(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *buf, size_t blen, - bool eos, CURLcode *err) +static CURLcode ssl_cf_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *buf, size_t blen, + bool eos, size_t *pnwritten) { struct ssl_connect_data *connssl = cf->ctx; struct cf_call_data save; - ssize_t nwritten = 0, early_written = 0; + CURLcode result = CURLE_OK; (void)eos; - *err = CURLE_OK; + *pnwritten = 0; CF_DATA_SAVE(save, cf, data); if(connssl->state == ssl_connection_deferred) { bool done = FALSE; - *err = ssl_cf_connect_deferred(cf, data, buf, blen, &done); - if(*err) { - nwritten = -1; + result = ssl_cf_connect_deferred(cf, data, buf, blen, &done); + if(result) goto out; - } else if(!done) { - *err = CURLE_AGAIN; - nwritten = -1; + result = CURLE_AGAIN; goto out; } DEBUGASSERT(connssl->state == ssl_connection_complete); @@ -1486,12 +1457,12 @@ static ssize_t ssl_cf_send(struct Curl_cfilter *cf, if(connssl->earlydata_skip) { if(connssl->earlydata_skip >= blen) { connssl->earlydata_skip -= blen; - *err = CURLE_OK; - nwritten = (ssize_t)blen; + result = CURLE_OK; + *pnwritten = blen; goto out; } else { - early_written = connssl->earlydata_skip; + *pnwritten = connssl->earlydata_skip; buf = ((const char *)buf) + connssl->earlydata_skip; blen -= connssl->earlydata_skip; connssl->earlydata_skip = 0; @@ -1499,56 +1470,45 @@ static ssize_t ssl_cf_send(struct Curl_cfilter *cf, } /* OpenSSL and maybe other TLS libs do not like 0-length writes. Skip. */ - if(blen > 0) - nwritten = connssl->ssl_impl->send_plain(cf, data, buf, blen, err); - - if(nwritten >= 0) - nwritten += early_written; + if(blen > 0) { + size_t nwritten; + result = connssl->ssl_impl->send_plain(cf, data, buf, blen, &nwritten); + if(!result) + *pnwritten += nwritten; + } out: CF_DATA_RESTORE(cf, save); - return nwritten; + return result; } -static ssize_t ssl_cf_recv(struct Curl_cfilter *cf, - struct Curl_easy *data, char *buf, size_t len, - CURLcode *err) +static CURLcode ssl_cf_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, char *buf, size_t len, + size_t *pnread) { struct ssl_connect_data *connssl = cf->ctx; struct cf_call_data save; - ssize_t nread; + CURLcode result = CURLE_OK; CF_DATA_SAVE(save, cf, data); - *err = CURLE_OK; + *pnread = 0; if(connssl->state == ssl_connection_deferred) { bool done = FALSE; - *err = ssl_cf_connect_deferred(cf, data, NULL, 0, &done); - if(*err) { - nread = -1; + result = ssl_cf_connect_deferred(cf, data, NULL, 0, &done); + if(result) goto out; - } else if(!done) { - *err = CURLE_AGAIN; - nread = -1; + result = CURLE_AGAIN; goto out; } DEBUGASSERT(connssl->state == ssl_connection_complete); } - nread = connssl->ssl_impl->recv_plain(cf, data, buf, len, err); - if(nread > 0) { - DEBUGASSERT((size_t)nread <= len); - } - else if(nread == 0) { - /* eof */ - *err = CURLE_OK; - } + result = connssl->ssl_impl->recv_plain(cf, data, buf, len, pnread); out: - CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len, - nread, *err); CF_DATA_RESTORE(cf, save); - return nread; + return result; } static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf, @@ -1598,6 +1558,18 @@ static CURLcode ssl_cf_query(struct Curl_cfilter *cf, *when = connssl->handshake_done; return CURLE_OK; } + case CF_QUERY_SSL_INFO: + case CF_QUERY_SSL_CTX_INFO: { + struct curl_tlssessioninfo *info = pres2; + struct cf_call_data save; + CF_DATA_SAVE(save, cf, data); + info->backend = Curl_ssl_backend(); + info->internals = connssl->ssl_impl->get_internals( + cf->ctx, (query == CF_QUERY_SSL_INFO) ? + CURLINFO_TLS_SSL_PTR : CURLINFO_TLS_SESSION); + CF_DATA_RESTORE(cf, save); + return CURLE_OK; + } default: break; } @@ -1625,7 +1597,6 @@ struct Curl_cftype Curl_cft_ssl = { ssl_cf_connect, ssl_cf_close, ssl_cf_shutdown, - Curl_cf_def_get_host, ssl_cf_adjust_pollset, ssl_cf_data_pending, ssl_cf_send, @@ -1646,7 +1617,6 @@ struct Curl_cftype Curl_cft_ssl_proxy = { ssl_cf_connect, ssl_cf_close, ssl_cf_shutdown, - Curl_cf_def_get_host, ssl_cf_adjust_pollset, ssl_cf_data_pending, ssl_cf_send, @@ -1769,40 +1739,6 @@ bool Curl_ssl_supports(struct Curl_easy *data, unsigned int ssl_option) return (Curl_ssl->supports & ssl_option); } -static struct Curl_cfilter *get_ssl_filter(struct Curl_cfilter *cf) -{ - for(; cf; cf = cf->next) { - if(cf->cft == &Curl_cft_ssl) - return cf; -#ifndef CURL_DISABLE_PROXY - if(cf->cft == &Curl_cft_ssl_proxy) - return cf; -#endif - } - return NULL; -} - - -void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, - CURLINFO info, int n) -{ - void *result = NULL; - (void)n; - if(data->conn) { - struct Curl_cfilter *cf; - /* get first SSL filter in chain, if any is present */ - cf = get_ssl_filter(data->conn->cfilter[sockindex]); - if(cf) { - struct ssl_connect_data *connssl = cf->ctx; - struct cf_call_data save; - CF_DATA_SAVE(save, cf, data); - result = connssl->ssl_impl->get_internals(cf->ctx, info); - CF_DATA_RESTORE(cf, save); - } - } - return result; -} - static CURLcode vtls_shutdown_blocking(struct Curl_cfilter *cf, struct Curl_easy *data, bool send_shutdown, bool *done) diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index 0bb333b987..c62b8ae258 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -190,8 +190,6 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data, bool Curl_ssl_cert_status_request(void); -bool Curl_ssl_false_start(void); - /* The maximum size of the SSL channel binding is 85 bytes, as defined in * RFC 5929, Section 4.1. The 'tls-server-end-point:' prefix is 21 bytes long, * and SHA-512 is the longest supported hash algorithm, with a digest length of @@ -209,7 +207,7 @@ bool Curl_ssl_false_start(void); * returned. */ CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex, - struct dynbuf *binding); + struct dynbuf *binding); #define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */ @@ -235,16 +233,6 @@ CURLcode Curl_cf_ssl_proxy_insert_after(struct Curl_cfilter *cf_at, */ bool Curl_ssl_supports(struct Curl_easy *data, unsigned int ssl_option); -/** - * Get the internal ssl instance (like OpenSSL's SSL*) from the filter - * chain at `sockindex` of type specified by `info`. - * For `n` == 0, the first active (top down) instance is returned. - * 1 gives the second active, etc. - * NULL is returned when no active SSL filter is present. - */ -void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex, - CURLINFO info, int n); - /** * Get the ssl_config_data in `data` that is relevant for cfilter `cf`. */ @@ -274,8 +262,6 @@ extern struct Curl_cftype Curl_cft_ssl_proxy; #define Curl_ssl_free_certinfo(x) Curl_nop_stmt #define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN) #define Curl_ssl_cert_status_request() FALSE -#define Curl_ssl_false_start() FALSE -#define Curl_ssl_get_internals(a,b,c,d) NULL #define Curl_ssl_supports(a,b) FALSE #define Curl_ssl_cfilter_add(a,b,c) CURLE_NOT_BUILT_IN #define Curl_ssl_cfilter_remove(a,b,c) CURLE_OK diff --git a/lib/vtls/vtls_int.h b/lib/vtls/vtls_int.h index 0632a07657..11987c382a 100644 --- a/lib/vtls/vtls_int.h +++ b/lib/vtls/vtls_int.h @@ -127,6 +127,7 @@ struct ssl_connect_data { BIT(use_alpn); /* if ALPN shall be used in handshake */ BIT(peer_closed); /* peer has closed connection */ BIT(prefs_checked); /* SSL preferences have been checked */ + BIT(input_pending); /* data for SSL_read() may be available */ }; @@ -175,13 +176,12 @@ struct Curl_ssl { CURLcode (*set_engine_default)(struct Curl_easy *data); struct curl_slist *(*engines_list)(struct Curl_easy *data); - bool (*false_start)(void); CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen, unsigned char *sha256sum, size_t sha256sumlen); - ssize_t (*recv_plain)(struct Curl_cfilter *cf, struct Curl_easy *data, - char *buf, size_t len, CURLcode *code); - ssize_t (*send_plain)(struct Curl_cfilter *cf, struct Curl_easy *data, - const void *mem, size_t len, CURLcode *code); + CURLcode (*recv_plain)(struct Curl_cfilter *cf, struct Curl_easy *data, + char *buf, size_t len, size_t *pnread); + CURLcode (*send_plain)(struct Curl_cfilter *cf, struct Curl_easy *data, + const void *mem, size_t len, size_t *pnwritten); CURLcode (*get_channel_binding)(struct Curl_easy *data, int sockindex, struct dynbuf *binding); diff --git a/lib/vtls/vtls_scache.c b/lib/vtls/vtls_scache.c index 1fe4b5bbde..662539cd89 100644 --- a/lib/vtls/vtls_scache.c +++ b/lib/vtls/vtls_scache.c @@ -29,9 +29,6 @@ #ifdef HAVE_SYS_TYPES_H #include #endif -#ifdef HAVE_SYS_STAT_H -#include -#endif #ifdef HAVE_FCNTL_H #include #endif @@ -376,9 +373,9 @@ void Curl_ssl_scache_unlock(struct Curl_easy *data) } static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf, - const char *name, - char *path, - bool *is_local) + const char *name, + char *path, + bool *is_local) { if(path && path[0]) { /* We try to add absolute paths, so that the session key can stay @@ -410,8 +407,8 @@ static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf, } static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf, - const char *name, - struct curl_blob *blob) + const char *name, + struct curl_blob *blob) { CURLcode r = CURLE_OK; if(blob && blob->len) { @@ -649,7 +646,7 @@ cf_ssl_find_peer_by_key(struct Curl_easy *data, /* check for entries with known peer_key */ for(i = 0; scache && i < scache->peer_count; i++) { if(scache->peers[i].ssl_peer_key && - strcasecompare(ssl_peer_key, scache->peers[i].ssl_peer_key) && + curl_strequal(ssl_peer_key, scache->peers[i].ssl_peer_key) && cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) { /* yes, we have a cached session for this! */ *ppeer = &scache->peers[i]; @@ -868,9 +865,9 @@ CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf, } void Curl_ssl_scache_return(struct Curl_cfilter *cf, - struct Curl_easy *data, - const char *ssl_peer_key, - struct Curl_ssl_session *s) + struct Curl_easy *data, + const char *ssl_peer_key, + struct Curl_ssl_session *s) { /* See RFC 8446 C.4: * "Clients SHOULD NOT reuse a ticket for multiple connections." */ diff --git a/lib/vtls/vtls_scache.h b/lib/vtls/vtls_scache.h index eef50805e4..deedccfe8c 100644 --- a/lib/vtls/vtls_scache.h +++ b/lib/vtls/vtls_scache.h @@ -206,12 +206,6 @@ CURLcode Curl_ssl_session_export(struct Curl_easy *data, void *userptr); #endif /* USE_SSLS_EXPORT */ - -#else /* USE_SSL */ - -#define Curl_ssl_scache_create(x,y,z) ((void)x, CURLE_OK) -#define Curl_ssl_scache_destroy(x) do {} while(0) - -#endif /* USE_SSL (else) */ +#endif /* USE_SSL */ #endif /* HEADER_CURL_VTLS_SCACHE_H */ diff --git a/lib/vtls/wolfssl.c b/lib/vtls/wolfssl.c index 9c6f518260..c3560e8b8d 100644 --- a/lib/vtls/wolfssl.c +++ b/lib/vtls/wolfssl.c @@ -67,7 +67,6 @@ #include "../connect.h" /* for the connect timeout */ #include "../progress.h" #include "../select.h" -#include "../strcase.h" #include "../strdup.h" #include "x509asn1.h" #include "../curl_printf.h" @@ -227,9 +226,9 @@ static int wssl_do_file_type(const char *type) { if(!type || !type[0]) return WOLFSSL_FILETYPE_PEM; - if(strcasecompare(type, "PEM")) + if(curl_strequal(type, "PEM")) return WOLFSSL_FILETYPE_PEM; - if(strcasecompare(type, "DER")) + if(curl_strequal(type, "DER")) return WOLFSSL_FILETYPE_ASN1; return -1; } @@ -247,6 +246,9 @@ static const struct group_name_map gnm[] = { { WOLFSSL_P256_ML_KEM_512, "P256_ML_KEM_512" }, { WOLFSSL_P384_ML_KEM_768, "P384_ML_KEM_768" }, { WOLFSSL_P521_ML_KEM_1024, "P521_ML_KEM_1024" }, + { WOLFSSL_P256_ML_KEM_768, "P256_ML_KEM_768" }, + { WOLFSSL_P384_ML_KEM_1024, "P384_ML_KEM_1024" }, + { WOLFSSL_X25519_ML_KEM_768, "X25519_ML_KEM_768" }, { 0, NULL } }; #endif @@ -319,7 +321,7 @@ static int wssl_bio_cf_out_write(WOLFSSL_BIO *bio, struct ssl_connect_data *connssl = cf->ctx; struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nwritten, skiplen = 0; + size_t nwritten, skiplen = 0; CURLcode result = CURLE_OK; DEBUGASSERT(data); @@ -333,21 +335,21 @@ static int wssl_bio_cf_out_write(WOLFSSL_BIO *bio, skiplen = (ssize_t)(blen - wssl->io_send_blocked_len); blen = wssl->io_send_blocked_len; } - nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &result); + result = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &nwritten); wssl->io_result = result; - CURL_TRC_CF(data, cf, "bio_write(len=%d) -> %zd, %d", - blen, nwritten, result); + CURL_TRC_CF(data, cf, "bio_write(len=%d) -> %d, %zu", + blen, result, nwritten); #ifdef USE_FULL_BIO wolfSSL_BIO_clear_retry_flags(bio); #endif - if(nwritten < 0 && CURLE_AGAIN == result) { + if(CURLE_AGAIN == result) { wolfSSL_BIO_set_retry_write(bio); if(wssl->shutting_down && !wssl->io_send_blocked_len) wssl->io_send_blocked_len = blen; } else if(!result && skiplen) nwritten += skiplen; - return (int)nwritten; + return result ? -1 : (int)nwritten; } static int wssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) @@ -356,7 +358,7 @@ static int wssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) struct ssl_connect_data *connssl = cf->ctx; struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend; struct Curl_easy *data = CF_DATA_CURRENT(cf); - ssize_t nread; + size_t nread; CURLcode result = CURLE_OK; DEBUGASSERT(data); @@ -376,17 +378,17 @@ static int wssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen) } } - nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result); + result = Curl_conn_cf_recv(cf->next, data, buf, blen, &nread); wssl->io_result = result; - CURL_TRC_CF(data, cf, "bio_read(len=%d) -> %zd, %d", blen, nread, result); + CURL_TRC_CF(data, cf, "bio_read(len=%d) -> %d, %zu", blen, result, nread); #ifdef USE_FULL_BIO wolfSSL_BIO_clear_retry_flags(bio); #endif - if(nread < 0 && CURLE_AGAIN == result) + if(CURLE_AGAIN == result) wolfSSL_BIO_set_retry_read(bio); else if(nread == 0) connssl->peer_closed = TRUE; - return (int)nread; + return result ? -1 : (int)nread; } static WOLFSSL_BIO_METHOD *wssl_bio_cf_method = NULL; @@ -518,7 +520,7 @@ static CURLcode wssl_on_session_reuse(struct Curl_cfilter *cf, #endif if(!connssl->earlydata_max) { - /* Seems to be GnuTLS way to signal no EarlyData in session */ + /* Seems to be no WolfSSL way to signal no EarlyData in session */ CURL_TRC_CF(data, cf, "SSL session does not allow earlydata"); } else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) { @@ -962,6 +964,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, size_t idx = 0; #endif CURLcode result = CURLE_FAILED_INIT; + unsigned char transport; DEBUGASSERT(!wctx->ssl_ctx); DEBUGASSERT(!wctx->ssl); @@ -971,6 +974,8 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, goto out; } Curl_alpn_copy(&alpns, alpns_requested); + DEBUGASSERT(cf->next); + transport = Curl_conn_cf_get_transport(cf->next, data); #if LIBWOLFSSL_VERSION_HEX < 0x04002000 /* 4.2.0 (2019) */ req_method = wolfSSLv23_client_method(); @@ -1101,7 +1106,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, #endif curves = conn_config->curves; - if(!curves && cf->conn->transport == TRNSPRT_QUIC) + if(!curves && (transport == TRNSPRT_QUIC)) curves = (char *)CURL_UNCONST(QUIC_GROUPS); if(curves) { @@ -1242,8 +1247,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, } #endif - if(ssl_config->primary.cache_session && - cf->conn->transport != TRNSPRT_QUIC) { + if(ssl_config->primary.cache_session && (transport != TRNSPRT_QUIC)) { /* Register to get notified when a new session is received */ wolfSSL_CTX_sess_set_new_cb(wctx->ssl_ctx, wssl_vtls_new_session_cb); } @@ -1289,7 +1293,7 @@ CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx, wolfSSL_set_app_data(wctx->ssl, ssl_user_data); #ifdef WOLFSSL_QUIC - if(cf->conn->transport == TRNSPRT_QUIC) + if(transport == TRNSPRT_QUIC) wolfSSL_set_quic_use_legacy_codepoint(wctx->ssl, 0); #endif @@ -1789,49 +1793,48 @@ static CURLcode wssl_handshake(struct Curl_cfilter *cf, } } -static ssize_t wssl_send(struct Curl_cfilter *cf, - struct Curl_easy *data, - const void *buf, size_t blen, - CURLcode *curlcode) +static CURLcode wssl_send(struct Curl_cfilter *cf, + struct Curl_easy *data, + const void *buf, size_t blen, + size_t *pnwritten) { struct ssl_connect_data *connssl = cf->ctx; struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend; - size_t total_written = 0; - ssize_t nwritten = -1; - DEBUGASSERT(wssl); + CURLcode result = CURLE_OK; + int nwritten; + DEBUGASSERT(wssl); + *pnwritten = 0; wolfSSL_ERR_clear_error(); if(blen) { int memlen = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen; - int rc; - rc = wolfSSL_write(wssl->ssl, buf, memlen); - if(rc <= 0) { - int err = wolfSSL_get_error(wssl->ssl, rc); + nwritten = wolfSSL_write(wssl->ssl, buf, memlen); + + if(nwritten > 0) + *pnwritten += (size_t)nwritten; + else { + int err = wolfSSL_get_error(wssl->ssl, nwritten); switch(err) { case WOLFSSL_ERROR_WANT_READ: case WOLFSSL_ERROR_WANT_WRITE: /* there is data pending, re-invoke wolfSSL_write() */ - if(total_written) { - *curlcode = CURLE_OK; - nwritten = total_written; + if(*pnwritten) { + result = CURLE_OK; goto out; } - *curlcode = CURLE_AGAIN; - nwritten = -1; + result = CURLE_AGAIN; goto out; default: if(wssl->io_result == CURLE_AGAIN) { - if(total_written) { - *curlcode = CURLE_OK; - nwritten = total_written; + if(*pnwritten) { + result = CURLE_OK; goto out; } - *curlcode = CURLE_AGAIN; - nwritten = -1; + result = CURLE_AGAIN; goto out; } { @@ -1841,21 +1844,16 @@ static ssize_t wssl_send(struct Curl_cfilter *cf, sizeof(error_buffer)), SOCKERRNO); } - *curlcode = CURLE_SEND_ERROR; - nwritten = -1; + result = CURLE_SEND_ERROR; goto out; } } - else - total_written += rc; } - *curlcode = CURLE_OK; - nwritten = total_written; out: - CURL_TRC_CF(data, cf, "wssl_send(len=%zu) -> %" FMT_OFF_T ", %d", - blen, nwritten, *curlcode); - return nwritten; + CURL_TRC_CF(data, cf, "wssl_send(len=%zu) -> %d, %zu", + blen, result, *pnwritten); + return result; } static CURLcode wssl_shutdown(struct Curl_cfilter *cf, @@ -1982,10 +1980,10 @@ static void wssl_close(struct Curl_cfilter *cf, struct Curl_easy *data) } } -static ssize_t wssl_recv(struct Curl_cfilter *cf, - struct Curl_easy *data, - char *buf, size_t blen, - CURLcode *curlcode) +static CURLcode wssl_recv(struct Curl_cfilter *cf, + struct Curl_easy *data, + char *buf, size_t blen, + size_t *pnread) { struct ssl_connect_data *connssl = cf->ctx; struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend; @@ -1994,41 +1992,38 @@ static ssize_t wssl_recv(struct Curl_cfilter *cf, DEBUGASSERT(wssl); + *pnread = 0; wolfSSL_ERR_clear_error(); - *curlcode = CURLE_OK; nread = wolfSSL_read(wssl->ssl, buf, buffsize); - if(nread <= 0) { + if(nread > 0) + *pnread = (size_t)nread; + else { int err = wolfSSL_get_error(wssl->ssl, nread); switch(err) { case WOLFSSL_ERROR_ZERO_RETURN: /* no more data */ CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> CLOSED", blen); - *curlcode = CURLE_OK; - return 0; + return CURLE_OK; case WOLFSSL_ERROR_NONE: case WOLFSSL_ERROR_WANT_READ: case WOLFSSL_ERROR_WANT_WRITE: if(!wssl->io_result && connssl->peer_closed) { CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> CLOSED", blen); - *curlcode = CURLE_OK; - return 0; + return CURLE_OK; } /* there is data pending, re-invoke wolfSSL_read() */ CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen); - *curlcode = CURLE_AGAIN; - return -1; + return CURLE_AGAIN; default: if(wssl->io_result == CURLE_AGAIN) { CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen); - *curlcode = CURLE_AGAIN; - return -1; + return CURLE_AGAIN; } else if(!wssl->io_result && connssl->peer_closed) { CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> CLOSED", blen); - *curlcode = CURLE_OK; - return 0; + failf(data, "Connection closed abruptly"); } else { char error_buffer[256]; @@ -2037,13 +2032,12 @@ static ssize_t wssl_recv(struct Curl_cfilter *cf, sizeof(error_buffer)), SOCKERRNO); } - *curlcode = CURLE_RECV_ERROR; - return -1; + return CURLE_RECV_ERROR; } } - CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> %d", blen, nread); - return nread; + CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> 0, %zu", blen, *pnread); + return CURLE_OK; } size_t Curl_wssl_version(char *buffer, size_t size) @@ -2095,6 +2089,18 @@ static bool wssl_data_pending(struct Curl_cfilter *cf, return FALSE; } +void Curl_wssl_report_handshake(struct Curl_easy *data, + struct wssl_ctx *wssl) +{ +#if (LIBWOLFSSL_VERSION_HEX >= 0x03009010) + infof(data, "SSL connection using %s / %s", + wolfSSL_get_version(wssl->ssl), + wolfSSL_get_cipher_name(wssl->ssl)); +#else + infof(data, "SSL connected"); +#endif +} + static CURLcode wssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool *done) @@ -2168,16 +2174,9 @@ static CURLcode wssl_connect(struct Curl_cfilter *cf, } #endif /* HAVE_ALPN */ -#if (LIBWOLFSSL_VERSION_HEX >= 0x03009010) - infof(data, "SSL connection using %s / %s", - wolfSSL_get_version(wssl->ssl), - wolfSSL_get_cipher_name(wssl->ssl)); -#else - infof(data, "SSL connected"); -#endif - connssl->connecting_state = ssl_connect_done; connssl->state = ssl_connection_complete; + Curl_wssl_report_handshake(data, wssl); #ifdef WOLFSSL_EARLY_DATA if(connssl->earlydata_state > ssl_earlydata_none) { @@ -2286,7 +2285,6 @@ const struct Curl_ssl Curl_ssl_wolfssl = { NULL, /* set_engine */ NULL, /* set_engine_default */ NULL, /* engines_list */ - NULL, /* false_start */ wssl_sha256sum, /* sha256sum */ wssl_recv, /* recv decrypted data */ wssl_send, /* send data to encrypt */ diff --git a/lib/vtls/wolfssl.h b/lib/vtls/wolfssl.h index 0ddbee9ed9..19ca609c84 100644 --- a/lib/vtls/wolfssl.h +++ b/lib/vtls/wolfssl.h @@ -88,6 +88,8 @@ CURLcode Curl_wssl_verify_pinned(struct Curl_cfilter *cf, struct Curl_easy *data, struct wssl_ctx *wssl); +void Curl_wssl_report_handshake(struct Curl_easy *data, + struct wssl_ctx *wssl); #endif /* USE_WOLFSSL */ #endif /* HEADER_CURL_WOLFSSL_H */ diff --git a/lib/vtls/x509asn1.c b/lib/vtls/x509asn1.c index c6246cbd09..fe47e81a87 100644 --- a/lib/vtls/x509asn1.c +++ b/lib/vtls/x509asn1.c @@ -24,23 +24,21 @@ #include "../curl_setup.h" -#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ - defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ +#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ defined(USE_MBEDTLS) || defined(USE_RUSTLS) -#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ - defined(USE_MBEDTLS) || defined(USE_WOLFSSL) || defined(USE_RUSTLS) +#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_MBEDTLS) || \ + defined(USE_WOLFSSL) || defined(USE_RUSTLS) #define WANT_PARSEX509 /* uses Curl_parseX509() */ #endif -#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ - defined(USE_MBEDTLS) || defined(USE_RUSTLS) +#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_MBEDTLS) || \ + defined(USE_RUSTLS) #define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */ #endif #include #include "../urldata.h" -#include "../strcase.h" #include "../curl_ctype.h" #include "hostcheck.h" #include "vtls.h" @@ -263,7 +261,7 @@ static const struct Curl_OID *searchOID(const char *oid) { const struct Curl_OID *op; for(op = OIDtable; op->numoid; op++) - if(!strcmp(op->numoid, oid) || strcasecompare(op->textoid, oid)) + if(!strcmp(op->numoid, oid) || curl_strequal(op->textoid, oid)) return op; return NULL; @@ -591,7 +589,7 @@ CURLcode Curl_x509_GTime2str(struct dynbuf *store, * Return error code. */ static CURLcode UTime2str(struct dynbuf *store, - const char *beg, const char *end) + const char *beg, const char *end) { const char *tzp; size_t tzl; @@ -861,7 +859,7 @@ int Curl_parseX509(struct Curl_X509certificate *cert, if(!getASN1Element(&cert->subjectPublicKey, ccp, cert->subjectPublicKeyInfo.end)) return -1; - /* Get optional issuerUiqueID, subjectUniqueID and extensions. */ + /* Get optional issuerUniqueID, subjectUniqueID and extensions. */ cert->issuerUniqueID.tag = cert->subjectUniqueID.tag = 0; cert->extensions.tag = elem.tag = 0; cert->issuerUniqueID.header = cert->subjectUniqueID.header = NULL; @@ -990,7 +988,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, /* Generate all information records for the public key. */ - if(strcasecompare(algo, "ecPublicKey")) { + if(curl_strequal(algo, "ecPublicKey")) { /* * ECC public key is all the data, a value of type BIT STRING mapped to * OCTET STRING and should not be parsed as an ASN.1 value. @@ -1012,7 +1010,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, if(!getASN1Element(&pk, pubkey->beg + 1, pubkey->end)) return 1; - if(strcasecompare(algo, "rsaEncryption")) { + if(curl_strequal(algo, "rsaEncryption")) { const char *q; size_t len; @@ -1047,7 +1045,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, if(do_pubkey_field(data, certnum, "rsa(e)", &elem)) return 1; } - else if(strcasecompare(algo, "dsa")) { + else if(curl_strequal(algo, "dsa")) { p = getASN1Element(&elem, param->beg, param->end); if(p) { if(do_pubkey_field(data, certnum, "dsa(p)", &elem)) @@ -1065,7 +1063,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum, } } } - else if(strcasecompare(algo, "dhpublicnumber")) { + else if(curl_strequal(algo, "dhpublicnumber")) { p = getASN1Element(&elem, param->beg, param->end); if(p) { if(do_pubkey_field(data, certnum, "dh(p)", &elem)) @@ -1277,5 +1275,5 @@ done: #endif /* WANT_EXTRACT_CERTINFO */ -#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP - or USE_MBEDTLS or USE_RUSTLS */ +#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_MBEDTLS or + USE_RUSTLS */ diff --git a/lib/vtls/x509asn1.h b/lib/vtls/x509asn1.h index 1c9c35c3d4..f9bd455b70 100644 --- a/lib/vtls/x509asn1.h +++ b/lib/vtls/x509asn1.h @@ -27,8 +27,7 @@ #include "../curl_setup.h" -#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \ - defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ +#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ defined(USE_MBEDTLS) || defined(USE_RUSTLS) #include "../cfilters.h" @@ -79,8 +78,8 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data, const char *beg, const char *end); #ifdef UNITTESTS -#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \ - defined(USE_MBEDTLS) || defined(USE_RUSTLS) +#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_MBEDTLS) || \ + defined(USE_RUSTLS) /* used by unit1656.c */ CURLcode Curl_x509_GTime2str(struct dynbuf *store, @@ -91,6 +90,6 @@ CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem, #endif #endif -#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP - or USE_MBEDTLS or USE_RUSTLS */ +#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_MBEDTLS or + USE_RUSTLS */ #endif /* HEADER_CURL_X509ASN1_H */ diff --git a/lib/ws.c b/lib/ws.c index fc02025f1d..7f8a688ca1 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -543,7 +543,7 @@ static CURLcode ws_dec_pass(struct ws_decoder *dec, ws_dec_info(dec, data, "passing"); if(result) return result; - /* paylod parsing done */ + /* payload parsing done */ dec->state = WS_DEC_INIT; break; default: @@ -653,11 +653,11 @@ static CURLcode ws_cw_write(struct Curl_easy *data, } if(nbytes) { - ssize_t nwritten; - nwritten = Curl_bufq_write(&ctx->buf, (const unsigned char *)buf, - nbytes, &result); - if(nwritten < 0) { - infof(data, "[WS] error adding data to buffer %d", result); + size_t nwritten; + result = Curl_bufq_write(&ctx->buf, (const unsigned char *)buf, + nbytes, &nwritten); + if(result) { + infof(data, "WS: error adding data to buffer %d", result); return result; } } @@ -756,8 +756,7 @@ static ssize_t ws_enc_write_head(struct Curl_easy *data, { unsigned char firstbyte = 0; unsigned char head[14]; - size_t hlen; - ssize_t n; + size_t hlen, n; if(payload_len < 0) { failf(data, "[WS] starting new frame with negative payload length %" @@ -837,16 +836,16 @@ static ssize_t ws_enc_write_head(struct Curl_easy *data, /* reset for payload to come */ enc->xori = 0; - n = Curl_bufq_write(out, head, hlen, err); - if(n < 0) + *err = Curl_bufq_write(out, head, hlen, &n); + if(*err) return -1; - if((size_t)n != hlen) { + if(n != hlen) { /* We use a bufq with SOFT_LIMIT, writing should always succeed */ DEBUGASSERT(0); *err = CURLE_SEND_ERROR; return -1; } - return n; + return (ssize_t)n; } static ssize_t ws_enc_write_payload(struct ws_encoder *enc, @@ -854,8 +853,7 @@ static ssize_t ws_enc_write_payload(struct ws_encoder *enc, const unsigned char *buf, size_t buflen, struct bufq *out, CURLcode *err) { - ssize_t n; - size_t i, len; + size_t i, len, n; if(Curl_bufq_is_full(out)) { *err = CURLE_AGAIN; @@ -869,8 +867,8 @@ static ssize_t ws_enc_write_payload(struct ws_encoder *enc, for(i = 0; i < len; ++i) { unsigned char c = buf[i] ^ enc->mask[enc->xori]; - n = Curl_bufq_write(out, &c, 1, err); - if(n < 0) { + *err = Curl_bufq_write(out, &c, 1, &n); + if(*err) { if((*err != CURLE_AGAIN) || !i) return -1; break; @@ -1054,15 +1052,16 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, } if(data->set.connect_only) { - ssize_t nwritten; + size_t nwritten; /* In CONNECT_ONLY setup, the payloads from `mem` need to be received * when using `curl_ws_recv` later on after this transfer is already * marked as DONE. */ - nwritten = Curl_bufq_write(&ws->recvbuf, (const unsigned char *)mem, - nread, &result); - if(nwritten < 0) + result = Curl_bufq_write(&ws->recvbuf, (const unsigned char *)mem, + nread, &nwritten); + if(result) return result; - CURL_TRC_WS(data, "%zu bytes payload", nread); + DEBUGASSERT(nread == nwritten); + infof(data, "%zu bytes websocket payload", nread); } else { /* !connect_only */ /* And pass any additional data to the writers */ @@ -1137,22 +1136,17 @@ static ssize_t ws_client_collect(const unsigned char *buf, size_t buflen, return nwritten; } -static ssize_t nw_in_recv(void *reader_ctx, - unsigned char *buf, size_t buflen, - CURLcode *err) +static CURLcode nw_in_recv(void *reader_ctx, + unsigned char *buf, size_t buflen, + size_t *pnread) { struct Curl_easy *data = reader_ctx; - size_t nread; - - *err = curl_easy_recv(data, buf, buflen, &nread); - if(*err) - return -1; - return (ssize_t)nread; + return curl_easy_recv(data, buf, buflen, pnread); } -CURL_EXTERN CURLcode curl_ws_recv(CURL *d, void *buffer, - size_t buflen, size_t *nread, - const struct curl_ws_frame **metap) +CURLcode curl_ws_recv(CURL *d, void *buffer, + size_t buflen, size_t *nread, + const struct curl_ws_frame **metap) { struct Curl_easy *data = d; struct connectdata *conn = data->conn; @@ -1192,10 +1186,10 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *d, void *buffer, /* receive more when our buffer is empty */ if(Curl_bufq_is_empty(&ws->recvbuf)) { - ssize_t n = Curl_bufq_slurp(&ws->recvbuf, nw_in_recv, data, &result); - if(n < 0) { + size_t n; + result = Curl_bufq_slurp(&ws->recvbuf, nw_in_recv, data, &n); + if(result) return result; - } else if(n == 0) { /* connection closed */ infof(data, "[WS] connection expectedly closed?"); @@ -1373,10 +1367,10 @@ static CURLcode ws_send_raw(struct Curl_easy *data, const void *buffer, return result; } -CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer_arg, - size_t buflen, size_t *sent, - curl_off_t fragsize, - unsigned int flags) +CURLcode curl_ws_send(CURL *d, const void *buffer_arg, + size_t buflen, size_t *sent, + curl_off_t fragsize, + unsigned int flags) { struct websocket *ws; const unsigned char *buffer = buffer_arg; @@ -1527,7 +1521,7 @@ static CURLcode ws_setup_conn(struct Curl_easy *data, } -CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *d) +const struct curl_ws_frame *curl_ws_meta(CURL *d) { /* we only return something for websocket, called from within the callback when not using raw mode */ @@ -1600,9 +1594,9 @@ const struct Curl_handler Curl_handler_wss = { #else -CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, - size_t *nread, - const struct curl_ws_frame **metap) +CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, + size_t *nread, + const struct curl_ws_frame **metap) { (void)curl; (void)buffer; @@ -1612,10 +1606,10 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, return CURLE_NOT_BUILT_IN; } -CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer, - size_t buflen, size_t *sent, - curl_off_t fragsize, - unsigned int flags) +CURLcode curl_ws_send(CURL *curl, const void *buffer, + size_t buflen, size_t *sent, + curl_off_t fragsize, + unsigned int flags) { (void)curl; (void)buffer; @@ -1626,7 +1620,7 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer, return CURLE_NOT_BUILT_IN; } -CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *data) +const struct curl_ws_frame *curl_ws_meta(CURL *data) { (void)data; return NULL;