Update networking layer w/ CURL and emscripten impl

This commit is contained in:
2025-11-08 01:50:36 +11:00
parent a17925904d
commit f6874dc55a
4105 changed files with 694617 additions and 179 deletions
+328
View File
@@ -0,0 +1,328 @@
#***************************************************************************
# _ _ ____ _
# Project ___| | | | _ \| |
# / __| | | | |_) | |
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
# SPDX-License-Identifier: curl
#
###########################################################################
set(LIBCURL_OUTPUT_NAME "${LIB_NAME}" CACHE STRING "Basename of the curl library")
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_DEBUG_MACROS} "BUILDING_LIBCURL")
configure_file("curl_config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h")
# 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")
list(APPEND HHEADERS "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h")
# The rest of the build
set_property(DIRECTORY APPEND PROPERTY INCLUDE_DIRECTORIES
"${PROJECT_BINARY_DIR}/lib" # for "curl_config.h"
)
if(CURL_BUILD_TESTING)
# 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 C_CLANG_TIDY "")
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 DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/unitprotos.h")
endif()
## Library definition
if(NOT DEFINED IMPORT_LIB_SUFFIX)
# Suffix implib name with "_imp" by default, to avoid conflicting with
# the generated static "libcurl.lib" (typically with MSVC).
if(WIN32 AND BUILD_SHARED_LIBS AND
CMAKE_IMPORT_LIBRARY_SUFFIX STREQUAL CMAKE_STATIC_LIBRARY_SUFFIX)
set(IMPORT_LIB_SUFFIX "_imp")
else()
set(IMPORT_LIB_SUFFIX "")
endif()
endif()
if(NOT DEFINED STATIC_LIB_SUFFIX)
set(STATIC_LIB_SUFFIX "")
endif()
# Detect implib static lib filename collision
if(WIN32 AND BUILD_STATIC_LIBS AND BUILD_SHARED_LIBS AND
"${IMPORT_LIB_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}" STREQUAL
"${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}")
message(FATAL_ERROR "Library suffix is the same ('${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}') "
"for the import and static '${LIBCURL_OUTPUT_NAME}' library. "
"Set IMPORT_LIB_SUFFIX and/or STATIC_LIB_SUFFIX to different values, "
"or disable building either the shared or static library to avoid the filename collision.")
endif()
# Whether to do a single compilation pass for libcurl sources and reuse these
# objects to generate both static and shared target.
if(NOT DEFINED SHARE_LIB_OBJECT)
# Enable it by default on platforms where PIC is the default for both shared
# and static and there is a way to tell the linker which libcurl symbols it
# should export (vs. marking these symbols exportable at compile-time).
if(WIN32)
set(SHARE_LIB_OBJECT ON)
else()
# On other platforms, make it an option disabled by default
set(SHARE_LIB_OBJECT OFF)
endif()
endif()
if(SHARE_LIB_OBJECT AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
set(LIB_OBJECT "libcurl_object")
add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES}) # Requires CMake 3.12
if(WIN32)
# Define CURL_STATICLIB always, to disable __declspec(dllexport) for
# exported libcurl symbols. We handle exports via libcurl.def instead.
# Except with symbol hiding disabled or debug mode enabled, when we export
# _all_ symbols from libcurl DLL, without using libcurl.def.
set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB")
endif()
target_link_libraries(${LIB_OBJECT} PRIVATE ${CURL_LIBS})
set_target_properties(${LIB_OBJECT} PROPERTIES
POSITION_INDEPENDENT_CODE ON)
if(CURL_HIDES_PRIVATE_SYMBOLS)
set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}")
set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
endif()
if(CURL_HAS_LTO)
if(CMAKE_CONFIGURATION_TYPES)
set_target_properties(${LIB_OBJECT} PROPERTIES
INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
else()
set_target_properties(${LIB_OBJECT} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
endif()
if(CURL_CODE_COVERAGE)
set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS})
set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS})
endif()
target_include_directories(${LIB_OBJECT} INTERFACE
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>")
set(LIB_SOURCE $<TARGET_OBJECTS:${LIB_OBJECT}>)
else()
set(LIB_SOURCE ${HHEADERS} ${CSOURCES})
endif()
# We want it to be called libcurl on all platforms
if(BUILD_STATIC_LIBS)
list(APPEND libcurl_export ${LIB_STATIC})
add_library(${LIB_STATIC} STATIC ${LIB_SOURCE})
add_library(${PROJECT_NAME}::${LIB_STATIC} ALIAS ${LIB_STATIC})
if(WIN32)
set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB")
endif()
target_link_libraries(${LIB_STATIC} PRIVATE ${CURL_LIBS})
# Remove the "lib" prefix since the library is already named "libcurl".
set_target_properties(${LIB_STATIC} PROPERTIES
PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}"
SUFFIX "${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}"
INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB"
INTERFACE_LINK_DIRECTORIES "${CURL_LIBDIRS}")
if(CURL_HIDES_PRIVATE_SYMBOLS)
set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}")
set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
endif()
if(CURL_HAS_LTO)
if(CMAKE_CONFIGURATION_TYPES)
set_target_properties(${LIB_STATIC} PROPERTIES
INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
else()
set_target_properties(${LIB_STATIC} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
endif()
if(CURL_CODE_COVERAGE)
set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS})
set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS})
endif()
target_include_directories(${LIB_STATIC} INTERFACE
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>")
endif()
if(BUILD_SHARED_LIBS)
list(APPEND libcurl_export ${LIB_SHARED})
add_library(${LIB_SHARED} SHARED ${LIB_SOURCE})
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 ${LIB_RCFILES})
if(CURL_HIDES_PRIVATE_SYMBOLS)
set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "${PROJECT_SOURCE_DIR}/lib/libcurl.def")
endif()
endif()
target_link_libraries(${LIB_SHARED} PRIVATE ${CURL_LIBS})
# Remove the "lib" prefix since the library is already named "libcurl".
set_target_properties(${LIB_SHARED} PROPERTIES
PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}"
IMPORT_PREFIX "" IMPORT_SUFFIX "${IMPORT_LIB_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}"
POSITION_INDEPENDENT_CODE ON)
if(CURL_HIDES_PRIVATE_SYMBOLS)
set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS "${CURL_CFLAG_SYMBOLS_HIDE}")
set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
endif()
if(CURL_HAS_LTO)
if(CMAKE_CONFIGURATION_TYPES)
set_target_properties(${LIB_SHARED} PROPERTIES
INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
else()
set_target_properties(${LIB_SHARED} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
endif()
endif()
if(CURL_CODE_COVERAGE)
set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS ${CURL_COVERAGE_MACROS})
set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_OPTIONS ${CURL_COVERAGE_CFLAGS})
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS ${CURL_COVERAGE_LDFLAGS})
else()
set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_FLAGS ${CURL_COVERAGE_LDFLAGS})
endif()
endif()
target_include_directories(${LIB_SHARED} INTERFACE
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>")
if(CMAKE_DLL_NAME_WITH_SOVERSION OR
CYGWIN OR
APPLE OR
CMAKE_SYSTEM_NAME STREQUAL "AIX" OR
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
# on those ancient systems.
CMAKE_SYSTEM_NAME STREQUAL "FreeBSD")
set(_soversion_default TRUE)
else()
set(_soversion_default FALSE)
endif()
option(CURL_LIBCURL_SOVERSION "Enable libcurl SOVERSION" ${_soversion_default})
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
curl_transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")
include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")
math(EXPR _cmakesoname "${VERSIONCHANGE} - ${VERSIONDEL}")
set(_cmakeversion "${_cmakesoname}.${VERSIONDEL}.${VERSIONADD}")
endif()
if(CURL_LIBCURL_SOVERSION)
set_target_properties(${LIB_SHARED} PROPERTIES
VERSION "${_cmakeversion}" SOVERSION "${_cmakesoname}")
endif()
## Versioned symbols
if(CURL_LIBCURL_VERSIONED_SYMBOLS)
if(NOT DEFINED CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX)
# Default to prefixes used by autotools
if(CURL_WITH_MULTI_SSL)
set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "MULTISSL_")
elseif(CURL_USE_OPENSSL)
set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "OPENSSL_")
elseif(CURL_USE_MBEDTLS)
set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "MBEDTLS_")
elseif(CURL_USE_WOLFSSL)
set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "WOLFSSL_")
elseif(CURL_USE_GNUTLS)
set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "GNUTLS_")
elseif(CURL_USE_RUSTLS)
set(CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX "RUSTLS_")
endif()
endif()
# Generate version script for the linker, for versioned symbols.
# Consumed variables:
# CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX
# CURL_LIBCURL_VERSIONED_SYMBOLS_SONAME
set(CURL_LIBCURL_VERSIONED_SYMBOLS_SONAME ${_cmakesoname})
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/libcurl.vers.in"
"${CMAKE_CURRENT_BINARY_DIR}/libcurl.vers" @ONLY)
include(CMakePushCheckState)
include(CheckCSourceCompiles)
cmake_push_check_state()
set(CMAKE_REQUIRED_LINK_OPTIONS "-Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/libcurl.vers")
check_c_source_compiles("int main(void) { return 0; }" HAVE_VERSIONED_SYMBOLS)
if(HAVE_VERSIONED_SYMBOLS)
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_OPTIONS "${CMAKE_REQUIRED_LINK_OPTIONS}")
else()
set_property(TARGET ${LIB_SHARED} APPEND PROPERTY LINK_FLAGS "${CMAKE_REQUIRED_LINK_OPTIONS}")
endif()
else()
message(WARNING "Versioned symbols requested, but not supported by the toolchain.")
endif()
cmake_pop_check_state()
endif()
endif()
add_library(${LIB_NAME} ALIAS ${LIB_SELECTED})
add_library(${PROJECT_NAME}::${LIB_NAME} ALIAS ${LIB_SELECTED})
if(CURL_ENABLE_EXPORT_TARGET)
if(BUILD_STATIC_LIBS)
install(TARGETS ${LIB_STATIC}
EXPORT ${TARGETS_EXPORT_NAME}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
endif()
if(BUILD_SHARED_LIBS)
install(TARGETS ${LIB_SHARED}
EXPORT ${TARGETS_EXPORT_NAME}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
endif()
export(TARGETS ${libcurl_export}
FILE "${PROJECT_BINARY_DIR}/libcurl-target.cmake"
NAMESPACE ${PROJECT_NAME}::
)
endif()
+191
View File
@@ -0,0 +1,191 @@
#***************************************************************************
# _ _ ____ _
# Project ___| | | | _ \| |
# / __| | | | |_) | |
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
# SPDX-License-Identifier: curl
#
###########################################################################
AUTOMAKE_OPTIONS = foreign nostdinc
# Get CSOURCES, HHEADERS, LIB_RCFILES variables
include Makefile.inc
CMAKE_DIST = CMakeLists.txt curl_config.h.cmake
EXTRA_DIST = config-mac.h config-os400.h config-plan9.h config-riscos.h \
config-win32.h curl_config.h.in $(LIB_RCFILES) libcurl.def \
$(CMAKE_DIST) Makefile.soname optiontable.pl
lib_LTLIBRARIES = libcurl.la
if BUILD_UNITTESTS
noinst_LTLIBRARIES = libcurlu.la
# generate a file with "private" prototypes for unit testing
UNITPROTOS = unitprotos.h
else
noinst_LTLIBRARIES =
endif
# This might hold -Werror
CFLAGS += @CURL_CFLAG_EXTRAS@
# Specify our include paths here, and do it relative to $(top_srcdir) and
# $(top_builddir), to ensure that these paths which belong to the library
# being currently built and tested are searched before the library which
# might possibly already be installed in the system.
#
# $(top_srcdir)/include is for libcurl's external include files
# $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file
# $(srcdir) for the generated unity source to find included sources
AM_CPPFLAGS = -I$(top_srcdir)/include \
-I$(top_builddir)/lib \
-I$(srcdir)
# Prevent LIBS from being used for all link targets
LIBS = $(BLANK_AT_MAKETIME)
AM_LDFLAGS =
AM_CFLAGS =
if DEBUGBUILD
AM_CPPFLAGS += -DDEBUGBUILD
endif
if CURLDEBUG
AM_CPPFLAGS += -DCURLDEBUG
endif
AM_CPPFLAGS += -DBUILDING_LIBCURL
if DOING_NATIVE_WINDOWS
CSOURCES += dllmain.c
endif
if USE_UNITY
libcurl_unity.c: $(top_srcdir)/scripts/mk-unity.pl $(CSOURCES)
@PERL@ $(top_srcdir)/scripts/mk-unity.pl --include $(CSOURCES) > libcurl_unity.c
nodist_libcurl_la_SOURCES = libcurl_unity.c
libcurl_la_SOURCES =
nodist_libcurlu_la_SOURCES = libcurl_unity.c
libcurlu_la_SOURCES =
CLEANFILES = libcurl_unity.c
else
libcurl_la_SOURCES = $(CSOURCES) $(HHEADERS)
libcurlu_la_SOURCES = $(CSOURCES) $(HHEADERS)
CLEANFILES =
endif
CLEANFILES += $(UNITPROTOS)
libcurl_la_CPPFLAGS_EXTRA =
libcurl_la_LDFLAGS_EXTRA =
libcurl_la_CFLAGS_EXTRA =
if CURL_LT_SHLIB_USE_VERSION_INFO
# Get VERSIONCHANGE, VERSIONADD, VERSIONDEL, VERSIONINFO variables
include Makefile.soname
libcurl_la_LDFLAGS_EXTRA += $(VERSIONINFO)
endif
if CURL_LT_SHLIB_USE_NO_UNDEFINED
libcurl_la_LDFLAGS_EXTRA += -no-undefined
endif
if CURL_LT_SHLIB_USE_MIMPURE_TEXT
libcurl_la_LDFLAGS_EXTRA += -mimpure-text
endif
if CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS
libcurl_la_LDFLAGS_EXTRA += -Wl,--version-script=libcurl.vers
else
# if symbol-hiding is enabled, hide them!
if DOING_CURL_SYMBOL_HIDING
libcurl_la_LDFLAGS_EXTRA += -export-symbols-regex '^curl_.*'
endif
endif
if USE_CPPFLAG_CURL_STATICLIB
libcurl_la_CPPFLAGS_EXTRA += -DCURL_STATICLIB
else
if HAVE_WINDRES
libcurl_la_SOURCES += $(LIB_RCFILES)
$(LIB_RCFILES): $(top_srcdir)/include/curl/curlver.h
endif
endif
if DOING_CURL_SYMBOL_HIDING
libcurl_la_CPPFLAGS_EXTRA += -DCURL_HIDDEN_SYMBOLS
libcurl_la_CFLAGS_EXTRA += $(CFLAG_CURL_SYMBOL_HIDING)
endif
libcurl_la_CPPFLAGS = $(AM_CPPFLAGS) $(libcurl_la_CPPFLAGS_EXTRA)
libcurl_la_LDFLAGS = $(AM_LDFLAGS) $(libcurl_la_LDFLAGS_EXTRA) $(CURL_LDFLAGS_LIB) $(LIBCURL_PC_LIBS_PRIVATE)
libcurl_la_CFLAGS = $(AM_CFLAGS) $(libcurl_la_CFLAGS_EXTRA)
libcurlu_la_CPPFLAGS = $(AM_CPPFLAGS) -DCURL_STATICLIB -DUNITTESTS
libcurlu_la_LDFLAGS = $(AM_LDFLAGS) -static $(LIBCURL_PC_LIBS_PRIVATE)
libcurlu_la_CFLAGS = $(AM_CFLAGS)
CHECKSRC = $(CS_$(V))
CS_0 = @echo " RUN " $@;
CS_1 =
CS_ = $(CS_0)
checksrc:
$(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \
$(CSOURCES) $(HHEADERS))
if NOT_CURL_CI
if DEBUGBUILD
# for debug builds, we scan the sources on all regular make invokes
CHECKSOURCES = checksrc
endif
endif
all-local: $(CHECKSOURCES)
UNIT_V = $(UNITV_$(V))
UNITV_0 = @echo " UNITPR " $@;
UNITV_1 =
UNITV_ = $(UNITV_0)
# UNITPROTOS depends on every C file in the lib/ dir
$(UNITPROTOS): $(CSOURCES)
$(UNIT_V)(cd $(srcdir) && @PERL@ ../scripts/extract-unit-protos $(CSOURCES) > $(top_builddir)/lib/$(UNITPROTOS))
# disable the tests that are mostly causing false positives
TIDYFLAGS := -checks=-clang-analyzer-security.insecureAPI.bzero,-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-security.ArrayBound,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling -quiet
if CURL_WERROR
TIDYFLAGS += --warnings-as-errors=*
endif
TIDY := clang-tidy
tidy:
(_csources=`echo ' $(CSOURCES)' | sed -E -e 's/ +$$//' -e 's/ +/ /g' -e 's| | $(srcdir)/|g'`; \
$(TIDY) $$_csources $(TIDYFLAGS) $(CURL_CLANG_TIDYFLAGS) -- $(AM_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H)
optiontable:
@PERL@ $(srcdir)/optiontable.pl < $(top_srcdir)/include/curl/curl.h > $(srcdir)/easyoptions.c
if HAVE_WINDRES
.rc.lo:
$(LIBTOOL) --tag=RC --mode=compile $(RC) -I$(top_srcdir)/include $(RCFLAGS) -i $< -o $@
endif
File diff suppressed because it is too large Load Diff
+409
View File
@@ -0,0 +1,409 @@
#***************************************************************************
# _ _ ____ _
# Project ___| | | | _ \| |
# / __| | | | |_) | |
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
# SPDX-License-Identifier: curl
#
###########################################################################
# Shared between CMakeLists.txt and Makefile.am
LIB_CURLX_CFILES = \
curlx/base64.c \
curlx/dynbuf.c \
curlx/fopen.c \
curlx/inet_ntop.c \
curlx/inet_pton.c \
curlx/multibyte.c \
curlx/nonblock.c \
curlx/strerr.c \
curlx/strparse.c \
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/fopen.h \
curlx/inet_ntop.h \
curlx/inet_pton.h \
curlx/multibyte.h \
curlx/nonblock.h \
curlx/strerr.h \
curlx/strparse.h \
curlx/timediff.h \
curlx/timeval.h \
curlx/version_win32.h \
curlx/wait.h \
curlx/warnless.h \
curlx/winapi.h
LIB_VAUTH_CFILES = \
vauth/cleartext.c \
vauth/cram.c \
vauth/digest.c \
vauth/digest_sspi.c \
vauth/gsasl.c \
vauth/krb5_gssapi.c \
vauth/krb5_sspi.c \
vauth/ntlm.c \
vauth/ntlm_sspi.c \
vauth/oauth2.c \
vauth/spnego_gssapi.c \
vauth/spnego_sspi.c \
vauth/vauth.c
LIB_VAUTH_HFILES = \
vauth/digest.h \
vauth/vauth.h
LIB_VTLS_CFILES = \
vtls/apple.c \
vtls/cipher_suite.c \
vtls/gtls.c \
vtls/hostcheck.c \
vtls/keylog.c \
vtls/mbedtls.c \
vtls/mbedtls_threadlock.c \
vtls/openssl.c \
vtls/rustls.c \
vtls/schannel.c \
vtls/schannel_verify.c \
vtls/vtls.c \
vtls/vtls_scache.c \
vtls/vtls_spack.c \
vtls/wolfssl.c \
vtls/x509asn1.c
LIB_VTLS_HFILES = \
vtls/apple.h \
vtls/cipher_suite.h \
vtls/gtls.h \
vtls/hostcheck.h \
vtls/keylog.h \
vtls/mbedtls.h \
vtls/mbedtls_threadlock.h \
vtls/openssl.h \
vtls/rustls.h \
vtls/schannel.h \
vtls/schannel_int.h \
vtls/vtls.h \
vtls/vtls_int.h \
vtls/vtls_scache.h \
vtls/vtls_spack.h \
vtls/wolfssl.h \
vtls/x509asn1.h
LIB_VQUIC_CFILES = \
vquic/curl_ngtcp2.c \
vquic/curl_osslq.c \
vquic/curl_quiche.c \
vquic/vquic.c \
vquic/vquic-tls.c
LIB_VQUIC_HFILES = \
vquic/curl_ngtcp2.h \
vquic/curl_osslq.h \
vquic/curl_quiche.h \
vquic/vquic.h \
vquic/vquic_int.h \
vquic/vquic-tls.h
LIB_VSSH_CFILES = \
vssh/libssh.c \
vssh/libssh2.c \
vssh/curl_path.c
LIB_VSSH_HFILES = \
vssh/curl_path.h \
vssh/ssh.h
LIB_CFILES = \
altsvc.c \
amigaos.c \
asyn-ares.c \
asyn-base.c \
asyn-thrdd.c \
bufq.c \
bufref.c \
cf-h1-proxy.c \
cf-h2-proxy.c \
cf-haproxy.c \
cf-https-connect.c \
cf-ip-happy.c \
cf-socket.c \
cfilters.c \
conncache.c \
connect.c \
content_encoding.c \
cookie.c \
cshutdn.c \
curl_addrinfo.c \
curl_endian.c \
curl_fnmatch.c \
curl_fopen.c \
curl_get_line.c \
curl_gethostname.c \
curl_gssapi.c \
curl_memrchr.c \
curl_ntlm_core.c \
curl_range.c \
curl_rtmp.c \
curl_sasl.c \
curl_sha512_256.c \
curl_sspi.c \
curl_threads.c \
curl_trc.c \
cw-out.c \
cw-pause.c \
dict.c \
doh.c \
dynhds.c \
easy.c \
easygetopt.c \
easyoptions.c \
escape.c \
fake_addrinfo.c \
file.c \
fileinfo.c \
formdata.c \
ftp.c \
ftplistparser.c \
getenv.c \
getinfo.c \
gopher.c \
hash.c \
headers.c \
hmac.c \
hostip.c \
hostip4.c \
hostip6.c \
hsts.c \
http.c \
http1.c \
http2.c \
http_aws_sigv4.c \
http_chunks.c \
http_digest.c \
http_negotiate.c \
http_ntlm.c \
http_proxy.c \
httpsrr.c \
idn.c \
if2ip.c \
imap.c \
ldap.c \
llist.c \
macos.c \
md4.c \
md5.c \
memdebug.c \
mime.c \
mprintf.c \
mqtt.c \
multi.c \
multi_ev.c \
multi_ntfy.c \
netrc.c \
noproxy.c \
openldap.c \
parsedate.c \
pingpong.c \
pop3.c \
progress.c \
psl.c \
rand.c \
rename.c \
request.c \
rtsp.c \
select.c \
sendf.c \
setopt.c \
sha256.c \
share.c \
slist.c \
smb.c \
smtp.c \
socketpair.c \
socks.c \
socks_gssapi.c \
socks_sspi.c \
speedcheck.c \
splay.c \
strcase.c \
strdup.c \
strequal.c \
strerror.c \
system_win32.c \
telnet.c \
tftp.c \
transfer.c \
uint-bset.c \
uint-hash.c \
uint-spbset.c \
uint-table.c \
url.c \
urlapi.c \
version.c \
ws.c
LIB_HFILES = \
altsvc.h \
amigaos.h \
arpa_telnet.h \
asyn.h \
bufq.h \
bufref.h \
cf-h1-proxy.h \
cf-h2-proxy.h \
cf-haproxy.h \
cf-https-connect.h \
cf-ip-happy.h \
cf-socket.h \
cfilters.h \
conncache.h \
cshutdn.h \
connect.h \
content_encoding.h \
cookie.h \
curl_addrinfo.h \
curl_ctype.h \
curl_endian.h \
curl_fnmatch.h \
curl_fopen.h \
curl_get_line.h \
curl_gethostname.h \
curl_gssapi.h \
curl_hmac.h \
curl_ldap.h \
curl_md4.h \
curl_md5.h \
curl_mem_undef.h \
curl_memory.h \
curl_memrchr.h \
curl_ntlm_core.h \
curl_printf.h \
curl_range.h \
curl_rtmp.h \
curl_sasl.h \
curl_setup.h \
curl_setup_once.h \
curl_sha256.h \
curl_sha512_256.h \
curl_sspi.h \
curl_threads.h \
curl_trc.h \
cw-out.h \
cw-pause.h \
dict.h \
doh.h \
dynhds.h \
easy_lock.h \
easyif.h \
easyoptions.h \
escape.h \
fake_addrinfo.h \
file.h \
fileinfo.h \
formdata.h \
ftp.h \
ftplistparser.h \
functypes.h \
getinfo.h \
gopher.h \
hash.h \
headers.h \
hostip.h \
hsts.h \
http.h \
http1.h \
http2.h \
http_aws_sigv4.h \
http_chunks.h \
http_digest.h \
http_negotiate.h \
http_ntlm.h \
http_proxy.h \
httpsrr.h \
idn.h \
if2ip.h \
imap.h \
llist.h \
macos.h \
memdebug.h \
mime.h \
mqtt.h \
multihandle.h \
multi_ev.h \
multi_ntfy.h \
multiif.h \
netrc.h \
noproxy.h \
parsedate.h \
pingpong.h \
pop3.h \
progress.h \
psl.h \
rand.h \
rename.h \
request.h \
rtsp.h \
select.h \
sendf.h \
setopt.h \
setup-os400.h \
setup-vms.h \
setup-win32.h \
share.h \
sigpipe.h \
slist.h \
smb.h \
smtp.h \
sockaddr.h \
socketpair.h \
socks.h \
speedcheck.h \
splay.h \
strcase.h \
strdup.h \
strerror.h \
system_win32.h \
telnet.h \
tftp.h \
transfer.h \
uint-bset.h \
uint-hash.h \
uint-spbset.h \
uint-table.h \
url.h \
urlapi-int.h \
urldata.h \
ws.h
LIB_RCFILES = libcurl.rc
CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES) \
$(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES) $(LIB_CURLX_CFILES)
HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VTLS_HFILES) \
$(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES) $(LIB_CURLX_HFILES)
+44
View File
@@ -0,0 +1,44 @@
#***************************************************************************
# _ _ ____ _
# Project ___| | | | _ \| |
# / __| | | | |_) | |
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
# SPDX-License-Identifier: curl
#
###########################################################################
# Shared between CMakeLists.txt and Makefile.am
# Keep in sync with CURL_LIBCURL_VERSIONED_SYMBOLS_SONAME in configure.ac
VERSIONCHANGE=12
VERSIONADD=0
VERSIONDEL=8
# libtool version:
VERSIONINFO=-version-info $(VERSIONCHANGE):$(VERSIONADD):$(VERSIONDEL)
# This flag accepts an argument of the form current[:revision[:age]]. So,
# passing -version-info 3:12:1 sets current to 3, revision to 12, and age to
# 1.
#
# Here's the simplified rule guide on how to change -version-info:
# (current version is C:R:A)
#
# 1. if there are only source changes, use C:R+1:A
# 2. if interfaces were added use C+1:0:A+1
# 3. if interfaces were removed, then use C+1:0:0
#
# For the full guide on libcurl ABI rules, see docs/libcurl/ABI
+674
View File
@@ -0,0 +1,674 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
/*
* The Alt-Svc: header is defined in RFC 7838:
* https://datatracker.ietf.org/doc/html/rfc7838
*/
#include "curl_setup.h"
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_ALTSVC)
#include <curl/curl.h>
#include "urldata.h"
#include "altsvc.h"
#include "curl_fopen.h"
#include "curl_get_line.h"
#include "parsedate.h"
#include "sendf.h"
#include "curlx/warnless.h"
#include "rename.h"
#include "strdup.h"
#include "curlx/inet_pton.h"
#include "curlx/strparse.h"
#include "connect.h"
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
#define MAX_ALTSVC_LINE 4095
#define MAX_ALTSVC_DATELEN 256
#define MAX_ALTSVC_HOSTLEN 2048
#define MAX_ALTSVC_ALPNLEN 10
#define H3VERSION "h3"
/* Given the ALPN ID, return the name */
const char *Curl_alpnid2str(enum alpnid id)
{
switch(id) {
case ALPN_h1:
return "h1";
case ALPN_h2:
return "h2";
case ALPN_h3:
return H3VERSION;
default:
return ""; /* bad */
}
}
static void altsvc_free(struct altsvc *as)
{
free(as->src.host);
free(as->dst.host);
free(as);
}
static struct altsvc *altsvc_createid(const char *srchost,
size_t hlen,
const char *dsthost,
size_t dlen, /* dsthost length */
enum alpnid srcalpnid,
enum alpnid dstalpnid,
size_t srcport,
size_t dstport)
{
struct altsvc *as = calloc(1, sizeof(struct altsvc));
if(!as)
return NULL;
DEBUGASSERT(hlen);
DEBUGASSERT(dlen);
if(!hlen || !dlen)
/* bad input */
goto error;
if((hlen > 2) && srchost[0] == '[') {
/* IPv6 address, strip off brackets */
srchost++;
hlen -= 2;
}
else if(srchost[hlen - 1] == '.') {
/* strip off trailing dot */
hlen--;
if(!hlen)
goto error;
}
if((dlen > 2) && dsthost[0] == '[') {
/* IPv6 address, strip off brackets */
dsthost++;
dlen -= 2;
}
as->src.host = Curl_memdup0(srchost, hlen);
if(!as->src.host)
goto error;
as->dst.host = Curl_memdup0(dsthost, dlen);
if(!as->dst.host)
goto error;
as->src.alpnid = srcalpnid;
as->dst.alpnid = dstalpnid;
as->src.port = (unsigned short)srcport;
as->dst.port = (unsigned short)dstport;
return as;
error:
altsvc_free(as);
return NULL;
}
static struct altsvc *altsvc_create(struct Curl_str *srchost,
struct Curl_str *dsthost,
struct Curl_str *srcalpn,
struct Curl_str *dstalpn,
size_t srcport,
size_t dstport)
{
enum alpnid dstalpnid =
Curl_alpn2alpnid(curlx_str(dstalpn), curlx_strlen(dstalpn));
enum alpnid srcalpnid =
Curl_alpn2alpnid(curlx_str(srcalpn), curlx_strlen(srcalpn));
if(!srcalpnid || !dstalpnid)
return NULL;
return altsvc_createid(curlx_str(srchost), curlx_strlen(srchost),
curlx_str(dsthost), curlx_strlen(dsthost),
srcalpnid, dstalpnid,
srcport, dstport);
}
/* only returns SERIOUS errors */
static CURLcode altsvc_add(struct altsvcinfo *asi, const char *line)
{
/* Example line:
h2 example.com 443 h3 shiny.example.com 8443 "20191231 10:00:00" 1
*/
struct Curl_str srchost;
struct Curl_str dsthost;
struct Curl_str srcalpn;
struct Curl_str dstalpn;
struct Curl_str date;
curl_off_t srcport;
curl_off_t dstport;
curl_off_t persist;
curl_off_t prio;
if(curlx_str_word(&line, &srcalpn, MAX_ALTSVC_ALPNLEN) ||
curlx_str_singlespace(&line) ||
curlx_str_word(&line, &srchost, MAX_ALTSVC_HOSTLEN) ||
curlx_str_singlespace(&line) ||
curlx_str_number(&line, &srcport, 65535) ||
curlx_str_singlespace(&line) ||
curlx_str_word(&line, &dstalpn, MAX_ALTSVC_ALPNLEN) ||
curlx_str_singlespace(&line) ||
curlx_str_word(&line, &dsthost, MAX_ALTSVC_HOSTLEN) ||
curlx_str_singlespace(&line) ||
curlx_str_number(&line, &dstport, 65535) ||
curlx_str_singlespace(&line) ||
curlx_str_quotedword(&line, &date, MAX_ALTSVC_DATELEN) ||
curlx_str_singlespace(&line) ||
curlx_str_number(&line, &persist, 1) ||
curlx_str_singlespace(&line) ||
curlx_str_number(&line, &prio, 0) ||
curlx_str_newline(&line))
;
else {
struct altsvc *as;
char dbuf[MAX_ALTSVC_DATELEN + 1];
time_t expires = 0;
/* The date parser works on a null-terminated string. The maximum length
is upheld by curlx_str_quotedword(). */
memcpy(dbuf, curlx_str(&date), curlx_strlen(&date));
dbuf[curlx_strlen(&date)] = 0;
Curl_getdate_capped(dbuf, &expires);
as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn,
(size_t)srcport, (size_t)dstport);
if(as) {
as->expires = expires;
as->prio = 0; /* not supported to just set zero */
as->persist = persist ? 1 : 0;
Curl_llist_append(&asi->list, as, &as->node);
}
}
return CURLE_OK;
}
/*
* Load alt-svc entries from the given file. The text based line-oriented file
* format is documented here: https://curl.se/docs/alt-svc.html
*
* This function only returns error on major problems that prevent alt-svc
* handling to work completely. It will ignore individual syntactical errors
* etc.
*/
static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
{
CURLcode result = CURLE_OK;
FILE *fp;
/* we need a private copy of the filename so that the altsvc cache file
name survives an easy handle reset */
free(asi->filename);
asi->filename = strdup(file);
if(!asi->filename)
return CURLE_OUT_OF_MEMORY;
fp = curlx_fopen(file, FOPEN_READTEXT);
if(fp) {
bool eof = FALSE;
struct dynbuf buf;
curlx_dyn_init(&buf, MAX_ALTSVC_LINE);
do {
result = Curl_get_line(&buf, fp, &eof);
if(!result) {
const char *lineptr = curlx_dyn_ptr(&buf);
curlx_str_passblanks(&lineptr);
if(curlx_str_single(&lineptr, '#'))
altsvc_add(asi, lineptr);
}
} while(!result && !eof);
curlx_dyn_free(&buf); /* free the line buffer */
curlx_fclose(fp);
}
return result;
}
/*
* Write this single altsvc entry to a single output line
*/
static CURLcode altsvc_out(struct altsvc *as, FILE *fp)
{
struct tm stamp;
const char *dst6_pre = "";
const char *dst6_post = "";
const char *src6_pre = "";
const char *src6_post = "";
CURLcode result = Curl_gmtime(as->expires, &stamp);
if(result)
return result;
#ifdef USE_IPV6
else {
char ipv6_unused[16];
if(curlx_inet_pton(AF_INET6, as->dst.host, ipv6_unused) == 1) {
dst6_pre = "[";
dst6_post = "]";
}
if(curlx_inet_pton(AF_INET6, as->src.host, ipv6_unused) == 1) {
src6_pre = "[";
src6_post = "]";
}
}
#endif
curl_mfprintf(fp,
"%s %s%s%s %u "
"%s %s%s%s %u "
"\"%d%02d%02d "
"%02d:%02d:%02d\" "
"%u %u\n",
Curl_alpnid2str(as->src.alpnid),
src6_pre, as->src.host, src6_post,
as->src.port,
Curl_alpnid2str(as->dst.alpnid),
dst6_pre, as->dst.host, dst6_post,
as->dst.port,
stamp.tm_year + 1900, stamp.tm_mon + 1, stamp.tm_mday,
stamp.tm_hour, stamp.tm_min, stamp.tm_sec,
as->persist, as->prio);
return CURLE_OK;
}
/* ---- library-wide functions below ---- */
/*
* Curl_altsvc_init() creates a new altsvc cache.
* It returns the new instance or NULL if something goes wrong.
*/
struct altsvcinfo *Curl_altsvc_init(void)
{
struct altsvcinfo *asi = calloc(1, sizeof(struct altsvcinfo));
if(!asi)
return NULL;
Curl_llist_init(&asi->list, NULL);
/* set default behavior */
asi->flags = CURLALTSVC_H1
#ifdef USE_HTTP2
| CURLALTSVC_H2
#endif
#ifdef USE_HTTP3
| CURLALTSVC_H3
#endif
;
return asi;
}
/*
* Curl_altsvc_load() loads alt-svc from file.
*/
CURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file)
{
DEBUGASSERT(asi);
return altsvc_load(asi, file);
}
/*
* Curl_altsvc_ctrl() passes on the external bitmask.
*/
CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl)
{
DEBUGASSERT(asi);
asi->flags = ctrl;
return CURLE_OK;
}
/*
* Curl_altsvc_cleanup() frees an altsvc cache instance and all associated
* resources.
*/
void Curl_altsvc_cleanup(struct altsvcinfo **altsvcp)
{
if(*altsvcp) {
struct Curl_llist_node *e;
struct Curl_llist_node *n;
struct altsvcinfo *altsvc = *altsvcp;
for(e = Curl_llist_head(&altsvc->list); e; e = n) {
struct altsvc *as = Curl_node_elem(e);
n = Curl_node_next(e);
altsvc_free(as);
}
free(altsvc->filename);
free(altsvc);
*altsvcp = NULL; /* clear the pointer */
}
}
/*
* Curl_altsvc_save() writes the altsvc cache to a file.
*/
CURLcode Curl_altsvc_save(struct Curl_easy *data,
struct altsvcinfo *altsvc, const char *file)
{
CURLcode result = CURLE_OK;
FILE *out;
char *tempstore = NULL;
if(!altsvc)
/* no cache activated */
return CURLE_OK;
/* if not new name is given, use the one we stored from the load */
if(!file && altsvc->filename)
file = altsvc->filename;
if((altsvc->flags & CURLALTSVC_READONLYFILE) || !file || !file[0])
/* marked as read-only, no file or zero length filename */
return CURLE_OK;
result = Curl_fopen(data, file, &out, &tempstore);
if(!result) {
struct Curl_llist_node *e;
struct Curl_llist_node *n;
fputs("# Your alt-svc cache. https://curl.se/docs/alt-svc.html\n"
"# This file was generated by libcurl! Edit at your own risk.\n",
out);
for(e = Curl_llist_head(&altsvc->list); e; e = n) {
struct altsvc *as = Curl_node_elem(e);
n = Curl_node_next(e);
result = altsvc_out(as, out);
if(result)
break;
}
curlx_fclose(out);
if(!result && tempstore && Curl_rename(tempstore, file))
result = CURLE_WRITE_ERROR;
if(result && tempstore)
unlink(tempstore);
}
free(tempstore);
return result;
}
/* hostcompare() returns true if 'host' matches 'check'. The first host
* argument may have a trailing dot present that will be ignored.
*/
static bool hostcompare(const char *host, const char *check)
{
size_t hlen = strlen(host);
size_t clen = strlen(check);
if(hlen && (host[hlen - 1] == '.'))
hlen--;
if(hlen != clen)
/* they cannot match if they have different lengths */
return FALSE;
return curl_strnequal(host, check, hlen);
}
/* altsvc_flush() removes all alternatives for this source origin from the
list */
static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid,
const char *srchost, unsigned short srcport)
{
struct Curl_llist_node *e;
struct Curl_llist_node *n;
for(e = Curl_llist_head(&asi->list); e; e = n) {
struct altsvc *as = Curl_node_elem(e);
n = Curl_node_next(e);
if((srcalpnid == as->src.alpnid) &&
(srcport == as->src.port) &&
hostcompare(srchost, as->src.host)) {
Curl_node_remove(e);
altsvc_free(as);
}
}
}
#if defined(DEBUGBUILD) || defined(UNITTESTS)
/* to play well with debug builds, we can *set* a fixed time this will
return */
static time_t altsvc_debugtime(void *unused)
{
const char *timestr = getenv("CURL_TIME");
(void)unused;
if(timestr) {
curl_off_t val;
curlx_str_number(&timestr, &val, TIME_T_MAX);
return (time_t)val;
}
return time(NULL);
}
#undef time
#define time(x) altsvc_debugtime(x)
#endif
/*
* Curl_altsvc_parse() takes an incoming alt-svc response header and stores
* the data correctly in the cache.
*
* 'value' points to the header *value*. That is contents to the right of the
* header name.
*
* Currently this function rejects invalid data without returning an error.
* Invalid hostname, port number will result in the specific alternative
* being rejected. Unknown protocols are skipped.
*/
CURLcode Curl_altsvc_parse(struct Curl_easy *data,
struct altsvcinfo *asi, const char *value,
enum alpnid srcalpnid, const char *srchost,
unsigned short srcport)
{
const char *p = value;
struct altsvc *as;
unsigned short dstport = srcport; /* the same by default */
size_t entries = 0;
struct Curl_str alpn;
const char *sp;
time_t maxage = 24 * 3600; /* default is 24 hours */
bool persist = FALSE;
#ifdef CURL_DISABLE_VERBOSE_STRINGS
(void)data;
#endif
DEBUGASSERT(asi);
/* initial check for "clear" */
if(!curlx_str_cspn(&p, &alpn, ";\n\r")) {
curlx_str_trimblanks(&alpn);
/* "clear" is a magic keyword */
if(curlx_str_casecompare(&alpn, "clear")) {
/* Flush cached alternatives for this source origin */
altsvc_flush(asi, srcalpnid, srchost, srcport);
return CURLE_OK;
}
}
p = value;
if(curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, '='))
return CURLE_OK; /* strange line */
curlx_str_trimblanks(&alpn);
/* Handle the optional 'ma' and 'persist' flags once first, as they need to
be known for each alternative service. Unknown flags are skipped. */
sp = strchr(p, ';');
if(sp) {
sp++; /* pass the semicolon */
for(;;) {
struct Curl_str name;
struct Curl_str val;
const char *vp;
curl_off_t num;
bool quoted;
/* allow some extra whitespaces around name and value */
if(curlx_str_until(&sp, &name, 20, '=') ||
curlx_str_single(&sp, '=') ||
curlx_str_until(&sp, &val, 80, ';'))
break;
curlx_str_trimblanks(&name);
curlx_str_trimblanks(&val);
/* the value might be quoted */
vp = curlx_str(&val);
quoted = (*vp == '\"');
if(quoted)
vp++;
if(!curlx_str_number(&vp, &num, TIME_T_MAX)) {
if(curlx_str_casecompare(&name, "ma"))
maxage = (time_t)num;
else if(curlx_str_casecompare(&name, "persist") && (num == 1))
persist = TRUE;
}
if(quoted && curlx_str_single(&sp, '\"'))
break;
if(curlx_str_single(&sp, ';'))
break;
}
}
do {
if(!curlx_str_single(&p, '=')) {
/* [protocol]="[host][:port], [protocol]="[host][:port]" */
enum alpnid dstalpnid =
Curl_alpn2alpnid(curlx_str(&alpn), curlx_strlen(&alpn));
if(!curlx_str_single(&p, '\"')) {
struct Curl_str dsthost;
curl_off_t port = 0;
if(curlx_str_single(&p, ':')) {
/* hostname starts here */
if(curlx_str_single(&p, '[')) {
if(curlx_str_until(&p, &dsthost, MAX_ALTSVC_HOSTLEN, ':')) {
infof(data, "Bad alt-svc hostname, ignoring.");
break;
}
}
else {
/* IPv6 host name */
if(curlx_str_until(&p, &dsthost, MAX_IPADR_LEN, ']') ||
curlx_str_single(&p, ']')) {
infof(data, "Bad alt-svc IPv6 hostname, ignoring.");
break;
}
}
if(curlx_str_single(&p, ':'))
break;
}
else
/* no destination name, use source host */
curlx_str_assign(&dsthost, srchost, strlen(srchost));
if(curlx_str_number(&p, &port, 0xffff)) {
infof(data, "Unknown alt-svc port number, ignoring.");
break;
}
dstport = (unsigned short)port;
if(curlx_str_single(&p, '\"'))
break;
if(dstalpnid) {
if(!entries++)
/* Flush cached alternatives for this source origin, if any - when
this is the first entry of the line. */
altsvc_flush(asi, srcalpnid, srchost, srcport);
as = altsvc_createid(srchost, strlen(srchost),
curlx_str(&dsthost),
curlx_strlen(&dsthost),
srcalpnid, dstalpnid,
srcport, dstport);
if(as) {
time_t secs = time(NULL);
/* The expires time also needs to take the Age: value (if any)
into account. [See RFC 7838 section 3.1] */
if(maxage > (TIME_T_MAX - secs))
as->expires = TIME_T_MAX;
else
as->expires = maxage + secs;
as->persist = persist;
Curl_llist_append(&asi->list, as, &as->node);
infof(data, "Added alt-svc: %.*s:%d over %s",
(int)curlx_strlen(&dsthost), curlx_str(&dsthost),
dstport, Curl_alpnid2str(dstalpnid));
}
}
}
else
break;
/* after the double quote there can be a comma if there is another
string or a semicolon if no more */
if(curlx_str_single(&p, ','))
break;
/* comma means another alternative is present */
if(curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, '='))
break;
curlx_str_trimblanks(&alpn);
}
else
break;
} while(1);
return CURLE_OK;
}
/*
* Return TRUE on a match
*/
bool Curl_altsvc_lookup(struct altsvcinfo *asi,
enum alpnid srcalpnid, const char *srchost,
int srcport,
struct altsvc **dstentry,
const int versions) /* one or more bits */
{
struct Curl_llist_node *e;
struct Curl_llist_node *n;
time_t now = time(NULL);
DEBUGASSERT(asi);
DEBUGASSERT(srchost);
DEBUGASSERT(dstentry);
for(e = Curl_llist_head(&asi->list); e; e = n) {
struct altsvc *as = Curl_node_elem(e);
n = Curl_node_next(e);
if(as->expires < now) {
/* an expired entry, remove */
Curl_node_remove(e);
altsvc_free(as);
continue;
}
if((as->src.alpnid == srcalpnid) &&
hostcompare(srchost, as->src.host) &&
(as->src.port == srcport) &&
(versions & (int)as->dst.alpnid)) {
/* match */
*dstentry = as;
return TRUE;
}
}
return FALSE;
}
#if defined(DEBUGBUILD) || defined(UNITTESTS)
#undef time
#endif
#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_ALTSVC */
+74
View File
@@ -0,0 +1,74 @@
#ifndef HEADER_CURL_ALTSVC_H
#define HEADER_CURL_ALTSVC_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_ALTSVC)
#include <curl/curl.h>
#include "llist.h"
struct althost {
char *host;
unsigned short port;
enum alpnid alpnid;
};
struct altsvc {
struct althost src;
struct althost dst;
time_t expires;
struct Curl_llist_node node;
unsigned int prio;
BIT(persist);
};
struct altsvcinfo {
char *filename;
struct Curl_llist list; /* list of entries */
long flags; /* the publicly set bitmask */
};
const char *Curl_alpnid2str(enum alpnid id);
struct altsvcinfo *Curl_altsvc_init(void);
CURLcode Curl_altsvc_load(struct altsvcinfo *asi, const char *file);
CURLcode Curl_altsvc_save(struct Curl_easy *data,
struct altsvcinfo *asi, const char *file);
CURLcode Curl_altsvc_ctrl(struct altsvcinfo *asi, const long ctrl);
void Curl_altsvc_cleanup(struct altsvcinfo **altsvc);
CURLcode Curl_altsvc_parse(struct Curl_easy *data,
struct altsvcinfo *altsvc, const char *value,
enum alpnid srcalpn, const char *srchost,
unsigned short srcport);
bool Curl_altsvc_lookup(struct altsvcinfo *asi,
enum alpnid srcalpnid, const char *srchost,
int srcport,
struct altsvc **dstentry,
const int versions); /* CURLALTSVC_H* bits */
#else
/* disabled */
#define Curl_altsvc_save(a,b,c)
#define Curl_altsvc_cleanup(x)
#endif /* !CURL_DISABLE_HTTP && !CURL_DISABLE_ALTSVC */
#endif /* HEADER_CURL_ALTSVC_H */
+247
View File
@@ -0,0 +1,247 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifdef __AMIGA__
#include <curl/curl.h>
#include "hostip.h"
#include "amigaos.h"
#ifdef HAVE_PROTO_BSDSOCKET_H
# if defined(__amigaos4__)
# include <bsdsocket/socketbasetags.h>
# elif !defined(USE_AMISSL)
# include <amitcp/socketbasetags.h>
# endif
# ifdef __libnix__
# include <stabs.h>
# endif
#endif
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
#ifdef HAVE_PROTO_BSDSOCKET_H
#ifdef __amigaos4__
/*
* AmigaOS 4.x specific code
*/
/*
* hostip4.c - Curl_ipv4_resolve_r() replacement code
*
* Logic that needs to be considered are the following build cases:
* - newlib networking
* - clib2 networking
* - direct bsdsocket.library networking (usually AmiSSL builds)
* Each with the threaded resolver enabled or not.
*
* With the threaded resolver enabled, try to use gethostbyname_r() where
* available, otherwise (re)open bsdsocket.library and fallback to
* gethostbyname().
*/
#include <proto/bsdsocket.h>
static struct SocketIFace *__CurlISocket = NULL;
static uint32 SocketFeatures = 0;
#define HAVE_BSDSOCKET_GETHOSTBYNAME_R 0x01
#define HAVE_BSDSOCKET_GETADDRINFO 0x02
CURLcode Curl_amiga_init(void)
{
struct SocketIFace *ISocket;
struct Library *base = OpenLibrary("bsdsocket.library", 4);
if(base) {
ISocket = (struct SocketIFace *)GetInterface(base, "main", 1, NULL);
if(ISocket) {
ULONG enabled = 0;
SocketBaseTags(SBTM_SETVAL(SBTC_CAN_SHARE_LIBRARY_BASES), TRUE,
SBTM_GETREF(SBTC_HAVE_GETHOSTADDR_R_API), (ULONG)&enabled,
TAG_DONE);
if(enabled) {
SocketFeatures |= HAVE_BSDSOCKET_GETHOSTBYNAME_R;
}
__CurlISocket = ISocket;
atexit(Curl_amiga_cleanup);
return CURLE_OK;
}
CloseLibrary(base);
}
return CURLE_FAILED_INIT;
}
void Curl_amiga_cleanup(void)
{
if(__CurlISocket) {
struct Library *base = __CurlISocket->Data.LibBase;
DropInterface((struct Interface *)__CurlISocket);
CloseLibrary(base);
__CurlISocket = NULL;
}
}
#ifdef CURLRES_AMIGA
/*
* Because we need to handle the different cases in hostip4.c at runtime,
* not at compile-time, based on what was detected in Curl_amiga_init(),
* we replace it completely with our own as to not complicate the baseline
* code. Assumes malloc/calloc/free are thread safe because Curl_he2ai()
* allocates memory also.
*/
struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname,
int port)
{
struct Curl_addrinfo *ai = NULL;
struct hostent *h;
struct SocketIFace *ISocket = __CurlISocket;
if(SocketFeatures & HAVE_BSDSOCKET_GETHOSTBYNAME_R) {
LONG h_errnop = 0;
struct hostent *buf;
buf = calloc(1, CURL_HOSTENT_SIZE);
if(buf) {
h = gethostbyname_r((STRPTR)hostname, buf,
(char *)buf + sizeof(struct hostent),
CURL_HOSTENT_SIZE - sizeof(struct hostent),
&h_errnop);
if(h) {
ai = Curl_he2ai(h, port);
}
free(buf);
}
}
else {
#ifdef CURLRES_THREADED
/* gethostbyname() is not thread safe, so we need to reopen bsdsocket
* on the thread's context
*/
struct Library *base = OpenLibrary("bsdsocket.library", 4);
if(base) {
ISocket = (struct SocketIFace *)GetInterface(base, "main", 1, NULL);
if(ISocket) {
h = gethostbyname((STRPTR)hostname);
if(h) {
ai = Curl_he2ai(h, port);
}
DropInterface((struct Interface *)ISocket);
}
CloseLibrary(base);
}
#else
/* not using threaded resolver - safe to use this as-is */
h = gethostbyname(hostname);
if(h) {
ai = Curl_he2ai(h, port);
}
#endif
}
return ai;
}
#endif /* CURLRES_AMIGA */
#ifdef USE_AMISSL
#include <signal.h>
int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *errorfds, struct timeval *timeout)
{
int r = WaitSelect(nfds, readfds, writefds, errorfds, timeout, 0);
/* Ensure Ctrl-C signal is actioned */
if((r == -1) && (SOCKERRNO == SOCKEINTR))
raise(SIGINT);
return r;
}
#endif /* USE_AMISSL */
#elif !defined(USE_AMISSL) /* __amigaos4__ */
/*
* Amiga OS3 specific code
*/
struct Library *SocketBase = NULL;
#ifdef __libnix__
void __request(const char *msg);
#define CURL_AMIGA_REQUEST(msg) __request(msg)
#else
#define CURL_AMIGA_REQUEST(msg) Printf((const unsigned char *)(msg "\n\a"), 0)
#endif
void Curl_amiga_cleanup(void)
{
if(SocketBase) {
CloseLibrary(SocketBase);
SocketBase = NULL;
}
}
CURLcode Curl_amiga_init(void)
{
if(!SocketBase)
SocketBase = OpenLibrary((const unsigned char *)"bsdsocket.library", 4);
if(!SocketBase) {
CURL_AMIGA_REQUEST("No TCP/IP Stack running!");
return CURLE_FAILED_INIT;
}
if(SocketBaseTags(SBTM_SETVAL(SBTC_ERRNOPTR(sizeof(errno))), (ULONG)&errno,
SBTM_SETVAL(SBTC_LOGTAGPTR), (ULONG)"curl",
TAG_DONE)) {
CURL_AMIGA_REQUEST("SocketBaseTags ERROR");
return CURLE_FAILED_INIT;
}
#ifndef __libnix__
atexit(Curl_amiga_cleanup);
#endif
return CURLE_OK;
}
#ifdef __libnix__
ADD2EXIT(Curl_amiga_cleanup, -50);
#endif
#endif /* !USE_AMISSL */
#endif /* HAVE_PROTO_BSDSOCKET_H */
#endif /* __AMIGA__ */
+41
View File
@@ -0,0 +1,41 @@
#ifndef HEADER_CURL_AMIGAOS_H
#define HEADER_CURL_AMIGAOS_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#if defined(__AMIGA__) && defined(HAVE_PROTO_BSDSOCKET_H) && \
(!defined(USE_AMISSL) || defined(__amigaos4__))
CURLcode Curl_amiga_init(void);
void Curl_amiga_cleanup(void);
#else
#define Curl_amiga_init() CURLE_OK
#define Curl_amiga_cleanup() Curl_nop_stmt
#endif
#endif /* HEADER_CURL_AMIGAOS_H */
+117
View File
@@ -0,0 +1,117 @@
#ifndef HEADER_CURL_ARPA_TELNET_H
#define HEADER_CURL_ARPA_TELNET_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#ifndef CURL_DISABLE_TELNET
/*
* Telnet option defines. Add more here if in need.
*/
#define CURL_TELOPT_BINARY 0 /* binary 8bit data */
#define CURL_TELOPT_ECHO 1 /* just echo! */
#define CURL_TELOPT_SGA 3 /* Suppress Go Ahead */
#define CURL_TELOPT_EXOPL 255 /* EXtended OPtions List */
#define CURL_TELOPT_TTYPE 24 /* Terminal TYPE */
#define CURL_TELOPT_NAWS 31 /* Negotiate About Window Size */
#define CURL_TELOPT_XDISPLOC 35 /* X DISPlay LOCation */
#define CURL_TELOPT_NEW_ENVIRON 39 /* NEW ENVIRONment variables */
#define CURL_NEW_ENV_VAR 0
#define CURL_NEW_ENV_VALUE 1
#ifndef CURL_DISABLE_VERBOSE_STRINGS
/*
* The telnet options represented as strings
*/
static const char * const telnetoptions[]=
{
"BINARY", "ECHO", "RCP", "SUPPRESS GO AHEAD",
"NAME", "STATUS", "TIMING MARK", "RCTE",
"NAOL", "NAOP", "NAOCRD", "NAOHTS",
"NAOHTD", "NAOFFD", "NAOVTS", "NAOVTD",
"NAOLFD", "EXTEND ASCII", "LOGOUT", "BYTE MACRO",
"DE TERMINAL", "SUPDUP", "SUPDUP OUTPUT", "SEND LOCATION",
"TERM TYPE", "END OF RECORD", "TACACS UID", "OUTPUT MARKING",
"TTYLOC", "3270 REGIME", "X3 PAD", "NAWS",
"TERM SPEED", "LFLOW", "LINEMODE", "XDISPLOC",
"OLD-ENVIRON", "AUTHENTICATION", "ENCRYPT", "NEW-ENVIRON"
};
#define CURL_TELOPT(x) telnetoptions[x]
#else
#define CURL_TELOPT(x) ""
#endif
#define CURL_TELOPT_MAXIMUM CURL_TELOPT_NEW_ENVIRON
#define CURL_TELOPT_OK(x) ((x) <= CURL_TELOPT_MAXIMUM)
#define CURL_NTELOPTS 40
/*
* First some defines
*/
#define CURL_xEOF 236 /* End Of File */
#define CURL_SE 240 /* Sub negotiation End */
#define CURL_NOP 241 /* No OPeration */
#define CURL_DM 242 /* Data Mark */
#define CURL_GA 249 /* Go Ahead, reverse the line */
#define CURL_SB 250 /* SuBnegotiation */
#define CURL_WILL 251 /* Our side WILL use this option */
#define CURL_WONT 252 /* Our side will not use this option */
#define CURL_DO 253 /* DO use this option! */
#define CURL_DONT 254 /* DON'T use this option! */
#define CURL_IAC 255 /* Interpret As Command */
#ifndef CURL_DISABLE_VERBOSE_STRINGS
/*
* Then those numbers represented as strings:
*/
static const char * const telnetcmds[]=
{
"EOF", "SUSP", "ABORT", "EOR", "SE",
"NOP", "DMARK", "BRK", "IP", "AO",
"AYT", "EC", "EL", "GA", "SB",
"WILL", "WONT", "DO", "DONT", "IAC"
};
#endif
#define CURL_TELCMD_MINIMUM CURL_xEOF /* the first one */
#define CURL_TELCMD_MAXIMUM CURL_IAC /* surprise, 255 is the last one! ;-) */
#define CURL_TELQUAL_IS 0
#define CURL_TELQUAL_SEND 1
#define CURL_TELQUAL_INFO 2
#define CURL_TELQUAL_NAME 3
#define CURL_TELCMD_OK(x) ( ((unsigned int)(x) >= CURL_TELCMD_MINIMUM) && \
((unsigned int)(x) <= CURL_TELCMD_MAXIMUM) )
#ifndef CURL_DISABLE_VERBOSE_STRINGS
#define CURL_TELCMD(x) telnetcmds[(x)-CURL_TELCMD_MINIMUM]
#else
#define CURL_TELCMD(x) ""
#endif
#endif /* CURL_DISABLE_TELNET */
#endif /* HEADER_CURL_ARPA_TELNET_H */
+986
View File
@@ -0,0 +1,986 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifdef CURLRES_ARES
/***********************************************************************
* Only for ares-enabled builds
* And only for functions that fulfill the asynch resolver backend API
* as defined in asyn.h, nothing else belongs in this file!
**********************************************************************/
#include <limits.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef __VMS
#include <in.h>
#include <inet.h>
#endif
#include "urldata.h"
#include "cfilters.h"
#include "sendf.h"
#include "hostip.h"
#include "hash.h"
#include "share.h"
#include "url.h"
#include "multiif.h"
#include "curlx/inet_pton.h"
#include "connect.h"
#include "select.h"
#include "progress.h"
#include "curlx/timediff.h"
#include "httpsrr.h"
#include <ares.h>
#include <ares_version.h> /* really old c-ares did not include this by
itself */
#if ARES_VERSION >= 0x010601
/* IPv6 supported since 1.6.1 */
#define HAVE_CARES_IPV6 1
#endif
#if ARES_VERSION >= 0x010704
#define HAVE_CARES_SERVERS_CSV 1
#define HAVE_CARES_LOCAL_DEV 1
#define HAVE_CARES_SET_LOCAL 1
#endif
#if ARES_VERSION >= 0x010b00
#define HAVE_CARES_PORTS_CSV 1
#endif
#if ARES_VERSION >= 0x011000
/* 1.16.0 or later has ares_getaddrinfo */
#define HAVE_CARES_GETADDRINFO 1
#else
/* How long we are willing to wait for additional parallel responses after
obtaining a "definitive" one. For old c-ares without getaddrinfo.
This is intended to equal the c-ares default timeout. cURL always uses that
default value. Unfortunately, c-ares does not expose its default timeout in
its API, but it is officially documented as 5 seconds.
See query_completed_cb() for an explanation of how this is used.
*/
#define HAPPY_EYEBALLS_DNS_TIMEOUT 5000
#endif
#ifdef USE_HTTPSRR
#if ARES_VERSION < 0x011c00
#error "requires c-ares 1.28.0 or newer for HTTPSRR"
#endif
#define HTTPSRR_WORKS
#endif
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
#define CARES_TIMEOUT_PER_ATTEMPT 2000
static int ares_ver = 0;
static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
bool reset_on_null);
/*
* Curl_async_global_init() - the generic low-level asynchronous name
* resolve API. Called from curl_global_init() to initialize global resolver
* environment. Initializes ares library.
*/
int Curl_async_global_init(void)
{
#ifdef CARES_HAVE_ARES_LIBRARY_INIT
if(ares_library_init(ARES_LIB_INIT_ALL)) {
return CURLE_FAILED_INIT;
}
#endif
ares_version(&ares_ver);
return CURLE_OK;
}
/*
* Curl_async_global_cleanup()
*
* Called from curl_global_cleanup() to destroy global resolver environment.
* Deinitializes ares library.
*/
void Curl_async_global_cleanup(void)
{
#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
ares_library_cleanup();
#endif
}
static void sock_state_cb(void *data, ares_socket_t socket_fd,
int readable, int writable)
{
struct Curl_easy *easy = data;
if(!readable && !writable) {
DEBUGASSERT(easy);
Curl_multi_will_close(easy, socket_fd);
}
}
static CURLcode async_ares_init(struct Curl_easy *data)
{
struct async_ares_ctx *ares = &data->state.async.ares;
int status;
struct ares_options options;
int optmask = ARES_OPT_SOCK_STATE_CB;
CURLcode rc = CURLE_OK;
options.sock_state_cb = sock_state_cb;
options.sock_state_cb_data = data;
DEBUGASSERT(!ares->channel);
/*
if c ares < 1.20.0: curl set timeout to CARES_TIMEOUT_PER_ATTEMPT (2s)
if c-ares >= 1.20.0 it already has the timeout to 2s, curl does not need
to set the timeout value;
if c-ares >= 1.24.0, user can set the timeout via /etc/resolv.conf to
overwrite c-ares' timeout.
*/
DEBUGASSERT(ares_ver);
if(ares_ver < 0x011400) {
options.timeout = CARES_TIMEOUT_PER_ATTEMPT;
optmask |= ARES_OPT_TIMEOUTMS;
}
status = ares_init_options(&ares->channel, &options, optmask);
if(status != ARES_SUCCESS) {
ares->channel = NULL;
rc = (status == ARES_ENOMEM) ?
CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT;
goto out;
}
rc = async_ares_set_dns_servers(data, FALSE);
if(rc && rc != CURLE_NOT_BUILT_IN)
goto out;
rc = Curl_async_ares_set_dns_interface(data);
if(rc && rc != CURLE_NOT_BUILT_IN)
goto out;
rc = Curl_async_ares_set_dns_local_ip4(data);
if(rc && rc != CURLE_NOT_BUILT_IN)
goto out;
rc = Curl_async_ares_set_dns_local_ip6(data);
if(rc && rc != CURLE_NOT_BUILT_IN)
goto out;
rc = CURLE_OK;
out:
if(rc && ares->channel) {
ares_destroy(ares->channel);
ares->channel = NULL;
}
return rc;
}
static CURLcode async_ares_init_lazy(struct Curl_easy *data)
{
struct async_ares_ctx *ares = &data->state.async.ares;
if(!ares->channel)
return async_ares_init(data);
return CURLE_OK;
}
CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
{
struct async_ares_ctx *ares = &data->state.async.ares;
CURLcode result = CURLE_OK;
if(!ares->channel) {
result = async_ares_init(data);
}
*impl = ares->channel;
return result;
}
/*
* async_ares_cleanup() cleans up async resolver data.
*/
static void async_ares_cleanup(struct Curl_easy *data)
{
struct async_ares_ctx *ares = &data->state.async.ares;
if(ares->temp_ai) {
Curl_freeaddrinfo(ares->temp_ai);
ares->temp_ai = NULL;
}
#ifdef USE_HTTPSRR
Curl_httpsrr_cleanup(&ares->hinfo);
#endif
}
void Curl_async_ares_shutdown(struct Curl_easy *data)
{
/* c-ares has a method to "cancel" operations on a channel, but
* as reported in #18216, this does not totally reset the channel
* and ares may get stuck.
* We need to destroy the channel and on demand create a new
* one to avoid that. */
Curl_async_ares_destroy(data);
}
void Curl_async_ares_destroy(struct Curl_easy *data)
{
struct async_ares_ctx *ares = &data->state.async.ares;
if(ares->channel) {
ares_destroy(ares->channel);
ares->channel = NULL;
}
async_ares_cleanup(data);
}
/*
* Curl_async_pollset() is called when someone from the outside world
* (using curl_multi_fdset()) wants to get our fd_set setup.
*/
CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps)
{
struct async_ares_ctx *ares = &data->state.async.ares;
if(ares->channel)
return Curl_ares_pollset(data, ares->channel, ps);
return CURLE_OK;
}
/*
* Curl_async_is_resolved() is called repeatedly to check if a previous
* name resolve request has completed. It should also make sure to time-out if
* the operation seems to take too long.
*
* Returns normal CURLcode errors.
*/
CURLcode Curl_async_is_resolved(struct Curl_easy *data,
struct Curl_dns_entry **dns)
{
struct async_ares_ctx *ares = &data->state.async.ares;
CURLcode result = CURLE_OK;
DEBUGASSERT(dns);
*dns = NULL;
if(data->state.async.done) {
*dns = data->state.async.dns;
return ares->result;
}
if(Curl_ares_perform(ares->channel, 0) < 0) {
result = CURLE_UNRECOVERABLE_POLL;
goto out;
}
#ifndef HAVE_CARES_GETADDRINFO
/* Now that we have checked for any last minute results above, see if there
are any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
expires. */
if(ares->num_pending
/* This is only set to non-zero if the timer was started. */
&& (ares->happy_eyeballs_dns_time.tv_sec
|| ares->happy_eyeballs_dns_time.tv_usec)
&& (curlx_timediff(curlx_now(), ares->happy_eyeballs_dns_time)
>= HAPPY_EYEBALLS_DNS_TIMEOUT)) {
/* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
running. */
memset(&ares->happy_eyeballs_dns_time, 0,
sizeof(ares->happy_eyeballs_dns_time));
/* Cancel the raw c-ares request, which will fire query_completed_cb() with
ARES_ECANCELLED synchronously for all pending responses. This will
leave us with res->num_pending == 0, which is perfect for the next
block. */
ares_cancel(ares->channel);
DEBUGASSERT(ares->num_pending == 0);
}
#endif
if(!ares->num_pending) {
/* all c-ares operations done, what is the result to report? */
Curl_resolv_unlink(data, &data->state.async.dns);
data->state.async.done = TRUE;
result = ares->result;
if(ares->ares_status == ARES_SUCCESS && !result) {
data->state.async.dns =
Curl_dnscache_mk_entry(data, ares->temp_ai,
data->state.async.hostname, 0,
data->state.async.port, FALSE);
ares->temp_ai = NULL; /* temp_ai now owned by entry */
#ifdef HTTPSRR_WORKS
if(data->state.async.dns) {
struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo);
if(!lhrr)
result = CURLE_OUT_OF_MEMORY;
else
data->state.async.dns->hinfo = lhrr;
}
#endif
if(!result && data->state.async.dns)
result = Curl_dnscache_add(data, data->state.async.dns);
}
/* if we have not found anything, report the proper
* CURLE_COULDNT_RESOLVE_* code */
if(!result && !data->state.async.dns) {
const char *msg = NULL;
if(ares->ares_status != ARES_SUCCESS)
msg = ares_strerror(ares->ares_status);
result = Curl_resolver_error(data, msg);
}
if(result)
Curl_resolv_unlink(data, &data->state.async.dns);
*dns = data->state.async.dns;
CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
result, *dns ? "" : "not ");
async_ares_cleanup(data);
}
out:
ares->result = result;
return result;
}
/*
* Curl_async_await()
*
* Waits for a resolve to finish. This function should be avoided since using
* this risk getting the multi interface to "hang".
*
* 'entry' MUST be non-NULL.
*
* Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
* CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
*/
CURLcode Curl_async_await(struct Curl_easy *data,
struct Curl_dns_entry **entry)
{
struct async_ares_ctx *ares = &data->state.async.ares;
CURLcode result = CURLE_OK;
timediff_t timeout;
struct curltime now = curlx_now();
DEBUGASSERT(entry);
*entry = NULL; /* clear on entry */
timeout = Curl_timeleft(data, &now, TRUE);
if(timeout < 0) {
/* already expired! */
connclose(data->conn, "Timed out before name resolve started");
return CURLE_OPERATION_TIMEDOUT;
}
if(!timeout)
timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
/* Wait for the name resolve query to complete. */
while(!result) {
struct timeval *tvp, tv, store;
int itimeout;
timediff_t timeout_ms;
#if TIMEDIFF_T_MAX > INT_MAX
itimeout = (timeout > INT_MAX) ? INT_MAX : (int)timeout;
#else
itimeout = (int)timeout;
#endif
store.tv_sec = itimeout/1000;
store.tv_usec = (itimeout%1000)*1000;
tvp = ares_timeout(ares->channel, &store, &tv);
/* use the timeout period ares returned to us above if less than one
second is left, otherwise just use 1000ms to make sure the progress
callback gets called frequent enough */
if(!tvp->tv_sec)
timeout_ms = (timediff_t)(tvp->tv_usec/1000);
else
timeout_ms = 1000;
if(Curl_ares_perform(ares->channel, timeout_ms) < 0)
return CURLE_UNRECOVERABLE_POLL;
result = Curl_async_is_resolved(data, entry);
if(result || data->state.async.done)
break;
if(Curl_pgrsUpdate(data))
result = CURLE_ABORTED_BY_CALLBACK;
else {
struct curltime now2 = curlx_now();
timediff_t timediff = curlx_timediff(now2, now); /* spent time */
if(timediff <= 0)
timeout -= 1; /* always deduct at least 1 */
else if(timediff > timeout)
timeout = -1;
else
timeout -= timediff;
now = now2; /* for next loop */
}
if(timeout < 0)
result = CURLE_OPERATION_TIMEDOUT;
}
/* Operation complete, if the lookup was successful we now have the entry
in the cache. */
data->state.async.done = TRUE;
*entry = data->state.async.dns;
if(result)
ares_cancel(ares->channel);
return result;
}
#ifndef HAVE_CARES_GETADDRINFO
/* Connects results to the list */
static void async_addr_concat(struct Curl_addrinfo **pbase,
struct Curl_addrinfo *ai)
{
if(!ai)
return;
/* When adding `ai` to an existing address list, we prefer ipv6
* to be in front. */
#ifdef USE_IPV6 /* CURLRES_IPV6 */
if(*pbase && (*pbase)->ai_family == PF_INET6) {
/* ipv6 already in front, append `ai` */
struct Curl_addrinfo *tail = *pbase;
while(tail->ai_next)
tail = tail->ai_next;
tail->ai_next = ai;
}
else
#endif /* CURLRES_IPV6 */
{
/* prepend to the (possibly) existing list. */
struct Curl_addrinfo *tail = ai;
while(tail->ai_next)
tail = tail->ai_next;
tail->ai_next = *pbase;
*pbase = ai;
}
}
/*
* ares_query_completed_cb() is the callback that ares will call when
* the host query initiated by ares_gethostbyname() from
* Curl_async_getaddrinfo(), when using ares, is completed either
* successfully or with failure.
*/
static void async_ares_hostbyname_cb(void *user_data,
int status,
int timeouts,
struct hostent *hostent)
{
struct Curl_easy *data = (struct Curl_easy *)user_data;
struct async_ares_ctx *ares = &data->state.async.ares;
(void)timeouts; /* ignored */
if(ARES_EDESTRUCTION == status)
return;
if(ARES_SUCCESS == status) {
ares->ares_status = status; /* one success overrules any error */
async_addr_concat(&ares->temp_ai,
Curl_he2ai(hostent, data->state.async.port));
}
else if(ares->ares_status != ARES_SUCCESS) {
/* no success so far, remember last error */
ares->ares_status = status;
}
ares->num_pending--;
CURL_TRC_DNS(data, "ares: hostbyname done, status=%d, pending=%d, "
"addr=%sfound",
status, ares->num_pending, ares->temp_ai ? "" : "not ");
/* If there are responses still pending, we presume they must be the
complementary IPv4 or IPv6 lookups that we started in parallel in
Curl_async_getaddrinfo() (for Happy Eyeballs). If we have got a
"definitive" response from one of a set of parallel queries, we need to
think about how long we are willing to wait for more responses. */
if(ares->num_pending
/* Only these c-ares status values count as "definitive" for these
purposes. For example, ARES_ENODATA is what we expect when there is
no IPv6 entry for a domain name, and that is not a reason to get more
aggressive in our timeouts for the other response. Other errors are
either a result of bad input (which should affect all parallel
requests), local or network conditions, non-definitive server
responses, or us cancelling the request. */
&& (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) {
/* Right now, there can only be up to two parallel queries, so do not
bother handling any other cases. */
DEBUGASSERT(ares->num_pending == 1);
/* it is possible that one of these parallel queries could succeed
quickly, but the other could always fail or timeout (when we are
talking to a pool of DNS servers that can only successfully resolve
IPv4 address, for example).
it is also possible that the other request could always just take
longer because it needs more time or only the second DNS server can
fulfill it successfully. But, to align with the philosophy of Happy
Eyeballs, we do not want to wait _too_ long or users will think
requests are slow when IPv6 lookups do not actually work (but IPv4
ones do).
So, now that we have a usable answer (some IPv4 addresses, some IPv6
addresses, or "no such domain"), we start a timeout for the remaining
pending responses. Even though it is typical that this resolved
request came back quickly, that needn't be the case. It might be that
this completing request did not get a result from the first DNS
server or even the first round of the whole DNS server pool. So it
could already be quite some time after we issued the DNS queries in
the first place. Without modifying c-ares, we cannot know exactly
where in its retry cycle we are. We could guess based on how much
time has gone by, but it does not really matter. Happy Eyeballs tells
us that, given usable information in hand, we simply do not want to
wait "too much longer" after we get a result.
We simply wait an additional amount of time equal to the default
c-ares query timeout. That is enough time for a typical parallel
response to arrive without being "too long". Even on a network
where one of the two types of queries is failing or timing out
constantly, this will usually mean we wait a total of the default
c-ares timeout (5 seconds) plus the round trip time for the successful
request, which seems bearable. The downside is that c-ares might race
with us to issue one more retry just before we give up, but it seems
better to "waste" that request instead of trying to guess the perfect
timeout to prevent it. After all, we do not even know where in the
c-ares retry cycle each request is.
*/
ares->happy_eyeballs_dns_time = curlx_now();
Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT,
EXPIRE_HAPPY_EYEBALLS_DNS);
}
}
#else
/* c-ares 1.16.0 or later */
/*
* async_ares_node2addr() converts an address list provided by c-ares
* to an internal libcurl compatible list.
*/
static struct Curl_addrinfo *
async_ares_node2addr(struct ares_addrinfo_node *node)
{
/* traverse the ares_addrinfo_node list */
struct ares_addrinfo_node *ai;
struct Curl_addrinfo *cafirst = NULL;
struct Curl_addrinfo *calast = NULL;
int error = 0;
for(ai = node; ai != NULL; ai = ai->ai_next) {
size_t ss_size;
struct Curl_addrinfo *ca;
/* ignore elements with unsupported address family, */
/* settle family-specific sockaddr structure size. */
if(ai->ai_family == AF_INET)
ss_size = sizeof(struct sockaddr_in);
#ifdef USE_IPV6
else if(ai->ai_family == AF_INET6)
ss_size = sizeof(struct sockaddr_in6);
#endif
else
continue;
/* ignore elements without required address info */
if(!ai->ai_addr || !(ai->ai_addrlen > 0))
continue;
/* ignore elements with bogus address size */
if((size_t)ai->ai_addrlen < ss_size)
continue;
ca = malloc(sizeof(struct Curl_addrinfo) + ss_size);
if(!ca) {
error = EAI_MEMORY;
break;
}
/* copy each structure member individually, member ordering, */
/* size, or padding might be different for each platform. */
ca->ai_flags = ai->ai_flags;
ca->ai_family = ai->ai_family;
ca->ai_socktype = ai->ai_socktype;
ca->ai_protocol = ai->ai_protocol;
ca->ai_addrlen = (curl_socklen_t)ss_size;
ca->ai_addr = NULL;
ca->ai_canonname = NULL;
ca->ai_next = NULL;
ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
memcpy(ca->ai_addr, ai->ai_addr, ss_size);
/* if the return list is empty, this becomes the first element */
if(!cafirst)
cafirst = ca;
/* add this element last in the return list */
if(calast)
calast->ai_next = ca;
calast = ca;
}
/* if we failed, destroy the Curl_addrinfo list */
if(error) {
Curl_freeaddrinfo(cafirst);
cafirst = NULL;
}
return cafirst;
}
static void async_ares_addrinfo_cb(void *user_data, int status, int timeouts,
struct ares_addrinfo *ares_ai)
{
struct Curl_easy *data = (struct Curl_easy *)user_data;
struct async_ares_ctx *ares = &data->state.async.ares;
(void)timeouts;
if(ares->ares_status != ARES_SUCCESS) /* do not overwrite success */
ares->ares_status = status;
if(status == ARES_SUCCESS) {
ares->temp_ai = async_ares_node2addr(ares_ai->nodes);
ares_freeaddrinfo(ares_ai);
}
ares->num_pending--;
CURL_TRC_DNS(data, "ares: addrinfo done, query status=%d, "
"overall status=%d, pending=%d, addr=%sfound",
status, ares->ares_status, ares->num_pending,
ares->temp_ai ? "" : "not ");
}
#endif
#ifdef USE_HTTPSRR
static void async_ares_rr_done(void *user_data, ares_status_t status,
size_t timeouts,
const ares_dns_record_t *dnsrec)
{
struct Curl_easy *data = user_data;
struct async_ares_ctx *ares = &data->state.async.ares;
(void)timeouts;
--ares->num_pending;
CURL_TRC_DNS(data, "ares: httpsrr done, status=%d, pending=%d, "
"dnsres=%sfound",
status, ares->num_pending,
(dnsrec &&
ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER)) ?
"" : "not ");
if((ARES_SUCCESS != status) || !dnsrec)
return;
ares->result = Curl_httpsrr_from_ares(data, dnsrec, &ares->hinfo);
}
#endif /* USE_HTTPSRR */
/*
* Curl_async_getaddrinfo() - when using ares
*
* Returns name information about the given hostname and port number. If
* successful, the 'hostent' is returned and the fourth argument will point to
* memory we need to free after use. That memory *MUST* be freed with
* Curl_freeaddrinfo(), nothing else.
*/
struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
const char *hostname,
int port,
int ip_version,
int *waitp)
{
struct async_ares_ctx *ares = &data->state.async.ares;
#ifdef USE_HTTPSRR
char *rrname = NULL;
#endif
*waitp = 0; /* default to synchronous response */
if(async_ares_init_lazy(data))
return NULL;
data->state.async.done = FALSE; /* not done */
data->state.async.dns = NULL; /* clear */
data->state.async.port = port;
data->state.async.ip_version = ip_version;
data->state.async.hostname = strdup(hostname);
if(!data->state.async.hostname)
return NULL;
#ifdef USE_HTTPSRR
if(port != 443) {
rrname = curl_maprintf("_%d_.https.%s", port, hostname);
if(!rrname) {
free(data->state.async.hostname);
return NULL;
}
}
#endif
/* initial status - failed */
ares->ares_status = ARES_ENOTFOUND;
ares->result = CURLE_OK;
#if !defined(CURL_DISABLE_VERBOSE_STRINGS) && \
ARES_VERSION >= 0x011800 /* >= v1.24.0 */
if(CURL_TRC_DNS_is_verbose(data)) {
char *csv = ares_get_servers_csv(ares->channel);
CURL_TRC_DNS(data, "asyn-ares: servers=%s", csv);
ares_free_string(csv);
}
#endif
#ifdef HAVE_CARES_GETADDRINFO
{
struct ares_addrinfo_hints hints;
char service[12];
int pf = PF_INET;
memset(&hints, 0, sizeof(hints));
#ifdef CURLRES_IPV6
if((ip_version != CURL_IPRESOLVE_V4) &&
Curl_ipv6works(data)) {
/* The stack seems to be IPv6-enabled */
if(ip_version == CURL_IPRESOLVE_V6)
pf = PF_INET6;
else
pf = PF_UNSPEC;
}
#endif /* CURLRES_IPV6 */
CURL_TRC_DNS(data, "asyn-ares: fire off getaddrinfo for %s",
(pf == PF_UNSPEC) ? "A+AAAA" :
((pf == PF_INET) ? "A" : "AAAA"));
hints.ai_family = pf;
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
*/
hints.ai_flags = ARES_AI_NUMERICSERV;
curl_msnprintf(service, sizeof(service), "%d", port);
ares->num_pending = 1;
ares_getaddrinfo(ares->channel, data->state.async.hostname,
service, &hints, async_ares_addrinfo_cb, data);
}
#else
#ifdef HAVE_CARES_IPV6
if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
/* The stack seems to be IPv6-enabled */
/* areschannel is already setup in the Curl_open() function */
CURL_TRC_DNS(data, "asyn-ares: fire off query for A");
ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET,
async_ares_hostbyname_cb, data);
CURL_TRC_DNS(data, "asyn-ares: fire off query for AAAA");
ares->num_pending = 2;
ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET6,
async_ares_hostbyname_cb, data);
}
else
#endif
{
/* areschannel is already setup in the Curl_open() function */
CURL_TRC_DNS(data, "asyn-ares: fire off query for A");
ares->num_pending = 1;
ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET,
async_ares_hostbyname_cb, data);
}
#endif
#ifdef USE_HTTPSRR
{
CURL_TRC_DNS(data, "asyn-ares: fire off query for HTTPSRR: %s",
rrname ? rrname : data->state.async.hostname);
memset(&ares->hinfo, 0, sizeof(ares->hinfo));
ares->hinfo.port = -1;
ares->hinfo.rrname = rrname;
ares->num_pending++; /* one more */
ares_query_dnsrec(ares->channel,
rrname ? rrname : data->state.async.hostname,
ARES_CLASS_IN, ARES_REC_TYPE_HTTPS,
async_ares_rr_done, data, NULL);
}
#endif
*waitp = 1; /* expect asynchronous response */
return NULL; /* no struct yet */
}
/* Set what DNS server are is to use. This is called in 2 situations:
* 1. when the application does 'CURLOPT_DNS_SERVERS' and passing NULL
* means any previous set value should be unset. Which means
* we need to destroy and create the are channel anew, if there is one.
* 2. When we lazy init the ares channel and NULL means that there
* are no preferences and we do not reset any existing channel. */
static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
bool reset_on_null)
{
struct async_ares_ctx *ares = &data->state.async.ares;
CURLcode result = CURLE_NOT_BUILT_IN;
const char *servers = data->set.str[STRING_DNS_SERVERS];
int ares_result = ARES_SUCCESS;
#if defined(CURLDEBUG) && defined(HAVE_CARES_SERVERS_CSV)
if(getenv("CURL_DNS_SERVER"))
servers = getenv("CURL_DNS_SERVER");
#endif
if(!servers) {
if(reset_on_null) {
Curl_async_destroy(data);
}
return CURLE_OK;
}
#ifdef HAVE_CARES_SERVERS_CSV
/* if channel is not there, this is just a parameter check */
if(ares->channel)
#ifdef HAVE_CARES_PORTS_CSV
ares_result = ares_set_servers_ports_csv(ares->channel, servers);
#else
ares_result = ares_set_servers_csv(ares->channel, servers);
#endif
switch(ares_result) {
case ARES_SUCCESS:
result = CURLE_OK;
break;
case ARES_ENOMEM:
result = CURLE_OUT_OF_MEMORY;
break;
case ARES_ENOTINITIALIZED:
case ARES_ENODATA:
case ARES_EBADSTR:
default:
DEBUGF(infof(data, "bad servers set"));
result = CURLE_BAD_FUNCTION_ARGUMENT;
break;
}
#else /* too old c-ares version! */
(void)data;
(void)(ares_result);
#endif
return result;
}
CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data)
{
return async_ares_set_dns_servers(data, TRUE);
}
CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data)
{
#ifdef HAVE_CARES_LOCAL_DEV
struct async_ares_ctx *ares = &data->state.async.ares;
const char *interf = data->set.str[STRING_DNS_INTERFACE];
if(!interf)
interf = "";
/* if channel is not there, this is just a parameter check */
if(ares->channel)
ares_set_local_dev(ares->channel, interf);
return CURLE_OK;
#else /* c-ares version too old! */
(void)data;
(void)interf;
return CURLE_NOT_BUILT_IN;
#endif
}
CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data)
{
#ifdef HAVE_CARES_SET_LOCAL
struct async_ares_ctx *ares = &data->state.async.ares;
struct in_addr a4;
const char *local_ip4 = data->set.str[STRING_DNS_LOCAL_IP4];
if((!local_ip4) || (local_ip4[0] == 0)) {
a4.s_addr = 0; /* disabled: do not bind to a specific address */
}
else {
if(curlx_inet_pton(AF_INET, local_ip4, &a4) != 1) {
DEBUGF(infof(data, "bad DNS IPv4 address"));
return CURLE_BAD_FUNCTION_ARGUMENT;
}
}
/* if channel is not there yet, this is just a parameter check */
if(ares->channel)
ares_set_local_ip4(ares->channel, ntohl(a4.s_addr));
return CURLE_OK;
#else /* c-ares version too old! */
(void)data;
(void)local_ip4;
return CURLE_NOT_BUILT_IN;
#endif
}
CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data)
{
#if defined(HAVE_CARES_SET_LOCAL) && defined(USE_IPV6)
struct async_ares_ctx *ares = &data->state.async.ares;
unsigned char a6[INET6_ADDRSTRLEN];
const char *local_ip6 = data->set.str[STRING_DNS_LOCAL_IP6];
if((!local_ip6) || (local_ip6[0] == 0)) {
/* disabled: do not bind to a specific address */
memset(a6, 0, sizeof(a6));
}
else {
if(curlx_inet_pton(AF_INET6, local_ip6, a6) != 1) {
DEBUGF(infof(data, "bad DNS IPv6 address"));
return CURLE_BAD_FUNCTION_ARGUMENT;
}
}
/* if channel is not there, this is just a parameter check */
if(ares->channel)
ares_set_local_ip6(ares->channel, a6);
return CURLE_OK;
#else /* c-ares version too old! */
(void)data;
return CURLE_NOT_BUILT_IN;
#endif
}
#endif /* CURLRES_ARES */
+222
View File
@@ -0,0 +1,222 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef __VMS
#include <in.h>
#include <inet.h>
#endif
#ifdef USE_ARES
#include <ares.h>
#include <ares_version.h> /* really old c-ares did not include this by
itself */
#endif
#include "urldata.h"
#include "asyn.h"
#include "sendf.h"
#include "hostip.h"
#include "hash.h"
#include "multiif.h"
#include "select.h"
#include "share.h"
#include "url.h"
#include "curl_memory.h"
/* The last #include file should be: */
#include "memdebug.h"
/***********************************************************************
* Only for builds using asynchronous name resolves
**********************************************************************/
#ifdef CURLRES_ASYNCH
#ifdef USE_ARES
#if ARES_VERSION < 0x010600
#error "requires c-ares 1.6.0 or newer"
#endif
/*
* Curl_ares_pollset() is called when the outside world (using
* curl_multi_fdset()) wants to get our fd_set setup and we are talking with
* ares. The caller must make sure that this function is only called when we
* have a working ares channel.
*
* Returns: sockets-in-use-bitmap
*/
CURLcode Curl_ares_pollset(struct Curl_easy *data,
ares_channel channel,
struct easy_pollset *ps)
{
struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 };
struct timeval timebuf;
curl_socket_t sockets[16]; /* ARES documented limit */
unsigned int bitmap, i;
struct timeval *timeout;
timediff_t milli;
CURLcode result = CURLE_OK;
DEBUGASSERT(channel);
if(!channel)
return CURLE_FAILED_INIT;
bitmap = ares_getsock(channel, (ares_socket_t *)sockets,
CURL_ARRAYSIZE(sockets));
for(i = 0; i < CURL_ARRAYSIZE(sockets); ++i) {
int flags = 0;
if(ARES_GETSOCK_READABLE(bitmap, i))
flags |= CURL_POLL_IN;
if(ARES_GETSOCK_WRITABLE(bitmap, i))
flags |= CURL_POLL_OUT;
if(!flags)
break;
result = Curl_pollset_change(data, ps, sockets[i], flags, 0);
if(result)
return result;
}
timeout = ares_timeout(channel, &maxtime, &timebuf);
if(!timeout)
timeout = &maxtime;
milli = curlx_tvtoms(timeout);
Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
return result;
}
/*
* Curl_ares_perform()
*
* 1) Ask ares what sockets it currently plays with, then
* 2) wait for the timeout period to check for action on ares' sockets.
* 3) tell ares to act on all the sockets marked as "with action"
*
* return number of sockets it worked on, or -1 on error
*/
int Curl_ares_perform(ares_channel channel,
timediff_t timeout_ms)
{
int nfds;
int bitmask;
ares_socket_t socks[ARES_GETSOCK_MAXNUM];
struct pollfd pfd[ARES_GETSOCK_MAXNUM];
int i;
int num = 0;
if(!channel)
return 0;
bitmask = ares_getsock(channel, socks, ARES_GETSOCK_MAXNUM);
for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
pfd[i].events = 0;
pfd[i].revents = 0;
if(ARES_GETSOCK_READABLE(bitmask, i)) {
pfd[i].fd = socks[i];
pfd[i].events |= POLLRDNORM|POLLIN;
}
if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
pfd[i].fd = socks[i];
pfd[i].events |= POLLWRNORM|POLLOUT;
}
if(pfd[i].events)
num++;
else
break;
}
if(num) {
nfds = Curl_poll(pfd, (unsigned int)num, timeout_ms);
if(nfds < 0)
return -1;
}
else
nfds = 0;
if(!nfds)
/* Call ares_process() unconditionally here, even if we simply timed out
above, as otherwise the ares name resolve will not timeout! */
ares_process_fd(channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
else {
/* move through the descriptors and ask for processing on them */
for(i = 0; i < num; i++)
ares_process_fd(channel,
(pfd[i].revents & (POLLRDNORM|POLLIN)) ?
pfd[i].fd : ARES_SOCKET_BAD,
(pfd[i].revents & (POLLWRNORM|POLLOUT)) ?
pfd[i].fd : ARES_SOCKET_BAD);
}
return nfds;
}
#endif
#endif /* CURLRES_ASYNCH */
#ifdef USE_CURL_ASYNC
#include "doh.h"
void Curl_async_shutdown(struct Curl_easy *data)
{
#ifdef CURLRES_ARES
Curl_async_ares_shutdown(data);
#endif
#ifdef CURLRES_THREADED
Curl_async_thrdd_shutdown(data);
#endif
#ifndef CURL_DISABLE_DOH
Curl_doh_cleanup(data);
#endif
Curl_safefree(data->state.async.hostname);
}
void Curl_async_destroy(struct Curl_easy *data)
{
#ifdef CURLRES_ARES
Curl_async_ares_destroy(data);
#endif
#ifdef CURLRES_THREADED
Curl_async_thrdd_destroy(data);
#endif
#ifndef CURL_DISABLE_DOH
Curl_doh_cleanup(data);
#endif
Curl_safefree(data->state.async.hostname);
}
#endif /* USE_CURL_ASYNC */
+797
View File
@@ -0,0 +1,797 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include "socketpair.h"
/***********************************************************************
* Only for threaded name resolves builds
**********************************************************************/
#ifdef CURLRES_THREADED
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef __VMS
#include <in.h>
#include <inet.h>
#endif
#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
# include <pthread.h>
#endif
#ifdef HAVE_GETADDRINFO
# define RESOLVER_ENOMEM EAI_MEMORY /* = WSA_NOT_ENOUGH_MEMORY on Windows */
#else
# define RESOLVER_ENOMEM SOCKENOMEM
#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 "curl_threads.h"
#include "select.h"
#ifdef USE_ARES
#include <ares.h>
#ifdef USE_HTTPSRR
#define USE_HTTPSRR_ARES /* the combo */
#endif
#endif
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_async_global_init()
* Called from curl_global_init() to initialize global resolver environment.
* Does nothing here.
*/
int Curl_async_global_init(void)
{
#if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
if(ares_library_init(ARES_LIB_INIT_ALL)) {
return CURLE_FAILED_INIT;
}
#endif
return CURLE_OK;
}
/*
* Curl_async_global_cleanup()
* Called from curl_global_cleanup() to destroy global resolver environment.
* Does nothing here.
*/
void Curl_async_global_cleanup(void)
{
#if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
ares_library_cleanup();
#endif
}
static void async_thrdd_destroy(struct Curl_easy *);
static void async_thrdd_shutdown(struct Curl_easy *);
CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
{
(void)data;
*impl = NULL;
return CURLE_OK;
}
/* Give up reference to add_ctx */
static void addr_ctx_unlink(struct async_thrdd_addr_ctx **paddr_ctx,
struct Curl_easy *data)
{
struct async_thrdd_addr_ctx *addr_ctx = *paddr_ctx;
bool destroy;
(void)data;
if(!addr_ctx)
return;
Curl_mutex_acquire(&addr_ctx->mutx);
if(!data) /* called by resolving thread */
addr_ctx->thrd_done = TRUE;
DEBUGASSERT(addr_ctx->ref_count);
--addr_ctx->ref_count;
destroy = !addr_ctx->ref_count;
Curl_mutex_release(&addr_ctx->mutx);
if(destroy) {
Curl_mutex_destroy(&addr_ctx->mutx);
free(addr_ctx->hostname);
if(addr_ctx->res)
Curl_freeaddrinfo(addr_ctx->res);
#ifndef CURL_DISABLE_SOCKETPAIR
#ifndef USE_EVENTFD
wakeup_close(addr_ctx->sock_pair[1]);
#endif
wakeup_close(addr_ctx->sock_pair[0]);
#endif
free(addr_ctx);
}
*paddr_ctx = NULL;
}
/* Initialize context for threaded resolver */
static struct async_thrdd_addr_ctx *
addr_ctx_create(struct Curl_easy *data,
const char *hostname, int port,
const struct addrinfo *hints)
{
struct async_thrdd_addr_ctx *addr_ctx = calloc(1, sizeof(*addr_ctx));
if(!addr_ctx)
return NULL;
addr_ctx->thread_hnd = curl_thread_t_null;
addr_ctx->port = port;
addr_ctx->ref_count = 1;
#ifdef HAVE_GETADDRINFO
DEBUGASSERT(hints);
addr_ctx->hints = *hints;
#else
(void)hints;
#endif
Curl_mutex_init(&addr_ctx->mutx);
#ifndef CURL_DISABLE_SOCKETPAIR
/* create socket pair or pipe */
if(wakeup_create(addr_ctx->sock_pair, FALSE) < 0) {
addr_ctx->sock_pair[0] = CURL_SOCKET_BAD;
addr_ctx->sock_pair[1] = CURL_SOCKET_BAD;
goto err_exit;
}
#endif
addr_ctx->sock_error = 0;
/* Copying hostname string because original can be destroyed by parent
* thread during gethostbyname execution.
*/
addr_ctx->hostname = strdup(hostname);
if(!addr_ctx->hostname)
goto err_exit;
return addr_ctx;
err_exit:
addr_ctx_unlink(&addr_ctx, data);
return NULL;
}
#ifdef HAVE_GETADDRINFO
/*
* getaddrinfo_thread() resolves a name and then exits.
*
* For builds without ARES, but with USE_IPV6, create a resolver thread
* and wait on it.
*/
static CURL_THREAD_RETURN_T CURL_STDCALL getaddrinfo_thread(void *arg)
{
struct async_thrdd_addr_ctx *addr_ctx = arg;
bool do_abort;
Curl_mutex_acquire(&addr_ctx->mutx);
do_abort = addr_ctx->do_abort;
Curl_mutex_release(&addr_ctx->mutx);
if(!do_abort) {
char service[12];
int rc;
curl_msnprintf(service, sizeof(service), "%d", addr_ctx->port);
rc = Curl_getaddrinfo_ex(addr_ctx->hostname, service,
&addr_ctx->hints, &addr_ctx->res);
if(rc) {
addr_ctx->sock_error = SOCKERRNO ? SOCKERRNO : rc;
if(addr_ctx->sock_error == 0)
addr_ctx->sock_error = RESOLVER_ENOMEM;
}
else {
Curl_addrinfo_set_port(addr_ctx->res, addr_ctx->port);
}
Curl_mutex_acquire(&addr_ctx->mutx);
do_abort = addr_ctx->do_abort;
Curl_mutex_release(&addr_ctx->mutx);
#ifndef CURL_DISABLE_SOCKETPAIR
if(!do_abort) {
#ifdef USE_EVENTFD
const uint64_t buf[1] = { 1 };
#else
const char buf[1] = { 1 };
#endif
/* Thread is done, notify transfer */
if(wakeup_write(addr_ctx->sock_pair[1], buf, sizeof(buf)) < 0) {
/* update sock_error to errno */
addr_ctx->sock_error = SOCKERRNO;
}
}
#endif
}
addr_ctx_unlink(&addr_ctx, NULL);
return 0;
}
#else /* HAVE_GETADDRINFO */
/*
* gethostbyname_thread() resolves a name and then exits.
*/
static CURL_THREAD_RETURN_T CURL_STDCALL gethostbyname_thread(void *arg)
{
struct async_thrdd_addr_ctx *addr_ctx = arg;
bool do_abort;
Curl_mutex_acquire(&addr_ctx->mutx);
do_abort = addr_ctx->do_abort;
Curl_mutex_release(&addr_ctx->mutx);
if(!do_abort) {
addr_ctx->res = Curl_ipv4_resolve_r(addr_ctx->hostname, addr_ctx->port);
if(!addr_ctx->res) {
addr_ctx->sock_error = SOCKERRNO;
if(addr_ctx->sock_error == 0)
addr_ctx->sock_error = RESOLVER_ENOMEM;
}
Curl_mutex_acquire(&addr_ctx->mutx);
do_abort = addr_ctx->do_abort;
Curl_mutex_release(&addr_ctx->mutx);
#ifndef CURL_DISABLE_SOCKETPAIR
if(!do_abort) {
#ifdef USE_EVENTFD
const uint64_t buf[1] = { 1 };
#else
const char buf[1] = { 1 };
#endif
/* Thread is done, notify transfer */
if(wakeup_write(addr_ctx->sock_pair[1], buf, sizeof(buf)) < 0) {
/* update sock_error to errno */
addr_ctx->sock_error = SOCKERRNO;
}
}
#endif
}
addr_ctx_unlink(&addr_ctx, NULL);
return 0;
}
#endif /* HAVE_GETADDRINFO */
/*
* async_thrdd_destroy() cleans up async resolver data and thread handle.
*/
static void async_thrdd_destroy(struct Curl_easy *data)
{
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
struct async_thrdd_addr_ctx *addr = thrdd->addr;
#ifdef USE_HTTPSRR_ARES
if(thrdd->rr.channel) {
ares_destroy(thrdd->rr.channel);
thrdd->rr.channel = NULL;
}
Curl_httpsrr_cleanup(&thrdd->rr.hinfo);
#endif
if(thrdd->addr && (thrdd->addr->thread_hnd != curl_thread_t_null)) {
bool done;
Curl_mutex_acquire(&addr->mutx);
#ifndef CURL_DISABLE_SOCKETPAIR
if(!addr->do_abort)
Curl_multi_will_close(data, addr->sock_pair[0]);
#endif
addr->do_abort = TRUE;
done = addr->thrd_done;
Curl_mutex_release(&addr->mutx);
if(done) {
Curl_thread_join(&addr->thread_hnd);
CURL_TRC_DNS(data, "async_thrdd_destroy, thread joined");
}
else {
/* thread is still running. Detach it. */
Curl_thread_destroy(&addr->thread_hnd);
CURL_TRC_DNS(data, "async_thrdd_destroy, thread detached");
}
}
/* release our reference to the shared context */
addr_ctx_unlink(&thrdd->addr, data);
}
#ifdef USE_HTTPSRR_ARES
static void async_thrdd_rr_done(void *user_data, ares_status_t status,
size_t timeouts,
const ares_dns_record_t *dnsrec)
{
struct Curl_easy *data = user_data;
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
(void)timeouts;
thrdd->rr.done = TRUE;
if((ARES_SUCCESS != status) || !dnsrec)
return;
thrdd->rr.result = Curl_httpsrr_from_ares(data, dnsrec, &thrdd->rr.hinfo);
}
static CURLcode async_rr_start(struct Curl_easy *data, int port)
{
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
int status;
char *rrname = NULL;
DEBUGASSERT(!thrdd->rr.channel);
if(port != 443) {
rrname = curl_maprintf("_%d_.https.%s", port, data->conn->host.name);
if(!rrname)
return CURLE_OUT_OF_MEMORY;
}
status = ares_init_options(&thrdd->rr.channel, NULL, 0);
if(status != ARES_SUCCESS) {
thrdd->rr.channel = NULL;
return CURLE_FAILED_INIT;
}
#ifdef CURLDEBUG
if(getenv("CURL_DNS_SERVER")) {
const char *servers = getenv("CURL_DNS_SERVER");
status = ares_set_servers_ports_csv(thrdd->rr.channel, servers);
if(status)
return CURLE_FAILED_INIT;
}
#endif
memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo));
thrdd->rr.hinfo.port = -1;
thrdd->rr.hinfo.rrname = rrname;
ares_query_dnsrec(thrdd->rr.channel,
rrname ? rrname : data->conn->host.name, ARES_CLASS_IN,
ARES_REC_TYPE_HTTPS,
async_thrdd_rr_done, data, NULL);
CURL_TRC_DNS(data, "Issued HTTPS-RR request for %s", data->conn->host.name);
return CURLE_OK;
}
#endif
/*
* async_thrdd_init() starts a new thread that performs the actual
* resolve. This function returns before the resolve is done.
*
* Returns FALSE in case of failure, otherwise TRUE.
*/
static bool async_thrdd_init(struct Curl_easy *data,
const char *hostname, int port, int ip_version,
const struct addrinfo *hints)
{
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
struct async_thrdd_addr_ctx *addr_ctx;
/* !checksrc! disable ERRNOVAR 1 */
int err = ENOMEM;
if(thrdd->addr
#ifdef USE_HTTPSRR_ARES
|| thrdd->rr.channel
#endif
) {
CURL_TRC_DNS(data, "starting new resolve, with previous not cleaned up");
async_thrdd_destroy(data);
DEBUGASSERT(!thrdd->addr);
#ifdef USE_HTTPSRR_ARES
DEBUGASSERT(!thrdd->rr.channel);
#endif
}
data->state.async.dns = NULL;
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;
addr_ctx = addr_ctx_create(data, hostname, port, hints);
if(!addr_ctx)
goto err_exit;
thrdd->addr = addr_ctx;
/* passing addr_ctx to the thread adds a reference */
addr_ctx->ref_count = 2;
addr_ctx->start = curlx_now();
#ifdef HAVE_GETADDRINFO
addr_ctx->thread_hnd = Curl_thread_create(getaddrinfo_thread, addr_ctx);
#else
addr_ctx->thread_hnd = Curl_thread_create(gethostbyname_thread, addr_ctx);
#endif
if(addr_ctx->thread_hnd == curl_thread_t_null) {
/* The thread never started */
addr_ctx->ref_count = 1;
addr_ctx->thrd_done = TRUE;
err = errno;
goto err_exit;
}
#ifdef USE_HTTPSRR_ARES
if(async_rr_start(data, port))
infof(data, "Failed HTTPS RR operation");
#endif
CURL_TRC_DNS(data, "resolve thread started for of %s:%d", hostname, port);
return TRUE;
err_exit:
CURL_TRC_DNS(data, "resolve thread failed init: %d", err);
async_thrdd_destroy(data);
CURL_SETERRNO(err);
return FALSE;
}
static void async_thrdd_shutdown(struct Curl_easy *data)
{
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
struct async_thrdd_addr_ctx *addr_ctx = thrdd->addr;
bool done;
if(!addr_ctx)
return;
if(addr_ctx->thread_hnd == curl_thread_t_null)
return;
Curl_mutex_acquire(&addr_ctx->mutx);
#ifndef CURL_DISABLE_SOCKETPAIR
if(!addr_ctx->do_abort)
Curl_multi_will_close(data, addr_ctx->sock_pair[0]);
#endif
addr_ctx->do_abort = TRUE;
done = addr_ctx->thrd_done;
Curl_mutex_release(&addr_ctx->mutx);
/* Wait for the thread to terminate if it is already marked done. If it is
not done yet we cannot do anything here. We had tried pthread_cancel but
it caused hanging and resource leaks (#18532). */
if(done && (addr_ctx->thread_hnd != curl_thread_t_null)) {
Curl_thread_join(&addr_ctx->thread_hnd);
CURL_TRC_DNS(data, "async_thrdd_shutdown, thread joined");
}
}
/*
* 'entry' may be NULL and then no data is returned
*/
static CURLcode asyn_thrdd_await(struct Curl_easy *data,
struct async_thrdd_addr_ctx *addr_ctx,
struct Curl_dns_entry **entry)
{
CURLcode result = CURLE_OK;
if(addr_ctx->thread_hnd != curl_thread_t_null) {
/* not interested in result? cancel, if still running... */
if(!entry)
async_thrdd_shutdown(data);
if(addr_ctx->thread_hnd != curl_thread_t_null) {
CURL_TRC_DNS(data, "resolve, wait for thread to finish");
if(!Curl_thread_join(&addr_ctx->thread_hnd)) {
DEBUGASSERT(0);
}
}
if(entry)
result = Curl_async_is_resolved(data, entry);
}
data->state.async.done = TRUE;
if(entry)
*entry = data->state.async.dns;
return result;
}
/*
* Until we gain a way to signal the resolver threads to stop early, we must
* simply wait for them and ignore their results.
*/
void Curl_async_thrdd_shutdown(struct Curl_easy *data)
{
async_thrdd_shutdown(data);
}
void Curl_async_thrdd_destroy(struct Curl_easy *data)
{
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
if(thrdd->addr && !data->set.quick_exit) {
(void)asyn_thrdd_await(data, thrdd->addr, NULL);
}
async_thrdd_destroy(data);
}
/*
* Curl_async_await()
*
* Waits for a resolve to finish. This function should be avoided since using
* this risk getting the multi interface to "hang".
*
* If 'entry' is non-NULL, make it point to the resolved dns entry
*
* Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
* CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
*
* This is the version for resolves-in-a-thread.
*/
CURLcode Curl_async_await(struct Curl_easy *data,
struct Curl_dns_entry **entry)
{
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
if(thrdd->addr)
return asyn_thrdd_await(data, thrdd->addr, entry);
return CURLE_FAILED_INIT;
}
/*
* Curl_async_is_resolved() is called repeatedly to check if a previous
* name resolve request has completed. It should also make sure to time-out if
* the operation seems to take too long.
*/
CURLcode Curl_async_is_resolved(struct Curl_easy *data,
struct Curl_dns_entry **dns)
{
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
bool done = FALSE;
DEBUGASSERT(dns);
*dns = NULL;
if(data->state.async.done) {
*dns = data->state.async.dns;
CURL_TRC_DNS(data, "threaded: is_resolved(), already done, dns=%sfound",
*dns ? "" : "not ");
return CURLE_OK;
}
#ifdef USE_HTTPSRR_ARES
/* best effort, ignore errors */
if(thrdd->rr.channel)
(void)Curl_ares_perform(thrdd->rr.channel, 0);
#endif
DEBUGASSERT(thrdd->addr);
if(!thrdd->addr)
return CURLE_FAILED_INIT;
Curl_mutex_acquire(&thrdd->addr->mutx);
done = thrdd->addr->thrd_done;
Curl_mutex_release(&thrdd->addr->mutx);
if(done) {
CURLcode result = CURLE_OK;
data->state.async.done = TRUE;
Curl_resolv_unlink(data, &data->state.async.dns);
Curl_expire_done(data, EXPIRE_ASYNC_NAME);
if(thrdd->addr->res) {
data->state.async.dns =
Curl_dnscache_mk_entry(data, thrdd->addr->res,
data->state.async.hostname, 0,
data->state.async.port, FALSE);
thrdd->addr->res = NULL;
if(!data->state.async.dns)
result = CURLE_OUT_OF_MEMORY;
#ifdef USE_HTTPSRR_ARES
if(thrdd->rr.channel) {
result = thrdd->rr.result;
if(!result) {
struct Curl_https_rrinfo *lhrr;
lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo);
if(!lhrr)
result = CURLE_OUT_OF_MEMORY;
else
data->state.async.dns->hinfo = lhrr;
}
}
#endif
if(!result && data->state.async.dns)
result = Curl_dnscache_add(data, data->state.async.dns);
}
if(!result && !data->state.async.dns)
result = Curl_resolver_error(data, NULL);
if(result)
Curl_resolv_unlink(data, &data->state.async.dns);
*dns = data->state.async.dns;
CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
result, *dns ? "" : "not ");
async_thrdd_shutdown(data);
return result;
}
else {
/* poll for name lookup done with exponential backoff up to 250ms */
/* should be fine even if this converts to 32-bit */
timediff_t elapsed = curlx_timediff(curlx_now(),
data->progress.t_startsingle);
if(elapsed < 0)
elapsed = 0;
if(thrdd->addr->poll_interval == 0)
/* Start at 1ms poll interval */
thrdd->addr->poll_interval = 1;
else if(elapsed >= thrdd->addr->interval_end)
/* Back-off exponentially if last interval expired */
thrdd->addr->poll_interval *= 2;
if(thrdd->addr->poll_interval > 250)
thrdd->addr->poll_interval = 250;
thrdd->addr->interval_end = elapsed + thrdd->addr->poll_interval;
Curl_expire(data, thrdd->addr->poll_interval, EXPIRE_ASYNC_NAME);
return CURLE_OK;
}
}
CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps)
{
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
CURLcode result = CURLE_OK;
bool thrd_done;
#if !defined(USE_HTTPSRR_ARES) && defined(CURL_DISABLE_SOCKETPAIR)
(void)ps;
#endif
#ifdef USE_HTTPSRR_ARES
if(thrdd->rr.channel) {
result = Curl_ares_pollset(data, thrdd->rr.channel, ps);
if(result)
return result;
}
#endif
if(!thrdd->addr)
return result;
Curl_mutex_acquire(&thrdd->addr->mutx);
thrd_done = thrdd->addr->thrd_done;
Curl_mutex_release(&thrdd->addr->mutx);
if(!thrd_done) {
#ifndef CURL_DISABLE_SOCKETPAIR
/* return read fd to client for polling the DNS resolution status */
result = Curl_pollset_add_in(data, ps, thrdd->addr->sock_pair[0]);
#else
timediff_t milli;
timediff_t ms = curlx_timediff(curlx_now(), thrdd->addr->start);
if(ms < 3)
milli = 0;
else if(ms <= 50)
milli = ms/3;
else if(ms <= 250)
milli = 50;
else
milli = 200;
Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
#endif
}
return result;
}
#ifndef HAVE_GETADDRINFO
/*
* Curl_async_getaddrinfo() - for platforms without getaddrinfo
*/
struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
const char *hostname,
int port,
int ip_version,
int *waitp)
{
(void)ip_version;
*waitp = 0; /* default to synchronous response */
/* fire up a new resolver thread! */
if(async_thrdd_init(data, hostname, port, ip_version, NULL)) {
*waitp = 1; /* expect asynchronous response */
return NULL;
}
failf(data, "getaddrinfo() thread failed");
return NULL;
}
#else /* !HAVE_GETADDRINFO */
/*
* Curl_async_getaddrinfo() - for getaddrinfo
*/
struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
const char *hostname,
int port,
int ip_version,
int *waitp)
{
struct addrinfo hints;
int pf = PF_INET;
*waitp = 0; /* default to synchronous response */
CURL_TRC_DNS(data, "init threaded resolve of %s:%d", hostname, port);
#ifdef CURLRES_IPV6
if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
/* The stack seems to be IPv6-enabled */
if(ip_version == CURL_IPRESOLVE_V6)
pf = PF_INET6;
else
pf = PF_UNSPEC;
}
#else
(void)ip_version;
#endif /* CURLRES_IPV6 */
memset(&hints, 0, sizeof(hints));
hints.ai_family = pf;
hints.ai_socktype =
(Curl_conn_get_transport(data, data->conn) == TRNSPRT_TCP) ?
SOCK_STREAM : SOCK_DGRAM;
/* fire up a new resolver thread! */
if(async_thrdd_init(data, hostname, port, ip_version, &hints)) {
*waitp = 1; /* expect asynchronous response */
return NULL;
}
failf(data, "getaddrinfo() thread failed to start");
return NULL;
}
#endif /* !HAVE_GETADDRINFO */
#endif /* CURLRES_THREADED */
+278
View File
@@ -0,0 +1,278 @@
#ifndef HEADER_CURL_ASYN_H
#define HEADER_CURL_ASYN_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
struct Curl_easy;
struct Curl_dns_entry;
#ifdef CURLRES_ASYNCH
#include "curl_addrinfo.h"
#include "httpsrr.h"
struct addrinfo;
struct hostent;
struct connectdata;
struct easy_pollset;
#if defined(CURLRES_ARES) && defined(CURLRES_THREADED)
#error cannot have both CURLRES_ARES and CURLRES_THREADED defined
#endif
/*
* This header defines all functions in the internal asynch resolver interface.
* All asynch resolvers need to provide these functions.
* asyn-ares.c and asyn-thread.c are the current implementations of asynch
* resolver backends.
*/
/*
* Curl_async_global_init()
*
* Called from curl_global_init() to initialize global resolver environment.
* Returning anything else than CURLE_OK fails curl_global_init().
*/
int Curl_async_global_init(void);
/*
* Curl_async_global_cleanup()
* Called from curl_global_cleanup() to destroy global resolver environment.
*/
void Curl_async_global_cleanup(void);
/*
* Curl_async_get_impl()
* Get the resolver implementation instance (c-ares channel) or NULL
* for passing to application callback.
*/
CURLcode Curl_async_get_impl(struct Curl_easy *easy, void **impl);
/* Curl_async_pollset()
*
* This function is called from the Curl_multi_pollset() function. 'sock' is a
* pointer to an array to hold the file descriptors, with 'numsock' being the
* size of that array (in number of entries). This function is supposed to
* return bitmask indicating what file descriptors (referring to array indexes
* in the 'sock' array) to wait for, read/write.
*/
CURLcode Curl_async_pollset(struct Curl_easy *data, struct easy_pollset *ps);
/*
* Curl_async_is_resolved()
*
* Called repeatedly to check if a previous name resolve request has
* completed. It should also make sure to time-out if the operation seems to
* take too long.
*
* Returns normal CURLcode errors.
*/
CURLcode Curl_async_is_resolved(struct Curl_easy *data,
struct Curl_dns_entry **dns);
/*
* Curl_async_await()
*
* Waits for a resolve to finish. This function should be avoided since using
* this risk getting the multi interface to "hang".
*
* On return 'entry' is assigned the resolved dns (CURLE_OK or NULL otherwise.
*
* Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
* CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
*/
CURLcode Curl_async_await(struct Curl_easy *data,
struct Curl_dns_entry **dnsentry);
/*
* Curl_async_getaddrinfo() - when using this resolver
*
* Returns name information about the given hostname and port number. If
* successful, the 'hostent' is returned and the fourth argument will point to
* memory we need to free after use. That memory *MUST* be freed with
* Curl_freeaddrinfo(), nothing else.
*
* Each resolver backend must of course make sure to return data in the
* correct format to comply with this.
*/
struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
const char *hostname,
int port,
int ip_version,
int *waitp);
#ifdef USE_ARES
/* common functions for c-ares and threaded resolver with HTTPSRR */
#include <ares.h>
CURLcode Curl_ares_pollset(struct Curl_easy *data,
ares_channel channel,
struct easy_pollset *ps);
int Curl_ares_perform(ares_channel channel,
timediff_t timeout_ms);
#endif
#ifdef CURLRES_ARES
/* async resolving implementation using c-ares alone */
struct async_ares_ctx {
ares_channel channel;
int num_pending; /* number of outstanding c-ares requests */
struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
parts */
int ares_status; /* ARES_SUCCESS, ARES_ENOTFOUND, etc. */
CURLcode result; /* CURLE_OK or error handling response */
#ifndef HAVE_CARES_GETADDRINFO
struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
#endif
#ifdef USE_HTTPSRR
struct Curl_https_rrinfo hinfo;
#endif
};
void Curl_async_ares_shutdown(struct Curl_easy *data);
void Curl_async_ares_destroy(struct Curl_easy *data);
/* Set the DNS server to use by ares, from `data` settings. */
CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data);
/* Set the DNS interfacer to use by ares, from `data` settings. */
CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data);
/* Set the local ipv4 address to use by ares, from `data` settings. */
CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data);
/* Set the local ipv6 address to use by ares, from `data` settings. */
CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data);
#endif /* CURLRES_ARES */
#ifdef CURLRES_THREADED
/* async resolving implementation using POSIX threads */
#include "curl_threads.h"
/* Context for threaded address resolver */
struct async_thrdd_addr_ctx {
curl_thread_t thread_hnd;
char *hostname; /* hostname to resolve, Curl_async.hostname
duplicate */
curl_mutex_t mutx;
#ifndef CURL_DISABLE_SOCKETPAIR
curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */
#endif
struct Curl_addrinfo *res;
#ifdef HAVE_GETADDRINFO
struct addrinfo hints;
#endif
struct curltime start;
timediff_t interval_end;
unsigned int poll_interval;
int port;
int sock_error;
int ref_count;
BIT(thrd_done);
BIT(do_abort);
};
/* Context for threaded resolver */
struct async_thrdd_ctx {
/* `addr` is a pointer since this memory is shared with a started
* thread. Since threads cannot be killed, we use reference counting
* so that we can "release" our pointer to this memory while the
* thread is still running. */
struct async_thrdd_addr_ctx *addr;
#if defined(USE_HTTPSRR) && defined(USE_ARES)
struct {
ares_channel channel;
struct Curl_https_rrinfo hinfo;
CURLcode result;
BIT(done);
} rr;
#endif
};
void Curl_async_thrdd_shutdown(struct Curl_easy *data);
void Curl_async_thrdd_destroy(struct Curl_easy *data);
#endif /* CURLRES_THREADED */
#ifndef CURL_DISABLE_DOH
struct doh_probes;
#endif
#else /* CURLRES_ASYNCH */
/* convert these functions if an asynch resolver is not used */
#define Curl_async_get_impl(x,y) (*(y) = NULL, CURLE_OK)
#define Curl_async_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
#define Curl_async_await(x,y) CURLE_COULDNT_RESOLVE_HOST
#define Curl_async_global_init() CURLE_OK
#define Curl_async_global_cleanup() Curl_nop_stmt
#endif /* !CURLRES_ASYNCH */
#if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH)
#define USE_CURL_ASYNC
#endif
#ifdef USE_CURL_ASYNC
struct Curl_async {
#ifdef CURLRES_ARES
struct async_ares_ctx ares;
#elif defined(CURLRES_THREADED)
struct async_thrdd_ctx thrdd;
#endif
#ifndef CURL_DISABLE_DOH
struct doh_probes *doh; /* DoH specific data for this request */
#endif
struct Curl_dns_entry *dns; /* result of resolving on success */
char *hostname; /* copy of the params resolv started with */
int port;
int ip_version;
BIT(done);
};
/*
* Curl_async_shutdown().
*
* This shuts down all ongoing operations.
*/
void Curl_async_shutdown(struct Curl_easy *data);
/*
* Curl_async_destroy().
*
* This frees the resources of any async resolve.
*/
void Curl_async_destroy(struct Curl_easy *data);
#else /* !USE_CURL_ASYNC */
#define Curl_async_shutdown(x) Curl_nop_stmt
#define Curl_async_destroy(x) Curl_nop_stmt
#endif /* USE_CURL_ASYNC */
/********** end of generic resolver interface functions *****************/
#endif /* HEADER_CURL_ASYN_H */
+624
View File
@@ -0,0 +1,624 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include "bufq.h"
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
static bool chunk_is_empty(const struct buf_chunk *chunk)
{
return chunk->r_offset >= chunk->w_offset;
}
static bool chunk_is_full(const struct buf_chunk *chunk)
{
return chunk->w_offset >= chunk->dlen;
}
static size_t chunk_len(const struct buf_chunk *chunk)
{
return chunk->w_offset - chunk->r_offset;
}
static void chunk_reset(struct buf_chunk *chunk)
{
chunk->next = NULL;
chunk->r_offset = chunk->w_offset = 0;
}
static size_t chunk_append(struct buf_chunk *chunk,
const unsigned char *buf, size_t len)
{
unsigned char *p = &chunk->x.data[chunk->w_offset];
size_t n = chunk->dlen - chunk->w_offset;
DEBUGASSERT(chunk->dlen >= chunk->w_offset);
if(n) {
n = CURLMIN(n, len);
memcpy(p, buf, n);
chunk->w_offset += n;
}
return n;
}
static size_t chunk_read(struct buf_chunk *chunk,
unsigned char *buf, size_t len)
{
unsigned char *p = &chunk->x.data[chunk->r_offset];
size_t n = chunk->w_offset - chunk->r_offset;
DEBUGASSERT(chunk->w_offset >= chunk->r_offset);
if(!n) {
return 0;
}
else if(n <= len) {
memcpy(buf, p, n);
chunk->r_offset = chunk->w_offset = 0;
return n;
}
else {
memcpy(buf, p, len);
chunk->r_offset += len;
return len;
}
}
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 */
CURLcode result;
*pnread = 0;
DEBUGASSERT(chunk->dlen >= chunk->w_offset);
if(!n)
return CURLE_AGAIN;
if(max_len && n > max_len)
n = max_len;
result = reader(reader_ctx, p, n, pnread);
if(!result) {
DEBUGASSERT(*pnread <= n);
chunk->w_offset += *pnread;
}
return result;
}
static void chunk_peek(const struct buf_chunk *chunk,
const unsigned char **pbuf, size_t *plen)
{
DEBUGASSERT(chunk->w_offset >= chunk->r_offset);
*pbuf = &chunk->x.data[chunk->r_offset];
*plen = chunk->w_offset - chunk->r_offset;
}
static void chunk_peek_at(const struct buf_chunk *chunk, size_t offset,
const unsigned char **pbuf, size_t *plen)
{
offset += chunk->r_offset;
DEBUGASSERT(chunk->w_offset >= offset);
*pbuf = &chunk->x.data[offset];
*plen = chunk->w_offset - offset;
}
static size_t chunk_skip(struct buf_chunk *chunk, size_t amount)
{
size_t n = chunk->w_offset - chunk->r_offset;
DEBUGASSERT(chunk->w_offset >= chunk->r_offset);
if(n) {
n = CURLMIN(n, amount);
chunk->r_offset += n;
if(chunk->r_offset == chunk->w_offset)
chunk->r_offset = chunk->w_offset = 0;
}
return n;
}
static void chunk_list_free(struct buf_chunk **anchor)
{
struct buf_chunk *chunk;
while(*anchor) {
chunk = *anchor;
*anchor = chunk->next;
free(chunk);
}
}
void Curl_bufcp_init(struct bufc_pool *pool,
size_t chunk_size, size_t spare_max)
{
DEBUGASSERT(chunk_size > 0);
DEBUGASSERT(spare_max > 0);
memset(pool, 0, sizeof(*pool));
pool->chunk_size = chunk_size;
pool->spare_max = spare_max;
}
static CURLcode bufcp_take(struct bufc_pool *pool,
struct buf_chunk **pchunk)
{
struct buf_chunk *chunk = NULL;
if(pool->spare) {
chunk = pool->spare;
pool->spare = chunk->next;
--pool->spare_count;
chunk_reset(chunk);
*pchunk = chunk;
return CURLE_OK;
}
/* Check for integer overflow before allocation */
if(pool->chunk_size > SIZE_MAX - sizeof(*chunk)) {
*pchunk = NULL;
return CURLE_OUT_OF_MEMORY;
}
chunk = calloc(1, sizeof(*chunk) + pool->chunk_size);
if(!chunk) {
*pchunk = NULL;
return CURLE_OUT_OF_MEMORY;
}
chunk->dlen = pool->chunk_size;
*pchunk = chunk;
return CURLE_OK;
}
static void bufcp_put(struct bufc_pool *pool,
struct buf_chunk *chunk)
{
if(pool->spare_count >= pool->spare_max) {
free(chunk);
}
else {
chunk_reset(chunk);
chunk->next = pool->spare;
pool->spare = chunk;
++pool->spare_count;
}
}
void Curl_bufcp_free(struct bufc_pool *pool)
{
chunk_list_free(&pool->spare);
pool->spare_count = 0;
}
static void bufq_init(struct bufq *q, struct bufc_pool *pool,
size_t chunk_size, size_t max_chunks, int opts)
{
DEBUGASSERT(chunk_size > 0);
DEBUGASSERT(max_chunks > 0);
memset(q, 0, sizeof(*q));
q->chunk_size = chunk_size;
q->max_chunks = max_chunks;
q->pool = pool;
q->opts = opts;
}
void Curl_bufq_init2(struct bufq *q, size_t chunk_size, size_t max_chunks,
int opts)
{
bufq_init(q, NULL, chunk_size, max_chunks, opts);
}
void Curl_bufq_init(struct bufq *q, size_t chunk_size, size_t max_chunks)
{
bufq_init(q, NULL, chunk_size, max_chunks, BUFQ_OPT_NONE);
}
void Curl_bufq_initp(struct bufq *q, struct bufc_pool *pool,
size_t max_chunks, int opts)
{
bufq_init(q, pool, pool->chunk_size, max_chunks, opts);
}
void Curl_bufq_free(struct bufq *q)
{
chunk_list_free(&q->head);
chunk_list_free(&q->spare);
q->tail = NULL;
q->chunk_count = 0;
}
void Curl_bufq_reset(struct bufq *q)
{
struct buf_chunk *chunk;
while(q->head) {
chunk = q->head;
q->head = chunk->next;
chunk->next = q->spare;
q->spare = chunk;
}
q->tail = NULL;
}
size_t Curl_bufq_len(const struct bufq *q)
{
const struct buf_chunk *chunk = q->head;
size_t len = 0;
while(chunk) {
len += chunk_len(chunk);
chunk = chunk->next;
}
return len;
}
bool Curl_bufq_is_empty(const struct bufq *q)
{
return !q->head || chunk_is_empty(q->head);
}
bool Curl_bufq_is_full(const struct bufq *q)
{
if(!q->tail || q->spare)
return FALSE;
if(q->chunk_count < q->max_chunks)
return FALSE;
if(q->chunk_count > q->max_chunks)
return TRUE;
/* we have no spares and cannot make more, is the tail full? */
return chunk_is_full(q->tail);
}
static struct buf_chunk *get_spare(struct bufq *q)
{
struct buf_chunk *chunk = NULL;
if(q->spare) {
chunk = q->spare;
q->spare = chunk->next;
chunk_reset(chunk);
return chunk;
}
if(q->chunk_count >= q->max_chunks && (!(q->opts & BUFQ_OPT_SOFT_LIMIT)))
return NULL;
if(q->pool) {
if(bufcp_take(q->pool, &chunk))
return NULL;
++q->chunk_count;
return chunk;
}
else {
/* Check for integer overflow before allocation */
if(q->chunk_size > SIZE_MAX - sizeof(*chunk)) {
return NULL;
}
chunk = calloc(1, sizeof(*chunk) + q->chunk_size);
if(!chunk)
return NULL;
chunk->dlen = q->chunk_size;
++q->chunk_count;
return chunk;
}
}
static void prune_head(struct bufq *q)
{
struct buf_chunk *chunk;
while(q->head && chunk_is_empty(q->head)) {
chunk = q->head;
q->head = chunk->next;
if(q->tail == chunk)
q->tail = q->head;
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;
if(q->tail && !chunk_is_full(q->tail))
return q->tail;
chunk = get_spare(q);
if(chunk) {
/* new tail, and possibly new head */
if(q->tail) {
q->tail->next = chunk;
q->tail = chunk;
}
else {
DEBUGASSERT(!q->head);
q->head = q->tail = chunk;
}
}
return chunk;
}
CURLcode Curl_bufq_write(struct bufq *q,
const unsigned char *buf, size_t len,
size_t *pnwritten)
{
struct buf_chunk *tail;
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))
/* should have gotten a tail, but did not */
return CURLE_OUT_OF_MEMORY;
break;
}
n = chunk_append(tail, buf, len);
if(!n)
break;
*pnwritten += n;
buf += n;
len -= n;
}
return (!*pnwritten && len) ? CURLE_AGAIN : CURLE_OK;
}
CURLcode Curl_bufq_cwrite(struct bufq *q,
const char *buf, size_t len,
size_t *pnwritten)
{
return Curl_bufq_write(q, (const unsigned char *)buf, len, pnwritten);
}
CURLcode Curl_bufq_read(struct bufq *q, unsigned char *buf, size_t len,
size_t *pnread)
{
*pnread = 0;
while(len && q->head) {
size_t n = chunk_read(q->head, buf, len);
if(n) {
*pnread += n;
buf += n;
len -= n;
}
prune_head(q);
}
return (!*pnread) ? CURLE_AGAIN : CURLE_OK;
}
CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len,
size_t *pnread)
{
return Curl_bufq_read(q, (unsigned char *)buf, len, pnread);
}
bool Curl_bufq_peek(struct bufq *q,
const unsigned char **pbuf, size_t *plen)
{
if(q->head && chunk_is_empty(q->head)) {
prune_head(q);
}
if(q->head && !chunk_is_empty(q->head)) {
chunk_peek(q->head, pbuf, plen);
return TRUE;
}
*pbuf = NULL;
*plen = 0;
return FALSE;
}
bool Curl_bufq_peek_at(struct bufq *q, size_t offset,
const unsigned char **pbuf, size_t *plen)
{
struct buf_chunk *c = q->head;
size_t clen;
while(c) {
clen = chunk_len(c);
if(!clen)
break;
if(offset >= clen) {
offset -= clen;
c = c->next;
continue;
}
chunk_peek_at(c, offset, pbuf, plen);
return TRUE;
}
*pbuf = NULL;
*plen = 0;
return FALSE;
}
void Curl_bufq_skip(struct bufq *q, size_t amount)
{
size_t n;
while(amount && q->head) {
n = chunk_skip(q->head, amount);
amount -= n;
prune_head(q);
}
}
CURLcode Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer,
void *writer_ctx, size_t *pwritten)
{
const unsigned char *buf;
size_t blen;
CURLcode result = CURLE_OK;
*pwritten = 0;
while(Curl_bufq_peek(q, &buf, &blen)) {
size_t chunk_written;
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(!*pwritten) {
/* treat as blocked */
result = CURLE_AGAIN;
}
break;
}
*pwritten += chunk_written;
Curl_bufq_skip(q, chunk_written);
}
return result;
}
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)
{
CURLcode result = CURLE_OK;
size_t n;
*pwritten = 0;
while(len) {
if(Curl_bufq_is_full(q)) {
/* try to make room in case we are full */
result = Curl_bufq_pass(q, writer, writer_ctx, &n);
if(result) {
if(result != CURLE_AGAIN) {
/* real error, fail */
return result;
}
/* would block, bufq is full, give up */
break;
}
}
/* 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 result;
/* result == CURLE_AGAIN */
if(*pwritten)
/* we did write successfully before */
result = CURLE_OK;
return result;
}
else if(n == 0)
/* edge case of writer returning 0 (and len is >0)
* break or we might enter an infinite loop here */
break;
/* Track what we added to bufq */
buf += n;
len -= n;
*pwritten += n;
}
return (!*pwritten && len) ? CURLE_AGAIN : CURLE_OK;
}
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;
*pnread = 0;
tail = get_non_full_tail(q);
if(!tail) {
if(q->chunk_count < q->max_chunks)
return CURLE_OUT_OF_MEMORY;
/* full, blocked */
return CURLE_AGAIN;
}
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) 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 CURLcode bufq_slurpn(struct bufq *q, size_t max_len,
Curl_bufq_reader *reader, void *reader_ctx,
size_t *pnread)
{
CURLcode result;
*pnread = 0;
while(1) {
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 */
return result;
}
result = CURLE_OK;
break;
}
else if(n == 0) {
/* eof, result remains CURLE_OK */
break;
}
*pnread += n;
if(max_len) {
DEBUGASSERT(n <= max_len);
max_len -= n;
if(!max_len)
break;
}
/* give up slurping when we get less bytes than we asked for */
if(q->tail && !chunk_is_full(q->tail))
break;
}
return result;
}
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, pnread);
}
+260
View File
@@ -0,0 +1,260 @@
#ifndef HEADER_CURL_BUFQ_H
#define HEADER_CURL_BUFQ_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include <curl/curl.h>
/**
* A chunk of bytes for reading and writing.
* The size is fixed a creation with read and write offset
* for where unread content is.
*/
struct buf_chunk {
struct buf_chunk *next; /* to keep it in a list */
size_t dlen; /* the amount of allocated x.data[] */
size_t r_offset; /* first unread bytes */
size_t w_offset; /* one after last written byte */
union {
unsigned char data[1]; /* the buffer for `dlen` bytes */
void *dummy; /* alignment */
} x;
};
/**
* A pool for providing/keeping a number of chunks of the same size
*
* The same pool can be shared by many `bufq` instances. However, a pool
* is not thread safe. All bufqs using it are supposed to operate in the
* same thread.
*/
struct bufc_pool {
struct buf_chunk *spare; /* list of available spare chunks */
size_t chunk_size; /* the size of chunks in this pool */
size_t spare_count; /* current number of spare chunks in list */
size_t spare_max; /* max number of spares to keep */
};
void Curl_bufcp_init(struct bufc_pool *pool,
size_t chunk_size, size_t spare_max);
void Curl_bufcp_free(struct bufc_pool *pool);
/**
* A queue of byte chunks for reading and writing.
* Reading is done from `head`, writing is done to `tail`.
*
* `bufq`s can be empty or full or neither. Its `len` is the number
* of bytes that can be read. For an empty bufq, `len` will be 0.
*
* By default, a bufq can hold up to `max_chunks * chunk_size` number
* of bytes. When `max_chunks` are used (in the `head` list) and the
* `tail` chunk is full, the bufq will report that it is full.
*
* On a full bufq, `len` may be less than the maximum number of bytes,
* e.g. when the head chunk is partially read. `len` may also become
* larger than the max when option `BUFQ_OPT_SOFT_LIMIT` is used.
*
* By default, writing to a full bufq will return (-1, CURLE_AGAIN). Same
* as reading from an empty bufq.
* With `BUFQ_OPT_SOFT_LIMIT` set, a bufq will allow writing becond this
* limit and use more than `max_chunks`. However it will report that it
* is full nevertheless. This is provided for situation where writes
* preferably never fail (except for memory exhaustion).
*
* By default and without a pool, a bufq will keep chunks that read
* empty in its `spare` list. Option `BUFQ_OPT_NO_SPARES` will
* disable that and free chunks once they become empty.
*
* When providing a pool to a bufq, all chunk creation and spare handling
* will be delegated to that pool.
*/
struct bufq {
struct buf_chunk *head; /* chunk with bytes to read from */
struct buf_chunk *tail; /* chunk to write to */
struct buf_chunk *spare; /* list of free chunks, unless `pool` */
struct bufc_pool *pool; /* optional pool for free chunks */
size_t chunk_count; /* current number of chunks in `head+spare` */
size_t max_chunks; /* max `head` chunks to use */
size_t chunk_size; /* size of chunks to manage */
int opts; /* options for handling queue, see below */
};
/**
* Default behaviour: chunk limit is "hard", meaning attempts to write
* more bytes than can be hold in `max_chunks` is refused and will return
* -1, CURLE_AGAIN. */
#define BUFQ_OPT_NONE (0)
/**
* Make `max_chunks` a "soft" limit. A bufq will report that it is "full"
* when `max_chunks` are used, but allows writing beyond this limit.
*/
#define BUFQ_OPT_SOFT_LIMIT (1 << 0)
/**
* Do not keep spare chunks.
*/
#define BUFQ_OPT_NO_SPARES (1 << 1)
/**
* Initialize a buffer queue that can hold up to `max_chunks` buffers
* each of size `chunk_size`. The bufq will not allow writing of
* more bytes than can be held in `max_chunks`.
*/
void Curl_bufq_init(struct bufq *q, size_t chunk_size, size_t max_chunks);
/**
* Initialize a buffer queue that can hold up to `max_chunks` buffers
* each of size `chunk_size` with the given options. See `BUFQ_OPT_*`.
*/
void Curl_bufq_init2(struct bufq *q, size_t chunk_size,
size_t max_chunks, int opts);
void Curl_bufq_initp(struct bufq *q, struct bufc_pool *pool,
size_t max_chunks, int opts);
/**
* Reset the buffer queue to be empty. Will keep any allocated buffer
* chunks around.
*/
void Curl_bufq_reset(struct bufq *q);
/**
* Free all resources held by the buffer queue.
*/
void Curl_bufq_free(struct bufq *q);
/**
* Return the total amount of data in the queue.
*/
size_t Curl_bufq_len(const struct bufq *q);
/**
* Returns TRUE iff there is no data in the buffer queue.
*/
bool Curl_bufq_is_empty(const struct bufq *q);
/**
* Returns TRUE iff there is no space left in the buffer queue.
*/
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.
* CURLE_AGAIN is returned if the buffer queue is full.
*/
CURLcode Curl_bufq_write(struct bufq *q,
const unsigned char *buf, size_t len,
size_t *pnwritten);
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.
*/
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);
/**
* Peek at the head chunk in the buffer queue. Returns a pointer to
* the chunk buf (at the current offset) and its length. Does not
* modify the buffer queue.
* Returns TRUE iff bytes are available. Sets `pbuf` to NULL and `plen`
* to 0 when no bytes are available.
* Repeated calls return the same information until the buffer queue
* is modified, see `Curl_bufq_skip()``
*/
bool Curl_bufq_peek(struct bufq *q,
const unsigned char **pbuf, size_t *plen);
bool Curl_bufq_peek_at(struct bufq *q, size_t offset,
const unsigned char **pbuf, size_t *plen);
/**
* Tell the buffer queue to discard `amount` buf bytes at the head
* of the queue. Skipping more buf than is currently buffered will
* just empty the queue.
*/
void Curl_bufq_skip(struct bufq *q, size_t amount);
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
* to indicate blocking at which point the queue will stop and return
* the amount of buf passed so far.
* -1 is returned on any other errors reported by the writer.
* Note that in case of a -1 chunks may have been written and
* the buffer queue will have different length than before.
*/
CURLcode Curl_bufq_pass(struct bufq *q, Curl_bufq_writer *writer,
void *writer_ctx, size_t *pwritten);
typedef CURLcode Curl_bufq_reader(void *reader_ctx,
unsigned char *buf, size_t len,
size_t *pnread);
/**
* Read bytes and append them to the end of the buffer queue until the
* reader returns blocking or the queue is full. A reader returns
* 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.
*/
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.
* if `max_len` is 0, no limit is imposed besides the chunk space.
* Returns the total amount of buf read (may be 0) or -1 on other
* reader errors.
*/
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.
* Will write bufq content or passed `buf` directly using the `writer`
* callback when it sees fit. 'buf' might get passed directly
* on or is placed into the buffer, depending on `len` and current
* amount buffered, chunk size, etc.
*/
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 */
+129
View File
@@ -0,0 +1,129 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include "urldata.h"
#include "bufref.h"
#include "strdup.h"
#include "curl_memory.h"
#include "memdebug.h"
#ifdef DEBUGBUILD
#define SIGNATURE 0x5c48e9b2 /* Random pattern. */
#endif
/*
* Init a bufref struct.
*/
void Curl_bufref_init(struct bufref *br)
{
DEBUGASSERT(br);
br->dtor = NULL;
br->ptr = NULL;
br->len = 0;
#ifdef DEBUGBUILD
br->signature = SIGNATURE;
#endif
}
/*
* Free the buffer and re-init the necessary fields. It does not touch the
* 'signature' field and thus this buffer reference can be reused.
*/
void Curl_bufref_free(struct bufref *br)
{
DEBUGASSERT(br);
DEBUGASSERT(br->signature == SIGNATURE);
DEBUGASSERT(br->ptr || !br->len);
if(br->ptr && br->dtor)
br->dtor(CURL_UNCONST(br->ptr));
br->dtor = NULL;
br->ptr = NULL;
br->len = 0;
}
/*
* Set the buffer reference to new values. The previously referenced buffer
* is released before assignment.
*/
void Curl_bufref_set(struct bufref *br, const void *ptr, size_t len,
void (*dtor)(void *))
{
DEBUGASSERT(ptr || !len);
DEBUGASSERT(len <= CURL_MAX_INPUT_LENGTH);
Curl_bufref_free(br);
br->ptr = (const unsigned char *) ptr;
br->len = len;
br->dtor = dtor;
}
/*
* Get a pointer to the referenced buffer.
*/
const unsigned char *Curl_bufref_ptr(const struct bufref *br)
{
DEBUGASSERT(br);
DEBUGASSERT(br->signature == SIGNATURE);
DEBUGASSERT(br->ptr || !br->len);
return br->ptr;
}
/*
* Get the length of the referenced buffer data.
*/
size_t Curl_bufref_len(const struct bufref *br)
{
DEBUGASSERT(br);
DEBUGASSERT(br->signature == SIGNATURE);
DEBUGASSERT(br->ptr || !br->len);
return br->len;
}
CURLcode Curl_bufref_memdup(struct bufref *br, const void *ptr, size_t len)
{
unsigned char *cpy = NULL;
DEBUGASSERT(br);
DEBUGASSERT(br->signature == SIGNATURE);
DEBUGASSERT(br->ptr || !br->len);
DEBUGASSERT(ptr || !len);
DEBUGASSERT(len <= CURL_MAX_INPUT_LENGTH);
if(ptr) {
cpy = Curl_memdup0(ptr, len);
if(!cpy)
return CURLE_OUT_OF_MEMORY;
}
Curl_bufref_set(br, cpy, len, curl_free);
return CURLE_OK;
}
+48
View File
@@ -0,0 +1,48 @@
#ifndef HEADER_CURL_BUFREF_H
#define HEADER_CURL_BUFREF_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
/*
* Generic buffer reference.
*/
struct bufref {
void (*dtor)(void *); /* Associated destructor. */
const unsigned char *ptr; /* Referenced data buffer. */
size_t len; /* The data size in bytes. */
#ifdef DEBUGBUILD
int signature; /* Detect API use mistakes. */
#endif
};
void Curl_bufref_init(struct bufref *br);
void Curl_bufref_set(struct bufref *br, const void *ptr, size_t len,
void (*dtor)(void *));
const unsigned char *Curl_bufref_ptr(const struct bufref *br);
size_t Curl_bufref_len(const struct bufref *br);
CURLcode Curl_bufref_memdup(struct bufref *br, const void *ptr, size_t len);
void Curl_bufref_free(struct bufref *br);
#endif
+764
View File
@@ -0,0 +1,764 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
#include <curl/curl.h>
#include "urldata.h"
#include "curlx/dynbuf.h"
#include "sendf.h"
#include "http.h"
#include "http1.h"
#include "http_proxy.h"
#include "url.h"
#include "select.h"
#include "progress.h"
#include "cfilters.h"
#include "cf-h1-proxy.h"
#include "connect.h"
#include "curl_trc.h"
#include "strcase.h"
#include "vtls/vtls.h"
#include "transfer.h"
#include "multiif.h"
#include "curlx/strparse.h"
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
typedef enum {
H1_TUNNEL_INIT, /* init/default/no tunnel state */
H1_TUNNEL_CONNECT, /* CONNECT request is being send */
H1_TUNNEL_RECEIVE, /* CONNECT answer is being received */
H1_TUNNEL_RESPONSE, /* CONNECT response received completely */
H1_TUNNEL_ESTABLISHED,
H1_TUNNEL_FAILED
} h1_tunnel_state;
/* struct for HTTP CONNECT tunneling */
struct h1_tunnel_state {
struct dynbuf rcvbuf;
struct dynbuf request_data;
size_t nsent;
size_t headerlines;
struct Curl_chunker ch;
enum keeponval {
KEEPON_DONE,
KEEPON_CONNECT,
KEEPON_IGNORE
} keepon;
curl_off_t cl; /* size of content to read and ignore */
h1_tunnel_state tunnel_state;
BIT(chunked_encoding);
BIT(close_connection);
};
static bool tunnel_is_established(struct h1_tunnel_state *ts)
{
return ts && (ts->tunnel_state == H1_TUNNEL_ESTABLISHED);
}
static bool tunnel_is_failed(struct h1_tunnel_state *ts)
{
return ts && (ts->tunnel_state == H1_TUNNEL_FAILED);
}
static CURLcode tunnel_reinit(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct h1_tunnel_state *ts)
{
(void)data;
(void)cf;
DEBUGASSERT(ts);
curlx_dyn_reset(&ts->rcvbuf);
curlx_dyn_reset(&ts->request_data);
ts->tunnel_state = H1_TUNNEL_INIT;
ts->keepon = KEEPON_CONNECT;
ts->cl = 0;
ts->close_connection = FALSE;
return CURLE_OK;
}
static CURLcode tunnel_init(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct h1_tunnel_state **pts)
{
struct h1_tunnel_state *ts;
if(cf->conn->handler->flags & PROTOPT_NOTCPPROXY) {
failf(data, "%s cannot be done over CONNECT", cf->conn->handler->scheme);
return CURLE_UNSUPPORTED_PROTOCOL;
}
ts = calloc(1, sizeof(*ts));
if(!ts)
return CURLE_OUT_OF_MEMORY;
infof(data, "allocate connect buffer");
curlx_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
curlx_dyn_init(&ts->request_data, DYN_HTTP_REQUEST);
Curl_httpchunk_init(data, &ts->ch, TRUE);
*pts = ts;
connkeep(cf->conn, "HTTP proxy CONNECT");
return tunnel_reinit(cf, data, ts);
}
static void h1_tunnel_go_state(struct Curl_cfilter *cf,
struct h1_tunnel_state *ts,
h1_tunnel_state new_state,
struct Curl_easy *data)
{
if(ts->tunnel_state == new_state)
return;
/* entering this one */
switch(new_state) {
case H1_TUNNEL_INIT:
CURL_TRC_CF(data, cf, "new tunnel state 'init'");
tunnel_reinit(cf, data, ts);
break;
case H1_TUNNEL_CONNECT:
CURL_TRC_CF(data, cf, "new tunnel state 'connect'");
ts->tunnel_state = H1_TUNNEL_CONNECT;
ts->keepon = KEEPON_CONNECT;
curlx_dyn_reset(&ts->rcvbuf);
break;
case H1_TUNNEL_RECEIVE:
CURL_TRC_CF(data, cf, "new tunnel state 'receive'");
ts->tunnel_state = H1_TUNNEL_RECEIVE;
break;
case H1_TUNNEL_RESPONSE:
CURL_TRC_CF(data, cf, "new tunnel state 'response'");
ts->tunnel_state = H1_TUNNEL_RESPONSE;
break;
case H1_TUNNEL_ESTABLISHED:
CURL_TRC_CF(data, cf, "new tunnel state 'established'");
infof(data, "CONNECT phase completed");
data->state.authproxy.done = TRUE;
data->state.authproxy.multipass = FALSE;
FALLTHROUGH();
case H1_TUNNEL_FAILED:
if(new_state == H1_TUNNEL_FAILED)
CURL_TRC_CF(data, cf, "new tunnel state 'failed'");
ts->tunnel_state = new_state;
curlx_dyn_reset(&ts->rcvbuf);
curlx_dyn_reset(&ts->request_data);
/* restore the protocol pointer */
data->info.httpcode = 0; /* clear it as it might've been used for the
proxy */
/* If a proxy-authorization header was used for the proxy, then we should
make sure that it is not accidentally used for the document request
after we have connected. So let's free and clear it here. */
Curl_safefree(data->state.aptr.proxyuserpwd);
break;
}
}
static void tunnel_free(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
if(cf) {
struct h1_tunnel_state *ts = cf->ctx;
if(ts) {
h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
curlx_dyn_free(&ts->rcvbuf);
curlx_dyn_free(&ts->request_data);
Curl_httpchunk_free(data, &ts->ch);
free(ts);
cf->ctx = NULL;
}
}
}
static bool tunnel_want_send(struct h1_tunnel_state *ts)
{
return ts->tunnel_state == H1_TUNNEL_CONNECT;
}
static CURLcode start_CONNECT(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct h1_tunnel_state *ts)
{
struct httpreq *req = NULL;
int http_minor;
CURLcode result;
/* This only happens if we have looped here due to authentication
reasons, and we do not really use the newly cloned URL here
then. Just free() it. */
Curl_safefree(data->req.newurl);
result = Curl_http_proxy_create_CONNECT(&req, cf, data, 1);
if(result)
goto out;
infof(data, "Establish HTTP proxy tunnel to %s", req->authority);
curlx_dyn_reset(&ts->request_data);
ts->nsent = 0;
ts->headerlines = 0;
http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1;
result = Curl_h1_req_write_head(req, http_minor, &ts->request_data);
if(!result)
result = Curl_creader_set_null(data);
out:
if(result)
failf(data, "Failed sending CONNECT to proxy");
if(req)
Curl_http_req_free(req);
return result;
}
static CURLcode send_CONNECT(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct h1_tunnel_state *ts,
bool *done)
{
char *buf = curlx_dyn_ptr(&ts->request_data);
size_t request_len = curlx_dyn_len(&ts->request_data);
size_t blen = request_len;
CURLcode result = CURLE_OK;
size_t nwritten;
if(blen <= ts->nsent)
goto out; /* we are done */
blen -= ts->nsent;
buf += ts->nsent;
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 >= nwritten);
ts->nsent += nwritten;
Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)nwritten);
out:
if(result)
failf(data, "Failed sending CONNECT to proxy");
*done = (!result && (ts->nsent >= request_len));
return result;
}
static CURLcode on_resp_header(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct h1_tunnel_state *ts,
const char *header)
{
CURLcode result = CURLE_OK;
struct SingleRequest *k = &data->req;
(void)cf;
if((checkprefix("WWW-Authenticate:", header) &&
(401 == k->httpcode)) ||
(checkprefix("Proxy-authenticate:", header) &&
(407 == k->httpcode))) {
bool proxy = (k->httpcode == 407);
char *auth = Curl_copy_header_value(header);
if(!auth)
return CURLE_OUT_OF_MEMORY;
CURL_TRC_CF(data, cf, "CONNECT: fwd auth header '%s'", header);
result = Curl_http_input_auth(data, proxy, auth);
free(auth);
if(result)
return result;
}
else if(checkprefix("Content-Length:", header)) {
if(k->httpcode/100 == 2) {
/* A client MUST ignore any Content-Length or Transfer-Encoding
header fields received in a successful response to CONNECT.
"Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
infof(data, "Ignoring Content-Length in CONNECT %03d response",
k->httpcode);
}
else {
const char *p = header + strlen("Content-Length:");
if(curlx_str_numblanks(&p, &ts->cl)) {
failf(data, "Unsupported Content-Length value");
return CURLE_WEIRD_SERVER_REPLY;
}
}
}
else if(Curl_compareheader(header,
STRCONST("Connection:"), STRCONST("close")))
ts->close_connection = TRUE;
else if(checkprefix("Transfer-Encoding:", header)) {
if(k->httpcode/100 == 2) {
/* A client MUST ignore any Content-Length or Transfer-Encoding
header fields received in a successful response to CONNECT.
"Successful" described as: 2xx (Successful). RFC 7231 4.3.6 */
infof(data, "Ignoring Transfer-Encoding in "
"CONNECT %03d response", k->httpcode);
}
else if(Curl_compareheader(header,
STRCONST("Transfer-Encoding:"),
STRCONST("chunked"))) {
infof(data, "CONNECT responded chunked");
ts->chunked_encoding = TRUE;
/* reset our chunky engine */
Curl_httpchunk_reset(data, &ts->ch, TRUE);
}
}
else if(Curl_compareheader(header,
STRCONST("Proxy-Connection:"),
STRCONST("close")))
ts->close_connection = TRUE;
else if(!strncmp(header, "HTTP/1.", 7) &&
((header[7] == '0') || (header[7] == '1')) &&
(header[8] == ' ') &&
ISDIGIT(header[9]) && ISDIGIT(header[10]) && ISDIGIT(header[11]) &&
!ISDIGIT(header[12])) {
/* store the HTTP code from the proxy */
data->info.httpproxycode = k->httpcode = (header[9] - '0') * 100 +
(header[10] - '0') * 10 + (header[11] - '0');
}
return result;
}
static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct h1_tunnel_state *ts,
bool *done)
{
CURLcode result = CURLE_OK;
struct SingleRequest *k = &data->req;
char *linep;
size_t line_len;
int error, writetype;
#define SELECT_OK 0
#define SELECT_ERROR 1
error = SELECT_OK;
*done = FALSE;
while(ts->keepon) {
size_t nread;
char byte;
/* Read one byte at a time to avoid a race condition. Wait at most one
second before looping to ensure continuous pgrsUpdates. */
result = Curl_conn_recv(data, cf->sockindex, &byte, 1, &nread);
if(result == CURLE_AGAIN)
/* socket buffer drained, return */
return CURLE_OK;
if(Curl_pgrsUpdate(data))
return CURLE_ABORTED_BY_CALLBACK;
if(result) {
ts->keepon = KEEPON_DONE;
break;
}
if(!nread) {
if(data->set.proxyauth && data->state.authproxy.avail &&
data->state.aptr.proxyuserpwd) {
/* proxy auth was requested and there was proxy auth available,
then deem this as "mere" proxy disconnect */
ts->close_connection = TRUE;
infof(data, "Proxy CONNECT connection closed");
}
else {
error = SELECT_ERROR;
failf(data, "Proxy CONNECT aborted");
}
ts->keepon = KEEPON_DONE;
break;
}
if(ts->keepon == KEEPON_IGNORE) {
/* This means we are currently ignoring a response-body */
if(ts->cl) {
/* A Content-Length based body: simply count down the counter
and make sure to break out of the loop when we are done! */
ts->cl--;
if(ts->cl <= 0) {
ts->keepon = KEEPON_DONE;
break;
}
}
else if(ts->chunked_encoding) {
/* chunked-encoded body, so we need to do the chunked dance
properly to know when the end of the body is reached */
size_t consumed = 0;
/* now parse the chunked piece of data so that we can
properly tell when the stream ends */
result = Curl_httpchunk_read(data, &ts->ch, &byte, 1, &consumed);
if(result)
return result;
if(Curl_httpchunk_is_done(data, &ts->ch)) {
/* we are done reading chunks! */
infof(data, "chunk reading DONE");
ts->keepon = KEEPON_DONE;
}
}
continue;
}
if(curlx_dyn_addn(&ts->rcvbuf, &byte, 1)) {
failf(data, "CONNECT response too large");
return CURLE_RECV_ERROR;
}
/* if this is not the end of a header line then continue */
if(byte != 0x0a)
continue;
ts->headerlines++;
linep = curlx_dyn_ptr(&ts->rcvbuf);
line_len = curlx_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
/* output debug if that is requested */
Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len);
/* send the header to the callback */
writetype = CLIENTWRITE_HEADER | CLIENTWRITE_CONNECT |
(ts->headerlines == 1 ? CLIENTWRITE_STATUS : 0);
result = Curl_client_write(data, writetype, linep, line_len);
if(result)
return result;
result = Curl_bump_headersize(data, line_len, TRUE);
if(result)
return result;
/* Newlines are CRLF, so the CR is ignored as the line is not
really terminated until the LF comes. Treat a following CR
as end-of-headers as well.*/
if(('\r' == linep[0]) ||
('\n' == linep[0])) {
/* end of response-headers from the proxy */
if((407 == k->httpcode) && !data->state.authproblem) {
/* If we get a 407 response code with content length
when we have no auth problem, we must ignore the
whole response-body */
ts->keepon = KEEPON_IGNORE;
if(ts->cl) {
infof(data, "Ignore %" FMT_OFF_T " bytes of response-body", ts->cl);
}
else if(ts->chunked_encoding) {
infof(data, "Ignore chunked response-body");
}
else {
/* without content-length or chunked encoding, we
cannot keep the connection alive since the close is
the end signal so we bail out at once instead */
CURL_TRC_CF(data, cf, "CONNECT: no content-length or chunked");
ts->keepon = KEEPON_DONE;
}
}
else {
ts->keepon = KEEPON_DONE;
}
DEBUGASSERT(ts->keepon == KEEPON_IGNORE
|| ts->keepon == KEEPON_DONE);
continue;
}
result = on_resp_header(cf, data, ts, linep);
if(result)
return result;
curlx_dyn_reset(&ts->rcvbuf);
} /* while there is buffer left and loop is requested */
if(error)
result = CURLE_RECV_ERROR;
*done = (ts->keepon == KEEPON_DONE);
if(!result && *done && data->info.httpproxycode/100 != 2) {
/* Deal with the possibly already received authenticate
headers. 'newurl' is set to a new URL if we must loop. */
result = Curl_http_auth_act(data);
}
return result;
}
static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct h1_tunnel_state *ts)
{
struct connectdata *conn = cf->conn;
CURLcode result;
bool done;
if(tunnel_is_established(ts))
return CURLE_OK;
if(tunnel_is_failed(ts))
return CURLE_RECV_ERROR; /* Need a cfilter close and new bootstrap */
do {
timediff_t check;
check = Curl_timeleft(data, NULL, TRUE);
if(check <= 0) {
failf(data, "Proxy CONNECT aborted due to timeout");
result = CURLE_OPERATION_TIMEDOUT;
goto out;
}
switch(ts->tunnel_state) {
case H1_TUNNEL_INIT:
/* Prepare the CONNECT request and make a first attempt to send. */
CURL_TRC_CF(data, cf, "CONNECT start");
result = start_CONNECT(cf, data, ts);
if(result)
goto out;
h1_tunnel_go_state(cf, ts, H1_TUNNEL_CONNECT, data);
FALLTHROUGH();
case H1_TUNNEL_CONNECT:
/* see that the request is completely sent */
CURL_TRC_CF(data, cf, "CONNECT send");
result = send_CONNECT(cf, data, ts, &done);
if(result || !done)
goto out;
h1_tunnel_go_state(cf, ts, H1_TUNNEL_RECEIVE, data);
FALLTHROUGH();
case H1_TUNNEL_RECEIVE:
/* read what is there */
CURL_TRC_CF(data, cf, "CONNECT receive");
result = recv_CONNECT_resp(cf, data, ts, &done);
if(Curl_pgrsUpdate(data)) {
result = CURLE_ABORTED_BY_CALLBACK;
goto out;
}
/* error or not complete yet. return for more multi-multi */
if(result || !done)
goto out;
/* got it */
h1_tunnel_go_state(cf, ts, H1_TUNNEL_RESPONSE, data);
FALLTHROUGH();
case H1_TUNNEL_RESPONSE:
CURL_TRC_CF(data, cf, "CONNECT response");
if(data->req.newurl) {
/* not the "final" response, we need to do a follow up request.
* If the other side indicated a connection close, or if someone
* else told us to close this connection, do so now.
*/
Curl_req_soft_reset(&data->req, data);
if(ts->close_connection || conn->bits.close) {
/* Close this filter and the sub-chain, re-connect the
* sub-chain and continue. Closing this filter will
* reset our tunnel state. To avoid recursion, we return
* and expect to be called again.
*/
CURL_TRC_CF(data, cf, "CONNECT need to close+open");
infof(data, "Connect me again please");
Curl_conn_cf_close(cf, data);
connkeep(conn, "HTTP proxy CONNECT");
result = Curl_conn_cf_connect(cf->next, data, &done);
goto out;
}
else {
/* staying on this connection, reset state */
h1_tunnel_go_state(cf, ts, H1_TUNNEL_INIT, data);
}
}
break;
default:
break;
}
} while(data->req.newurl);
DEBUGASSERT(ts->tunnel_state == H1_TUNNEL_RESPONSE);
if(data->info.httpproxycode/100 != 2) {
/* a non-2xx response and we have no next URL to try. */
Curl_safefree(data->req.newurl);
/* failure, close this connection to avoid reuse */
streamclose(conn, "proxy CONNECT failure");
h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
failf(data, "CONNECT tunnel failed, response %d", data->req.httpcode);
return CURLE_RECV_ERROR;
}
/* 2xx response, SUCCESS! */
h1_tunnel_go_state(cf, ts, H1_TUNNEL_ESTABLISHED, data);
infof(data, "CONNECT tunnel established, response %d",
data->info.httpproxycode);
result = CURLE_OK;
out:
if(result)
h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
return result;
}
static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
CURLcode result;
struct h1_tunnel_state *ts = cf->ctx;
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
}
CURL_TRC_CF(data, cf, "connect");
result = cf->next->cft->do_connect(cf->next, data, done);
if(result || !*done)
return result;
*done = FALSE;
if(!ts) {
result = tunnel_init(cf, data, &ts);
if(result)
return result;
cf->ctx = ts;
}
/* We want "seamless" operations through HTTP proxy tunnel */
result = H1_CONNECT(cf, data, ts);
if(result)
goto out;
Curl_safefree(data->state.aptr.proxyuserpwd);
out:
*done = (result == CURLE_OK) && tunnel_is_established(cf->ctx);
if(*done) {
cf->connected = TRUE;
/* The real request will follow the CONNECT, reset request partially */
Curl_req_soft_reset(&data->req, data);
Curl_client_reset(data);
Curl_pgrsSetUploadCounter(data, 0);
Curl_pgrsSetDownloadCounter(data, 0);
tunnel_free(cf, data);
}
return result;
}
static CURLcode cf_h1_proxy_adjust_pollset(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct easy_pollset *ps)
{
struct h1_tunnel_state *ts = cf->ctx;
CURLcode result = CURLE_OK;
if(!cf->connected) {
/* If we are not connected, but the filter "below" is
* and not waiting on something, we are tunneling. */
curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
if(ts) {
/* when we have sent a CONNECT to a proxy, we should rather either
wait for the socket to become readable to be able to get the
response headers or if we are still sending the request, wait
for write. */
if(tunnel_want_send(ts))
result = Curl_pollset_set_out_only(data, ps, sock);
else
result = Curl_pollset_set_in_only(data, ps, sock);
}
else
result = Curl_pollset_set_out_only(data, ps, sock);
}
return result;
}
static void cf_h1_proxy_destroy(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
CURL_TRC_CF(data, cf, "destroy");
tunnel_free(cf, data);
}
static void cf_h1_proxy_close(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
CURL_TRC_CF(data, cf, "close");
if(cf) {
cf->connected = FALSE;
if(cf->ctx) {
h1_tunnel_go_state(cf, cf->ctx, H1_TUNNEL_INIT, data);
}
if(cf->next)
cf->next->cft->do_close(cf->next, data);
}
}
struct Curl_cftype Curl_cft_h1_proxy = {
"H1-PROXY",
CF_TYPE_IP_CONNECT|CF_TYPE_PROXY,
0,
cf_h1_proxy_destroy,
cf_h1_proxy_connect,
cf_h1_proxy_close,
Curl_cf_def_shutdown,
cf_h1_proxy_adjust_pollset,
Curl_cf_def_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
Curl_cf_def_cntrl,
Curl_cf_def_conn_is_alive,
Curl_cf_def_conn_keep_alive,
Curl_cf_http_proxy_query,
};
CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data)
{
struct Curl_cfilter *cf;
CURLcode result;
(void)data;
result = Curl_cf_create(&cf, &Curl_cft_h1_proxy, NULL);
if(!result)
Curl_conn_cf_insert_after(cf_at, cf);
return result;
}
#endif /* !CURL_DISABLE_PROXY && ! CURL_DISABLE_HTTP */
+39
View File
@@ -0,0 +1,39 @@
#ifndef HEADER_CURL_H1_PROXY_H
#define HEADER_CURL_H1_PROXY_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
CURLcode Curl_cf_h1_proxy_insert_after(struct Curl_cfilter *cf,
struct Curl_easy *data);
extern struct Curl_cftype Curl_cft_h1_proxy;
#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */
#endif /* HEADER_CURL_H1_PROXY_H */
File diff suppressed because it is too large Load Diff
+38
View File
@@ -0,0 +1,38 @@
#ifndef HEADER_CURL_H2_PROXY_H
#define HEADER_CURL_H2_PROXY_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#if defined(USE_NGHTTP2) && !defined(CURL_DISABLE_PROXY)
CURLcode Curl_cf_h2_proxy_insert_after(struct Curl_cfilter *cf,
struct Curl_easy *data);
extern struct Curl_cftype Curl_cft_h2_proxy;
#endif /* USE_NGHTTP2 && !CURL_DISABLE_PROXY */
#endif /* HEADER_CURL_H2_PROXY_H */
+254
View File
@@ -0,0 +1,254 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifndef CURL_DISABLE_PROXY
#include <curl/curl.h>
#include "urldata.h"
#include "cfilters.h"
#include "cf-haproxy.h"
#include "curl_trc.h"
#include "multiif.h"
#include "select.h"
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
typedef enum {
HAPROXY_INIT, /* init/default/no tunnel state */
HAPROXY_SEND, /* data_out being sent */
HAPROXY_DONE /* all work done */
} haproxy_state;
struct cf_haproxy_ctx {
int state;
struct dynbuf data_out;
};
static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx)
{
DEBUGASSERT(ctx);
ctx->state = HAPROXY_INIT;
curlx_dyn_reset(&ctx->data_out);
}
static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx)
{
if(ctx) {
curlx_dyn_free(&ctx->data_out);
free(ctx);
}
}
static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
struct Curl_easy *data)
{
struct cf_haproxy_ctx *ctx = cf->ctx;
CURLcode result;
const char *client_ip;
struct ip_quadruple ipquad;
bool is_ipv6;
DEBUGASSERT(ctx);
DEBUGASSERT(ctx->state == HAPROXY_INIT);
#ifdef USE_UNIX_SOCKETS
if(cf->conn->unix_domain_socket)
/* the buffer is large enough to hold this! */
result = curlx_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n"));
else {
#endif /* USE_UNIX_SOCKETS */
result = Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad);
if(result)
return result;
/* Emit the correct prefix for IPv6 */
if(data->set.str[STRING_HAPROXY_CLIENT_IP])
client_ip = data->set.str[STRING_HAPROXY_CLIENT_IP];
else
client_ip = ipquad.local_ip;
result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
is_ipv6 ? "TCP6" : "TCP4",
client_ip, ipquad.remote_ip,
ipquad.local_port, ipquad.remote_port);
#ifdef USE_UNIX_SOCKETS
}
#endif /* USE_UNIX_SOCKETS */
return result;
}
static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
struct cf_haproxy_ctx *ctx = cf->ctx;
CURLcode result;
size_t len;
DEBUGASSERT(ctx);
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
}
result = cf->next->cft->do_connect(cf->next, data, done);
if(result || !*done)
return result;
switch(ctx->state) {
case HAPROXY_INIT:
result = cf_haproxy_date_out_set(cf, data);
if(result)
goto out;
ctx->state = HAPROXY_SEND;
FALLTHROUGH();
case HAPROXY_SEND:
len = curlx_dyn_len(&ctx->data_out);
if(len > 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 - nwritten);
if(curlx_dyn_len(&ctx->data_out) > 0) {
result = CURLE_OK;
goto out;
}
}
ctx->state = HAPROXY_DONE;
FALLTHROUGH();
default:
curlx_dyn_free(&ctx->data_out);
break;
}
out:
*done = (!result) && (ctx->state == HAPROXY_DONE);
cf->connected = *done;
return result;
}
static void cf_haproxy_destroy(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
(void)data;
CURL_TRC_CF(data, cf, "destroy");
cf_haproxy_ctx_free(cf->ctx);
}
static void cf_haproxy_close(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
CURL_TRC_CF(data, cf, "close");
cf->connected = FALSE;
cf_haproxy_ctx_reset(cf->ctx);
if(cf->next)
cf->next->cft->do_close(cf->next, data);
}
static CURLcode cf_haproxy_adjust_pollset(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct easy_pollset *ps)
{
if(cf->next->connected && !cf->connected) {
/* If we are not connected, but the filter "below" is
* and not waiting on something, we are sending. */
return Curl_pollset_set_out_only(
data, ps, Curl_conn_cf_get_socket(cf, data));
}
return CURLE_OK;
}
struct Curl_cftype Curl_cft_haproxy = {
"HAPROXY",
CF_TYPE_PROXY,
0,
cf_haproxy_destroy,
cf_haproxy_connect,
cf_haproxy_close,
Curl_cf_def_shutdown,
cf_haproxy_adjust_pollset,
Curl_cf_def_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
Curl_cf_def_cntrl,
Curl_cf_def_conn_is_alive,
Curl_cf_def_conn_keep_alive,
Curl_cf_def_query,
};
static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf,
struct Curl_easy *data)
{
struct Curl_cfilter *cf = NULL;
struct cf_haproxy_ctx *ctx;
CURLcode result;
(void)data;
ctx = calloc(1, sizeof(*ctx));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
ctx->state = HAPROXY_INIT;
curlx_dyn_init(&ctx->data_out, DYN_HAXPROXY);
result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx);
if(result)
goto out;
ctx = NULL;
out:
cf_haproxy_ctx_free(ctx);
*pcf = result ? NULL : cf;
return result;
}
CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data)
{
struct Curl_cfilter *cf;
CURLcode result;
result = cf_haproxy_create(&cf, data);
if(result)
goto out;
Curl_conn_cf_insert_after(cf_at, cf);
out:
return result;
}
#endif /* !CURL_DISABLE_PROXY */
+39
View File
@@ -0,0 +1,39 @@
#ifndef HEADER_CURL_CF_HAPROXY_H
#define HEADER_CURL_CF_HAPROXY_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include "urldata.h"
#ifndef CURL_DISABLE_PROXY
CURLcode Curl_cf_haproxy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data);
extern struct Curl_cftype Curl_cft_haproxy;
#endif /* !CURL_DISABLE_PROXY */
#endif /* HEADER_CURL_CF_HAPROXY_H */
+752
View File
@@ -0,0 +1,752 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifndef CURL_DISABLE_HTTP
#include "urldata.h"
#include <curl/curl.h>
#include "curl_trc.h"
#include "cfilters.h"
#include "connect.h"
#include "hostip.h"
#include "multiif.h"
#include "cf-https-connect.h"
#include "http2.h"
#include "select.h"
#include "vquic/vquic.h"
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
typedef enum {
CF_HC_INIT,
CF_HC_CONNECT,
CF_HC_SUCCESS,
CF_HC_FAILURE
} cf_hc_state;
struct cf_hc_baller {
const char *name;
struct Curl_cfilter *cf;
CURLcode result;
struct curltime started;
int reply_ms;
unsigned char transport;
enum alpnid alpn_id;
BIT(shutdown);
};
static void cf_hc_baller_reset(struct cf_hc_baller *b,
struct Curl_easy *data)
{
if(b->cf) {
Curl_conn_cf_close(b->cf, data);
Curl_conn_cf_discard_chain(&b->cf, data);
b->cf = NULL;
}
b->result = CURLE_OK;
b->reply_ms = -1;
}
static bool cf_hc_baller_is_active(struct cf_hc_baller *b)
{
return b->cf && !b->result;
}
static bool cf_hc_baller_has_started(struct cf_hc_baller *b)
{
return !!b->cf;
}
static int cf_hc_baller_reply_ms(struct cf_hc_baller *b,
struct Curl_easy *data)
{
if(b->cf && (b->reply_ms < 0))
b->cf->cft->query(b->cf, data, CF_QUERY_CONNECT_REPLY_MS,
&b->reply_ms, NULL);
return b->reply_ms;
}
static bool cf_hc_baller_data_pending(struct cf_hc_baller *b,
const struct Curl_easy *data)
{
return b->cf && !b->result && b->cf->cft->has_data_pending(b->cf, data);
}
static bool cf_hc_baller_needs_flush(struct cf_hc_baller *b,
struct Curl_easy *data)
{
return b->cf && !b->result && Curl_conn_cf_needs_flush(b->cf, data);
}
static CURLcode cf_hc_baller_cntrl(struct cf_hc_baller *b,
struct Curl_easy *data,
int event, int arg1, void *arg2)
{
if(b->cf && !b->result)
return Curl_conn_cf_cntrl(b->cf, data, FALSE, event, arg1, arg2);
return CURLE_OK;
}
struct cf_hc_ctx {
cf_hc_state state;
struct curltime started; /* when connect started */
CURLcode result; /* overall result */
struct cf_hc_baller ballers[2];
size_t baller_count;
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,
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";
break;
case ALPN_h1:
b->name = "h1";
break;
default:
b->result = CURLE_FAILED_INIT;
break;
}
}
static void cf_hc_baller_init(struct cf_hc_baller *b,
struct Curl_cfilter *cf,
struct Curl_easy *data,
int transport)
{
struct Curl_cfilter *save = cf->next;
cf->next = NULL;
b->started = curlx_now();
switch(b->alpn_id) {
case ALPN_h3:
transport = TRNSPRT_QUIC;
break;
default:
break;
}
if(!b->result)
b->result = Curl_cf_setup_insert_after(cf, data, transport,
CURL_CF_SSL_ENABLE);
b->cf = cf->next;
cf->next = save;
}
static CURLcode cf_hc_baller_connect(struct cf_hc_baller *b,
struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
struct Curl_cfilter *save = cf->next;
cf->next = b->cf;
b->result = Curl_conn_cf_connect(cf->next, data, done);
b->cf = cf->next; /* it might mutate */
cf->next = save;
return b->result;
}
static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_hc_ctx *ctx = cf->ctx;
size_t i;
if(ctx) {
for(i = 0; i < ctx->baller_count; ++i)
cf_hc_baller_reset(&ctx->ballers[i], data);
ctx->state = CF_HC_INIT;
ctx->result = CURLE_OK;
ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout;
ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 4;
}
}
static CURLcode baller_connected(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct cf_hc_baller *winner)
{
struct cf_hc_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
int reply_ms;
size_t i;
DEBUGASSERT(winner->cf);
for(i = 0; i < ctx->baller_count; ++i)
if(winner != &ctx->ballers[i])
cf_hc_baller_reset(&ctx->ballers[i], data);
reply_ms = cf_hc_baller_reply_ms(winner, data);
if(reply_ms >= 0)
CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms",
winner->name, (int)curlx_timediff(curlx_now(),
winner->started), reply_ms);
else
CURL_TRC_CF(data, cf, "deferred handshake %s: %dms",
winner->name, (int)curlx_timediff(curlx_now(),
winner->started));
/* install the winning filter below this one. */
cf->next = winner->cf;
winner->cf = NULL;
#ifdef USE_NGHTTP2
{
/* Using nghttp2, we add the filter "below" us, so when the conn
* closes, we tear it down for a fresh reconnect */
const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data);
if(alpn && !strcmp("h2", alpn)) {
result = Curl_http2_switch_at(cf, data);
if(result) {
ctx->state = CF_HC_FAILURE;
ctx->result = result;
return result;
}
}
}
#endif
ctx->state = CF_HC_SUCCESS;
cf->connected = TRUE;
return result;
}
static bool time_to_start_next(struct Curl_cfilter *cf,
struct Curl_easy *data,
size_t idx, struct curltime now)
{
struct cf_hc_ctx *ctx = cf->ctx;
timediff_t elapsed_ms;
size_t i;
if(idx >= ctx->baller_count)
return FALSE;
if(cf_hc_baller_has_started(&ctx->ballers[idx]))
return FALSE;
for(i = 0; i < idx; i++) {
if(!ctx->ballers[i].result)
break;
}
if(i == idx) {
CURL_TRC_CF(data, cf, "all previous attempts failed, starting %s",
ctx->ballers[idx].name);
return TRUE;
}
elapsed_ms = curlx_timediff(now, ctx->started);
if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) {
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 %" 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;
}
/* set the effective hard timeout again */
Curl_expire(data, ctx->hard_eyeballs_timeout_ms - elapsed_ms,
EXPIRE_ALPN_EYEBALLS);
}
return FALSE;
}
static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
struct cf_hc_ctx *ctx = cf->ctx;
struct curltime now;
CURLcode result = CURLE_OK;
size_t i, failed_ballers;
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
}
*done = FALSE;
now = curlx_now();
switch(ctx->state) {
case CF_HC_INIT:
DEBUGASSERT(!cf->next);
for(i = 0; i < ctx->baller_count; i++)
DEBUGASSERT(!ctx->ballers[i].cf);
CURL_TRC_CF(data, cf, "connect, init");
ctx->started = now;
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 %" FMT_TIMEDIFF_T
"ms", ctx->soft_eyeballs_timeout_ms);
}
ctx->state = CF_HC_CONNECT;
FALLTHROUGH();
case CF_HC_CONNECT:
if(cf_hc_baller_is_active(&ctx->ballers[0])) {
result = cf_hc_baller_connect(&ctx->ballers[0], cf, data, done);
if(!result && *done) {
result = baller_connected(cf, data, &ctx->ballers[0]);
goto out;
}
}
if(time_to_start_next(cf, data, 1, now)) {
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])) {
CURL_TRC_CF(data, cf, "connect, check %s", ctx->ballers[1].name);
result = cf_hc_baller_connect(&ctx->ballers[1], cf, data, done);
if(!result && *done) {
result = baller_connected(cf, data, &ctx->ballers[1]);
goto out;
}
}
failed_ballers = 0;
for(i = 0; i < ctx->baller_count; i++) {
if(ctx->ballers[i].result)
++failed_ballers;
}
if(failed_ballers == ctx->baller_count) {
/* all have failed. we give up */
CURL_TRC_CF(data, cf, "connect, all attempts failed");
for(i = 0; i < ctx->baller_count; i++) {
if(ctx->ballers[i].result) {
result = ctx->ballers[i].result;
break;
}
}
ctx->state = CF_HC_FAILURE;
goto out;
}
result = CURLE_OK;
*done = FALSE;
break;
case CF_HC_FAILURE:
result = ctx->result;
cf->connected = FALSE;
*done = FALSE;
break;
case CF_HC_SUCCESS:
result = CURLE_OK;
cf->connected = TRUE;
*done = TRUE;
break;
}
out:
CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done);
return result;
}
static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data, bool *done)
{
struct cf_hc_ctx *ctx = cf->ctx;
size_t i;
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
}
/* shutdown all ballers that have not done so already. If one fails,
* continue shutting down others until all are shutdown. */
for(i = 0; i < ctx->baller_count; i++) {
struct cf_hc_baller *b = &ctx->ballers[i];
bool bdone = FALSE;
if(!cf_hc_baller_is_active(b) || b->shutdown)
continue;
b->result = b->cf->cft->do_shutdown(b->cf, data, &bdone);
if(b->result || bdone)
b->shutdown = TRUE; /* treat a failed shutdown as done */
}
*done = TRUE;
for(i = 0; i < ctx->baller_count; i++) {
if(!ctx->ballers[i].shutdown)
*done = FALSE;
}
if(*done) {
for(i = 0; i < ctx->baller_count; i++) {
if(ctx->ballers[i].result)
result = ctx->ballers[i].result;
}
}
CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done);
return result;
}
static CURLcode cf_hc_adjust_pollset(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct easy_pollset *ps)
{
CURLcode result = CURLE_OK;
if(!cf->connected) {
struct cf_hc_ctx *ctx = cf->ctx;
size_t i;
for(i = 0; (i < ctx->baller_count) && !result; i++) {
struct cf_hc_baller *b = &ctx->ballers[i];
if(!cf_hc_baller_is_active(b))
continue;
result = Curl_conn_cf_adjust_pollset(b->cf, data, ps);
}
CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %d socks", result, ps->n);
}
return result;
}
static bool cf_hc_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
struct cf_hc_ctx *ctx = cf->ctx;
size_t i;
if(cf->connected)
return cf->next->cft->has_data_pending(cf->next, data);
for(i = 0; i < ctx->baller_count; i++)
if(cf_hc_baller_data_pending(&ctx->ballers[i], data))
return TRUE;
return FALSE;
}
static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf,
struct Curl_easy *data,
int query)
{
struct cf_hc_ctx *ctx = cf->ctx;
struct curltime t, tmax;
size_t i;
memset(&tmax, 0, sizeof(tmax));
for(i = 0; i < ctx->baller_count; i++) {
struct Curl_cfilter *cfb = ctx->ballers[i].cf;
memset(&t, 0, sizeof(t));
if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) {
if((t.tv_sec || t.tv_usec) && curlx_timediff_us(t, tmax) > 0)
tmax = t;
}
}
return tmax;
}
static CURLcode cf_hc_query(struct Curl_cfilter *cf,
struct Curl_easy *data,
int query, int *pres1, void *pres2)
{
struct cf_hc_ctx *ctx = cf->ctx;
size_t i;
if(!cf->connected) {
switch(query) {
case CF_QUERY_TIMER_CONNECT: {
struct curltime *when = pres2;
*when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT);
return CURLE_OK;
}
case CF_QUERY_TIMER_APPCONNECT: {
struct curltime *when = pres2;
*when = cf_get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT);
return CURLE_OK;
}
case CF_QUERY_NEED_FLUSH: {
for(i = 0; i < ctx->baller_count; i++)
if(cf_hc_baller_needs_flush(&ctx->ballers[i], data)) {
*pres1 = TRUE;
return CURLE_OK;
}
break;
}
default:
break;
}
}
return cf->next ?
cf->next->cft->query(cf->next, data, query, pres1, pres2) :
CURLE_UNKNOWN_OPTION;
}
static CURLcode cf_hc_cntrl(struct Curl_cfilter *cf,
struct Curl_easy *data,
int event, int arg1, void *arg2)
{
struct cf_hc_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
size_t i;
if(!cf->connected) {
for(i = 0; i < ctx->baller_count; i++) {
result = cf_hc_baller_cntrl(&ctx->ballers[i], data, event, arg1, arg2);
if(result && (result != CURLE_AGAIN))
goto out;
}
result = CURLE_OK;
}
out:
return result;
}
static void cf_hc_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
CURL_TRC_CF(data, cf, "close");
cf_hc_reset(cf, data);
cf->connected = FALSE;
if(cf->next) {
cf->next->cft->do_close(cf->next, data);
Curl_conn_cf_discard_chain(&cf->next, data);
}
}
static void cf_hc_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_hc_ctx *ctx = cf->ctx;
(void)data;
CURL_TRC_CF(data, cf, "destroy");
cf_hc_reset(cf, data);
Curl_safefree(ctx);
}
struct Curl_cftype Curl_cft_http_connect = {
"HTTPS-CONNECT",
0,
CURL_LOG_LVL_NONE,
cf_hc_destroy,
cf_hc_connect,
cf_hc_close,
cf_hc_shutdown,
cf_hc_adjust_pollset,
cf_hc_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
cf_hc_cntrl,
Curl_cf_def_conn_is_alive,
Curl_cf_def_conn_keep_alive,
cf_hc_query,
};
static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
enum alpnid *alpnids, size_t alpn_count,
unsigned char def_transport)
{
struct Curl_cfilter *cf = NULL;
struct cf_hc_ctx *ctx;
CURLcode result = CURLE_OK;
size_t i;
DEBUGASSERT(alpnids);
DEBUGASSERT(alpn_count);
DEBUGASSERT(alpn_count <= CURL_ARRAYSIZE(ctx->ballers));
if(!alpn_count || (alpn_count > CURL_ARRAYSIZE(ctx->ballers))) {
failf(data, "https-connect filter create with unsupported %zu ALPN ids",
alpn_count);
return CURLE_FAILED_INIT;
}
ctx = calloc(1, sizeof(*ctx));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
for(i = 0; i < alpn_count; ++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;
result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx);
if(result)
goto out;
ctx = NULL;
cf_hc_reset(cf, data);
out:
*pcf = result ? NULL : cf;
free(ctx);
return result;
}
static CURLcode cf_http_connect_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
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, def_transport);
if(result)
goto out;
Curl_conn_cf_add(data, conn, sockindex, cf);
out:
return result;
}
static bool cf_https_alpns_contain(enum alpnid id,
enum alpnid *list, size_t len)
{
size_t i;
for(i = 0; i < len; ++i) {
if(id == list[i])
return TRUE;
}
return FALSE;
}
CURLcode Curl_cf_https_setup(struct Curl_easy *data,
struct connectdata *conn,
int sockindex)
{
enum alpnid alpn_ids[2];
size_t alpn_count = 0;
CURLcode result = CURLE_OK;
struct Curl_cfilter cf_fake, *cf = NULL;
(void)sockindex;
/* we want to log for the filter before we create it, fake it. */
memset(&cf_fake, 0, sizeof(cf_fake));
cf_fake.cft = &Curl_cft_http_connect;
cf = &cf_fake;
if(conn->bits.tls_enable_alpn) {
#ifdef USE_HTTPSRR
/* Is there an HTTPSRR use its ALPNs here.
* We are here after having selected a connection to a host+port and
* can no longer change that. Any HTTPSRR advice for other hosts and ports
* we need to ignore. */
struct Curl_dns_entry *dns = data->state.dns[sockindex];
struct Curl_https_rrinfo *rr = dns ? dns->hinfo : NULL;
if(rr && !rr->no_def_alpn && /* ALPNs are defaults */
(!rr->target || /* for same host */
!rr->target[0] ||
(rr->target[0] == '.' &&
!rr->target[1])) &&
(rr->port < 0 || /* for same port */
rr->port == conn->remote_port)) {
size_t i;
for(i = 0; i < CURL_ARRAYSIZE(rr->alpns) &&
alpn_count < CURL_ARRAYSIZE(alpn_ids); ++i) {
enum alpnid alpn = rr->alpns[i];
if(cf_https_alpns_contain(alpn, alpn_ids, alpn_count))
continue;
switch(alpn) {
case ALPN_h3:
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");
alpn_ids[alpn_count++] = alpn;
}
break;
case ALPN_h2:
if(data->state.http_neg.allowed & CURL_HTTP_V2x) {
CURL_TRC_CF(data, cf, "adding h2 via HTTPS-RR");
alpn_ids[alpn_count++] = alpn;
}
break;
case ALPN_h1:
if(data->state.http_neg.allowed & CURL_HTTP_V1x) {
CURL_TRC_CF(data, cf, "adding h1 via HTTPS-RR");
alpn_ids[alpn_count++] = alpn;
}
break;
default: /* ignore */
break;
}
}
}
#endif
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, conn->transport_wanted);
if(!result) {
CURL_TRC_CF(data, cf, "adding wanted h3");
alpn_ids[alpn_count++] = ALPN_h3;
}
else if(data->state.http_neg.wanted == CURL_HTTP_V3x)
goto out; /* only h3 allowed, not possible, error out */
}
if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
(data->state.http_neg.wanted & CURL_HTTP_V2x) &&
!cf_https_alpns_contain(ALPN_h2, alpn_ids, alpn_count)) {
CURL_TRC_CF(data, cf, "adding wanted h2");
alpn_ids[alpn_count++] = ALPN_h2;
}
else if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
(data->state.http_neg.wanted & CURL_HTTP_V1x) &&
!cf_https_alpns_contain(ALPN_h1, alpn_ids, alpn_count)) {
CURL_TRC_CF(data, cf, "adding wanted h1");
alpn_ids[alpn_count++] = ALPN_h1;
}
}
/* 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,
conn->transport_wanted);
}
out:
return result;
}
#endif /* !CURL_DISABLE_HTTP */
+49
View File
@@ -0,0 +1,49 @@
#ifndef HEADER_CURL_CF_HTTP_H
#define HEADER_CURL_CF_HTTP_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifndef CURL_DISABLE_HTTP
struct Curl_cfilter;
struct Curl_easy;
struct connectdata;
struct Curl_cftype;
struct Curl_dns_entry;
extern struct Curl_cftype Curl_cft_http_connect;
CURLcode Curl_cf_http_connect_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
bool try_h3, bool try_h21);
CURLcode Curl_cf_https_setup(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
#endif /* !CURL_DISABLE_HTTP */
#endif /* HEADER_CURL_CF_HTTP_H */
+983
View File
@@ -0,0 +1,983 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h> /* <netinet/tcp.h> may need it */
#endif
#ifdef HAVE_SYS_UN_H
#include <sys/un.h> /* for sockaddr_un */
#endif
#ifdef HAVE_LINUX_TCP_H
#include <linux/tcp.h>
#elif defined(HAVE_NETINET_TCP_H)
#include <netinet/tcp.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef __VMS
#include <in.h>
#include <inet.h>
#endif
#include "urldata.h"
#include "connect.h"
#include "cfilters.h"
#include "cf-ip-happy.h"
#include "curl_trc.h"
#include "multiif.h"
#include "progress.h"
#include "select.h"
#include "vquic/vquic.h" /* for quic cfilters */
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
struct transport_provider {
int transport;
cf_ip_connect_create *cf_create;
};
static
#ifndef UNITTESTS
const
#endif
struct transport_provider transport_providers[] = {
{ TRNSPRT_TCP, Curl_cf_tcp_create },
#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3)
{ TRNSPRT_QUIC, Curl_cf_quic_create },
#endif
#ifndef CURL_DISABLE_TFTP
{ TRNSPRT_UDP, Curl_cf_udp_create },
#endif
#ifdef USE_UNIX_SOCKETS
{ TRNSPRT_UNIX, Curl_cf_unix_create },
#endif
};
static cf_ip_connect_create *get_cf_create(int transport)
{
size_t i;
for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
if(transport == transport_providers[i].transport)
return transport_providers[i].cf_create;
}
return NULL;
}
#ifdef UNITTESTS
/* used by unit2600.c */
void Curl_debug_set_transport_provider(int transport,
cf_ip_connect_create *cf_create)
{
size_t i;
for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
if(transport == transport_providers[i].transport) {
transport_providers[i].cf_create = cf_create;
return;
}
}
}
#endif /* UNITTESTS */
struct cf_ai_iter {
const struct Curl_addrinfo *head;
const struct Curl_addrinfo *last;
int ai_family;
int n;
};
static void cf_ai_iter_init(struct cf_ai_iter *iter,
const struct Curl_addrinfo *list,
int ai_family)
{
iter->head = list;
iter->ai_family = ai_family;
iter->last = NULL;
iter->n = -1;
}
static const struct Curl_addrinfo *cf_ai_iter_next(struct cf_ai_iter *iter)
{
const struct Curl_addrinfo *addr;
if(iter->n < 0) {
iter->n++;
for(addr = iter->head; addr; addr = addr->ai_next) {
if(addr->ai_family == iter->ai_family)
break;
}
iter->last = addr;
}
else if(iter->last) {
iter->n++;
for(addr = iter->last->ai_next; addr; addr = addr->ai_next) {
if(addr->ai_family == iter->ai_family)
break;
}
iter->last = addr;
}
return iter->last;
}
static bool cf_ai_iter_has_more(struct cf_ai_iter *iter)
{
const struct Curl_addrinfo *addr = iter->last ? iter->last->ai_next :
((iter->n < 0) ? iter->head : NULL);
while(addr) {
if(addr->ai_family == iter->ai_family)
return TRUE;
addr = addr->ai_next;
}
return FALSE;
}
struct cf_ip_attempt {
struct cf_ip_attempt *next;
const struct Curl_addrinfo *addr; /* List of addresses to try, not owned */
struct Curl_cfilter *cf; /* current sub-cfilter connecting */
cf_ip_connect_create *cf_create;
struct curltime started; /* start of current attempt */
CURLcode result;
int ai_family;
int transport;
int error;
BIT(connected); /* cf has connected */
BIT(shutdown); /* cf has shutdown */
BIT(inconclusive); /* connect was not a hard failure, we
* might talk to a restarting server */
};
static void cf_ip_attempt_free(struct cf_ip_attempt *a,
struct Curl_easy *data)
{
if(a) {
if(a->cf)
Curl_conn_cf_discard_chain(&a->cf, data);
free(a);
}
}
static CURLcode cf_ip_attempt_new(struct cf_ip_attempt **pa,
struct Curl_cfilter *cf,
struct Curl_easy *data,
const struct Curl_addrinfo *addr,
int ai_family,
int transport,
cf_ip_connect_create *cf_create)
{
struct Curl_cfilter *wcf;
struct cf_ip_attempt *a;
CURLcode result = CURLE_OK;
*pa = NULL;
a = calloc(1, sizeof(*a));
if(!a)
return CURLE_OUT_OF_MEMORY;
a->addr = addr;
a->ai_family = ai_family;
a->transport = transport;
a->result = CURLE_OK;
a->cf_create = cf_create;
*pa = a;
result = a->cf_create(&a->cf, data, cf->conn, a->addr, transport);
if(result)
goto out;
/* the new filter might have sub-filters */
for(wcf = a->cf; wcf; wcf = wcf->next) {
wcf->conn = cf->conn;
wcf->sockindex = cf->sockindex;
}
out:
if(result) {
cf_ip_attempt_free(a, data);
*pa = NULL;
}
return result;
}
static CURLcode cf_ip_attempt_connect(struct cf_ip_attempt *a,
struct Curl_easy *data,
bool *connected)
{
*connected = a->connected;
if(!a->result && !*connected) {
/* evaluate again */
a->result = Curl_conn_cf_connect(a->cf, data, connected);
if(!a->result) {
if(*connected) {
a->connected = TRUE;
}
}
else if(a->result == CURLE_WEIRD_SERVER_REPLY)
a->inconclusive = TRUE;
}
return a->result;
}
struct cf_ip_ballers {
struct cf_ip_attempt *running;
struct cf_ip_attempt *winner;
struct cf_ai_iter addr_iter;
#ifdef USE_IPV6
struct cf_ai_iter ipv6_iter;
#endif
cf_ip_connect_create *cf_create; /* for creating cf */
struct curltime started;
struct curltime last_attempt_started;
timediff_t attempt_delay_ms;
int last_attempt_ai_family;
int transport;
};
static CURLcode cf_ip_attempt_restart(struct cf_ip_attempt *a,
struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct Curl_cfilter *cf_prev = a->cf;
struct Curl_cfilter *wcf;
CURLcode result;
/* When restarting, we tear down and existing filter *after* we
* started up the new one. This gives us a new socket number and
* probably a new local port. Which may prevent confusion. */
a->result = CURLE_OK;
a->connected = FALSE;
a->inconclusive = FALSE;
a->cf = NULL;
result = a->cf_create(&a->cf, data, cf->conn, a->addr, a->transport);
if(!result) {
bool dummy;
/* the new filter might have sub-filters */
for(wcf = a->cf; wcf; wcf = wcf->next) {
wcf->conn = cf->conn;
wcf->sockindex = cf->sockindex;
}
a->result = cf_ip_attempt_connect(a, data, &dummy);
}
if(cf_prev)
Curl_conn_cf_discard_chain(&cf_prev, data);
return result;
}
static void cf_ip_ballers_clear(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct cf_ip_ballers *bs)
{
(void)cf;
while(bs->running) {
struct cf_ip_attempt *a = bs->running;
bs->running = a->next;
cf_ip_attempt_free(a, data);
}
cf_ip_attempt_free(bs->winner, data);
bs->winner = NULL;
}
static CURLcode cf_ip_ballers_init(struct cf_ip_ballers *bs, int ip_version,
const struct Curl_addrinfo *addr_list,
cf_ip_connect_create *cf_create,
int transport,
timediff_t attempt_delay_ms)
{
memset(bs, 0, sizeof(*bs));
bs->cf_create = cf_create;
bs->transport = transport;
bs->attempt_delay_ms = attempt_delay_ms;
bs->last_attempt_ai_family = AF_INET; /* so AF_INET6 is next */
if(transport == TRNSPRT_UNIX) {
#ifdef USE_UNIX_SOCKETS
cf_ai_iter_init(&bs->addr_iter, addr_list, AF_UNIX);
#else
return CURLE_UNSUPPORTED_PROTOCOL;
#endif
}
else { /* TCP/UDP/QUIC */
#ifdef USE_IPV6
if(ip_version == CURL_IPRESOLVE_V6)
cf_ai_iter_init(&bs->addr_iter, NULL, AF_INET);
else
cf_ai_iter_init(&bs->addr_iter, addr_list, AF_INET);
if(ip_version == CURL_IPRESOLVE_V4)
cf_ai_iter_init(&bs->ipv6_iter, NULL, AF_INET6);
else
cf_ai_iter_init(&bs->ipv6_iter, addr_list, AF_INET6);
#else
(void)ip_version;
cf_ai_iter_init(&bs->addr_iter, addr_list, AF_INET);
#endif
}
return CURLE_OK;
}
static CURLcode cf_ip_ballers_run(struct cf_ip_ballers *bs,
struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *connected)
{
CURLcode result = CURLE_OK;
struct cf_ip_attempt *a = NULL, **panchor;
bool do_more;
struct curltime now;
timediff_t next_expire_ms;
int i, inconclusive, ongoing;
if(bs->winner)
return CURLE_OK;
evaluate:
now = curlx_now();
ongoing = inconclusive = 0;
/* check if a running baller connects now */
i = -1;
for(panchor = &bs->running; *panchor; panchor = &((*panchor)->next)) {
++i;
a = *panchor;
a->result = cf_ip_attempt_connect(a, data, connected);
if(!a->result) {
if(*connected) {
/* connected, declare the winner, remove from running,
* clear remaining running list. */
CURL_TRC_CF(data, cf, "connect attempt #%d successful", i);
bs->winner = a;
*panchor = a->next;
a->next = NULL;
while(bs->running) {
a = bs->running;
bs->running = a->next;
cf_ip_attempt_free(a, data);
}
return CURLE_OK;
}
/* still running */
++ongoing;
}
else if(a->inconclusive) /* failed, but inconclusive */
++inconclusive;
}
if(bs->running)
CURL_TRC_CF(data, cf, "checked connect attempts: "
"%d ongoing, %d inconclusive", ongoing, inconclusive);
/* no attempt connected yet, start another one? */
if(!ongoing) {
if(!bs->started.tv_sec && !bs->started.tv_usec)
bs->started = now;
do_more = TRUE;
}
else {
bool more_possible = cf_ai_iter_has_more(&bs->addr_iter);
#ifdef USE_IPV6
if(!more_possible)
more_possible = cf_ai_iter_has_more(&bs->ipv6_iter);
#endif
do_more = more_possible &&
(curlx_timediff(now, bs->last_attempt_started) >=
bs->attempt_delay_ms);
if(do_more)
CURL_TRC_CF(data, cf, "happy eyeballs timeout expired, "
"start next attempt");
}
if(do_more) {
/* start the next attempt if there is another ip address to try.
* Alternate between address families when possible. */
const struct Curl_addrinfo *addr = NULL;
int ai_family = 0;
#ifdef USE_IPV6
if((bs->last_attempt_ai_family == AF_INET) ||
!cf_ai_iter_has_more(&bs->addr_iter)) {
addr = cf_ai_iter_next(&bs->ipv6_iter);
ai_family = bs->ipv6_iter.ai_family;
}
#endif
if(!addr) {
addr = cf_ai_iter_next(&bs->addr_iter);
ai_family = bs->addr_iter.ai_family;
}
if(addr) { /* try another address */
result = cf_ip_attempt_new(&a, cf, data, addr, ai_family,
bs->transport, bs->cf_create);
CURL_TRC_CF(data, cf, "starting %s attempt for ipv%s -> %d",
bs->running ? "next" : "first",
(ai_family == AF_INET) ? "4" : "6", result);
if(result)
goto out;
DEBUGASSERT(a);
/* append to running list */
panchor = &bs->running;
while(*panchor)
panchor = &((*panchor)->next);
*panchor = a;
bs->last_attempt_started = now;
bs->last_attempt_ai_family = ai_family;
/* and run everything again */
goto evaluate;
}
else if(inconclusive) {
/* tried all addresses, no success but some where inconclusive.
* Let's restart the inconclusive ones. */
timediff_t since_ms = curlx_timediff(now, bs->last_attempt_started);
timediff_t delay_ms = bs->attempt_delay_ms - since_ms;
if(delay_ms <= 0) {
CURL_TRC_CF(data, cf, "all attempts inconclusive, restarting one");
i = -1;
for(a = bs->running; a; a = a->next) {
++i;
if(!a->inconclusive)
continue;
result = cf_ip_attempt_restart(a, cf, data);
CURL_TRC_CF(data, cf, "restarted baller %d -> %d", i, result);
if(result) /* serious failure */
goto out;
bs->last_attempt_started = now;
goto evaluate;
}
DEBUGASSERT(0); /* should not come here */
}
else {
/* let's wait some more before restarting */
infof(data, "connect attempts inconclusive, retrying "
"in %" FMT_TIMEDIFF_T "ms", delay_ms);
Curl_expire(data, delay_ms, EXPIRE_HAPPY_EYEBALLS);
}
/* attempt timeout for restart has not expired yet */
goto out;
}
else if(!ongoing) {
/* no more addresses, no inconclusive attempts */
CURL_TRC_CF(data, cf, "no more attempts to try");
result = CURLE_COULDNT_CONNECT;
i = 0;
for(a = bs->running; a; a = a->next) {
CURL_TRC_CF(data, cf, "baller %d: result=%d", i, a->result);
if(a->result)
result = a->result;
}
}
}
out:
if(!result) {
bool more_possible;
/* when do we need to be called again? */
next_expire_ms = Curl_timeleft(data, &now, TRUE);
if(next_expire_ms <= 0) {
failf(data, "Connection timeout after %" FMT_OFF_T " ms",
curlx_timediff(now, data->progress.t_startsingle));
return CURLE_OPERATION_TIMEDOUT;
}
more_possible = cf_ai_iter_has_more(&bs->addr_iter);
#ifdef USE_IPV6
if(!more_possible)
more_possible = cf_ai_iter_has_more(&bs->ipv6_iter);
#endif
if(more_possible) {
timediff_t expire_ms, elapsed_ms;
elapsed_ms = curlx_timediff(now, bs->last_attempt_started);
expire_ms = CURLMAX(bs->attempt_delay_ms - elapsed_ms, 0);
next_expire_ms = CURLMIN(next_expire_ms, expire_ms);
if(next_expire_ms <= 0) {
CURL_TRC_CF(data, cf, "HAPPY_EYBALLS timeout due, re-evaluate");
goto evaluate;
}
CURL_TRC_CF(data, cf, "next HAPPY_EYBALLS timeout in %" FMT_TIMEDIFF_T
"ms", next_expire_ms);
Curl_expire(data, next_expire_ms, EXPIRE_HAPPY_EYEBALLS);
}
}
return result;
}
static CURLcode cf_ip_ballers_shutdown(struct cf_ip_ballers *bs,
struct Curl_easy *data,
bool *done)
{
struct cf_ip_attempt *a;
/* shutdown all ballers that have not done so already. If one fails,
* continue shutting down others until all are shutdown. */
*done = TRUE;
for(a = bs->running; a; a = a->next) {
bool bdone = FALSE;
if(a->shutdown)
continue;
a->result = a->cf->cft->do_shutdown(a->cf, data, &bdone);
if(a->result || bdone)
a->shutdown = TRUE; /* treat a failed shutdown as done */
else
*done = FALSE;
}
return CURLE_OK;
}
static CURLcode cf_ip_ballers_pollset(struct cf_ip_ballers *bs,
struct Curl_easy *data,
struct easy_pollset *ps)
{
struct cf_ip_attempt *a;
CURLcode result = CURLE_OK;
for(a = bs->running; a && !result; a = a->next) {
if(a->result)
continue;
result = Curl_conn_cf_adjust_pollset(a->cf, data, ps);
}
return result;
}
static bool cf_ip_ballers_pending(struct cf_ip_ballers *bs,
const struct Curl_easy *data)
{
struct cf_ip_attempt *a;
for(a = bs->running; a; a = a->next) {
if(a->result)
continue;
if(a->cf->cft->has_data_pending(a->cf, data))
return TRUE;
}
return FALSE;
}
static struct curltime cf_ip_ballers_max_time(struct cf_ip_ballers *bs,
struct Curl_easy *data,
int query)
{
struct curltime t, tmax;
struct cf_ip_attempt *a;
memset(&tmax, 0, sizeof(tmax));
for(a = bs->running; a; a = a->next) {
memset(&t, 0, sizeof(t));
if(!a->cf->cft->query(a->cf, data, query, NULL, &t)) {
if((t.tv_sec || t.tv_usec) && curlx_timediff_us(t, tmax) > 0)
tmax = t;
}
}
return tmax;
}
static int cf_ip_ballers_min_reply_ms(struct cf_ip_ballers *bs,
struct Curl_easy *data)
{
int reply_ms = -1, breply_ms;
struct cf_ip_attempt *a;
for(a = bs->running; a; a = a->next) {
if(!a->cf->cft->query(a->cf, data, CF_QUERY_CONNECT_REPLY_MS,
&breply_ms, NULL)) {
if(breply_ms >= 0 && (reply_ms < 0 || breply_ms < reply_ms))
reply_ms = breply_ms;
}
}
return reply_ms;
}
typedef enum {
SCFST_INIT,
SCFST_WAITING,
SCFST_DONE
} cf_connect_state;
struct cf_ip_happy_ctx {
int transport;
cf_ip_connect_create *cf_create;
cf_connect_state state;
struct cf_ip_ballers ballers;
struct curltime started;
};
static CURLcode is_connected(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *connected)
{
struct cf_ip_happy_ctx *ctx = cf->ctx;
struct connectdata *conn = cf->conn;
CURLcode result;
result = cf_ip_ballers_run(&ctx->ballers, cf, data, connected);
if(!result)
return CURLE_OK;
{
const char *hostname, *proxy_name = NULL;
char viamsg[160];
#ifndef CURL_DISABLE_PROXY
if(conn->bits.socksproxy)
proxy_name = conn->socks_proxy.host.name;
else if(conn->bits.httpproxy)
proxy_name = conn->http_proxy.host.name;
#endif
hostname = conn->bits.conn_to_host ? conn->conn_to_host.name :
conn->host.name;
#ifdef USE_UNIX_SOCKETS
if(conn->unix_domain_socket)
curl_msnprintf(viamsg, sizeof(viamsg), "over %s",
conn->unix_domain_socket);
else
#endif
{
int port;
if(cf->sockindex == SECONDARYSOCKET)
port = conn->secondary_port;
else if(cf->conn->bits.conn_to_port)
port = conn->conn_to_port;
else
port = conn->remote_port;
curl_msnprintf(viamsg, sizeof(viamsg), "port %u", port);
}
failf(data, "Failed to connect to %s %s %s%s%safter "
"%" FMT_TIMEDIFF_T " ms: %s",
hostname, viamsg,
proxy_name ? "via " : "",
proxy_name ? proxy_name : "",
proxy_name ? " " : "",
curlx_timediff(curlx_now(), data->progress.t_startsingle),
curl_easy_strerror(result));
}
#ifdef SOCKETIMEDOUT
if(SOCKETIMEDOUT == data->state.os_errno)
result = CURLE_OPERATION_TIMEDOUT;
#endif
return result;
}
/*
* Connect to the given host with timeout, proxy or remote does not matter.
* There might be more than one IP address to try out.
*/
static CURLcode start_connect(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_ip_happy_ctx *ctx = cf->ctx;
struct Curl_dns_entry *dns = data->state.dns[cf->sockindex];
if(!dns)
return CURLE_FAILED_INIT;
if(Curl_timeleft(data, NULL, TRUE) < 0) {
/* a precaution, no need to continue if time already is up */
failf(data, "Connection time-out");
return CURLE_OPERATION_TIMEDOUT;
}
CURL_TRC_CF(data, cf, "init ip ballers for transport %d", ctx->transport);
ctx->started = curlx_now();
return cf_ip_ballers_init(&ctx->ballers, cf->conn->ip_version,
dns->addr, ctx->cf_create, ctx->transport,
data->set.happy_eyeballs_timeout);
}
static void cf_ip_happy_ctx_clear(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_ip_happy_ctx *ctx = cf->ctx;
DEBUGASSERT(ctx);
DEBUGASSERT(data);
cf_ip_ballers_clear(cf, data, &ctx->ballers);
}
static CURLcode cf_ip_happy_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
struct cf_ip_happy_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
}
result = cf_ip_ballers_shutdown(&ctx->ballers, data, done);
CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done);
return result;
}
static CURLcode cf_ip_happy_adjust_pollset(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct easy_pollset *ps)
{
struct cf_ip_happy_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
if(!cf->connected) {
result = cf_ip_ballers_pollset(&ctx->ballers, data, ps);
CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %d socks", result, ps->n);
}
return result;
}
static CURLcode cf_ip_happy_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
struct cf_ip_happy_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
}
DEBUGASSERT(ctx);
*done = FALSE;
switch(ctx->state) {
case SCFST_INIT:
DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data));
DEBUGASSERT(!cf->connected);
result = start_connect(cf, data);
if(result)
return result;
ctx->state = SCFST_WAITING;
FALLTHROUGH();
case SCFST_WAITING:
result = is_connected(cf, data, done);
if(!result && *done) {
DEBUGASSERT(ctx->ballers.winner);
DEBUGASSERT(ctx->ballers.winner->cf);
DEBUGASSERT(ctx->ballers.winner->cf->connected);
/* we have a winner. Install and activate it.
* close/free all others. */
ctx->state = SCFST_DONE;
cf->connected = TRUE;
cf->next = ctx->ballers.winner->cf;
ctx->ballers.winner->cf = NULL;
cf_ip_happy_ctx_clear(cf, data);
Curl_expire_done(data, EXPIRE_HAPPY_EYEBALLS);
if(cf->conn->handler->protocol & PROTO_FAMILY_SSH)
Curl_pgrsTime(data, TIMER_APPCONNECT); /* we are connected already */
#ifndef CURL_DISABLE_VERBOSE_STRINGS
if(Curl_trc_cf_is_verbose(cf, data)) {
struct ip_quadruple ipquad;
bool is_ipv6;
if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) {
const char *host;
int port;
Curl_conn_get_current_host(data, cf->sockindex, &host, &port);
CURL_TRC_CF(data, cf, "Connected to %s (%s) port %u",
host, ipquad.remote_ip, ipquad.remote_port);
}
}
#endif
data->info.numconnects++; /* to track the # of connections made */
}
break;
case SCFST_DONE:
*done = TRUE;
break;
}
return result;
}
static void cf_ip_happy_close(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_ip_happy_ctx *ctx = cf->ctx;
CURL_TRC_CF(data, cf, "close");
cf_ip_happy_ctx_clear(cf, data);
cf->connected = FALSE;
ctx->state = SCFST_INIT;
if(cf->next) {
cf->next->cft->do_close(cf->next, data);
Curl_conn_cf_discard_chain(&cf->next, data);
}
}
static bool cf_ip_happy_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
struct cf_ip_happy_ctx *ctx = cf->ctx;
if(!cf->connected) {
return cf_ip_ballers_pending(&ctx->ballers, data);
}
return cf->next->cft->has_data_pending(cf->next, data);
}
static CURLcode cf_ip_happy_query(struct Curl_cfilter *cf,
struct Curl_easy *data,
int query, int *pres1, void *pres2)
{
struct cf_ip_happy_ctx *ctx = cf->ctx;
if(!cf->connected) {
switch(query) {
case CF_QUERY_CONNECT_REPLY_MS: {
*pres1 = cf_ip_ballers_min_reply_ms(&ctx->ballers, data);
CURL_TRC_CF(data, cf, "query connect reply: %dms", *pres1);
return CURLE_OK;
}
case CF_QUERY_TIMER_CONNECT: {
struct curltime *when = pres2;
*when = cf_ip_ballers_max_time(&ctx->ballers, data,
CF_QUERY_TIMER_CONNECT);
return CURLE_OK;
}
case CF_QUERY_TIMER_APPCONNECT: {
struct curltime *when = pres2;
*when = cf_ip_ballers_max_time(&ctx->ballers, data,
CF_QUERY_TIMER_APPCONNECT);
return CURLE_OK;
}
default:
break;
}
}
return cf->next ?
cf->next->cft->query(cf->next, data, query, pres1, pres2) :
CURLE_UNKNOWN_OPTION;
}
static void cf_ip_happy_destroy(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_ip_happy_ctx *ctx = cf->ctx;
CURL_TRC_CF(data, cf, "destroy");
if(ctx) {
cf_ip_happy_ctx_clear(cf, data);
}
/* release any resources held in state */
Curl_safefree(ctx);
}
struct Curl_cftype Curl_cft_ip_happy = {
"HAPPY-EYEBALLS",
0,
CURL_LOG_LVL_NONE,
cf_ip_happy_destroy,
cf_ip_happy_connect,
cf_ip_happy_close,
cf_ip_happy_shutdown,
cf_ip_happy_adjust_pollset,
cf_ip_happy_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
Curl_cf_def_cntrl,
Curl_cf_def_conn_is_alive,
Curl_cf_def_conn_keep_alive,
cf_ip_happy_query,
};
/**
* Create an IP happy eyeball connection filter that uses the, once resolved,
* address information to connect on ip families based on connection
* configuration.
* @param pcf output, the created cfilter
* @param data easy handle used in creation
* @param conn connection the filter is created for
* @param cf_create method to create the sub-filters performing the
* actual connects.
*/
static CURLcode cf_ip_happy_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
cf_ip_connect_create *cf_create,
int transport)
{
struct cf_ip_happy_ctx *ctx = NULL;
CURLcode result;
(void)data;
(void)conn;
*pcf = NULL;
ctx = calloc(1, sizeof(*ctx));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
ctx->transport = transport;
ctx->cf_create = cf_create;
result = Curl_cf_create(pcf, &Curl_cft_ip_happy, ctx);
out:
if(result) {
Curl_safefree(*pcf);
free(ctx);
}
return result;
}
CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
int transport)
{
cf_ip_connect_create *cf_create;
struct Curl_cfilter *cf;
CURLcode result;
/* Need to be first */
DEBUGASSERT(cf_at);
cf_create = get_cf_create(transport);
if(!cf_create) {
CURL_TRC_CF(data, cf_at, "unsupported transport type %d", transport);
return CURLE_UNSUPPORTED_PROTOCOL;
}
result = cf_ip_happy_create(&cf, data, cf_at->conn, cf_create, transport);
if(result)
return result;
Curl_conn_cf_insert_after(cf_at, cf);
return CURLE_OK;
}
+59
View File
@@ -0,0 +1,59 @@
#ifndef HEADER_CURL_IP_HAPPY_H
#define HEADER_CURL_IP_HAPPY_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include "curlx/nonblock.h" /* for curlx_nonblock() */
#include "sockaddr.h"
/**
* Create a cfilter for making an "ip" connection to the
* given address, using parameters from `conn`. The "ip" connection
* can be a TCP socket, a UDP socket or even a QUIC connection.
*
* It MUST use only the supplied `ai` for its connection attempt.
*
* Such a filter may be used in "happy eyeball" scenarios, and its
* `connect` implementation needs to support non-blocking. Once connected,
* it MAY be installed in the connection filter chain to serve transfers.
*/
typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
int transport);
CURLcode cf_ip_happy_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
int transport);
extern struct Curl_cftype Curl_cft_ip_happy;
#ifdef UNITTESTS
void Curl_debug_set_transport_provider(int transport,
cf_ip_connect_create *cf_create);
#endif
#endif /* HEADER_CURL_IP_HAPPY_H */
File diff suppressed because it is too large Load Diff
+168
View File
@@ -0,0 +1,168 @@
#ifndef HEADER_CURL_CF_SOCKET_H
#define HEADER_CURL_CF_SOCKET_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include "curlx/nonblock.h" /* for curlx_nonblock() */
#include "sockaddr.h"
struct Curl_addrinfo;
struct Curl_cfilter;
struct Curl_easy;
struct connectdata;
struct Curl_sockaddr_ex;
struct ip_quadruple;
/*
* The Curl_sockaddr_ex structure is basically libcurl's external API
* curl_sockaddr structure with enough space available to directly hold any
* protocol-specific address structures. The variable declared here will be
* used to pass / receive data to/from the fopensocket callback if this has
* been set, before that, it is initialized from parameters.
*/
struct Curl_sockaddr_ex {
int family;
int socktype;
int protocol;
unsigned int addrlen;
union {
struct sockaddr sa;
struct Curl_sockaddr_storage buf;
} addr;
};
#define curl_sa_addr addr.sa
#define curl_sa_addrbuf addr.buf
/*
* Parse interface option, and return the interface name and the host part.
*/
CURLcode Curl_parse_interface(const char *input,
char **dev, char **iface, char **host);
/*
* Create a socket based on info from 'conn' and 'ai'.
*
* Fill in 'addr' and 'sockfd' accordingly if OK is returned. If the open
* socket callback is set, used that!
*
*/
CURLcode Curl_socket_open(struct Curl_easy *data,
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);
#ifdef USE_WINSOCK
/* When you run a program that uses the Windows Sockets API, you may
experience slow performance when you copy data to a TCP server.
https://learn.microsoft.com/troubleshoot/windows-server/networking/slow-performance-copy-data-tcp-server-sockets-api
Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
Buffer Size
*/
void Curl_sndbuf_init(curl_socket_t sockfd);
#else
#define Curl_sndbuf_init(y) Curl_nop_stmt
#endif
/**
* Creates a cfilter that opens a TCP socket to the given address
* when calling its `connect` implementation.
* The filter will not touch any connection/data flags and can be
* used in happy eyeballing. Once selected for use, its `_active()`
* method needs to be called.
*/
CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
int transport);
/**
* Creates a cfilter that opens a UDP socket to the given address
* when calling its `connect` implementation.
* The filter will not touch any connection/data flags and can be
* used in happy eyeballing. Once selected for use, its `_active()`
* method needs to be called.
*/
CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
int transport);
/**
* Creates a cfilter that opens a UNIX socket to the given address
* when calling its `connect` implementation.
* The filter will not touch any connection/data flags and can be
* used in happy eyeballing. Once selected for use, its `_active()`
* method needs to be called.
*/
CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
const struct Curl_addrinfo *ai,
int transport);
/**
* Creates a cfilter that keeps a listening socket.
*/
CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
curl_socket_t *s);
/**
* Return TRUE iff the last filter at `sockindex` was set via
* Curl_conn_tcp_listen_set().
*/
bool Curl_conn_is_tcp_listen(struct Curl_easy *data,
int sockindex);
/**
* Peek at the socket and remote ip/port the socket filter is using.
* The filter owns all returned values.
* @param psock pointer to hold socket descriptor or NULL
* @param paddr pointer to hold addr reference or NULL
* @param pip pointer to get IP quadruple or NULL
* Returns error if the filter is of invalid type.
*/
CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
struct Curl_easy *data,
curl_socket_t *psock,
const struct Curl_sockaddr_ex **paddr,
struct ip_quadruple *pip) WARN_UNUSED_RESULT;
extern struct Curl_cftype Curl_cft_tcp;
extern struct Curl_cftype Curl_cft_udp;
extern struct Curl_cftype Curl_cft_unix;
extern struct Curl_cftype Curl_cft_tcp_accept;
#endif /* HEADER_CURL_CF_SOCKET_H */
File diff suppressed because it is too large Load Diff
+694
View File
@@ -0,0 +1,694 @@
#ifndef HEADER_CURL_CFILTERS_H
#define HEADER_CURL_CFILTERS_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "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.
*/
typedef void Curl_cft_destroy_this(struct Curl_cfilter *cf,
struct Curl_easy *data);
/* Callback to close the connection immediately. */
typedef void Curl_cft_close(struct Curl_cfilter *cf,
struct Curl_easy *data);
/* Callback to close the connection filter gracefully, non-blocking.
* Implementations MUST NOT chain calls to cf->next.
*/
typedef CURLcode Curl_cft_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done);
typedef CURLcode Curl_cft_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done);
struct easy_pollset;
/* Passing in an easy_pollset for monitoring of sockets, let
* filters add or remove sockets actions (CURL_POLL_OUT, CURL_POLL_IN).
* This may add a socket or, in case no actions remain, remove
* a socket from the set.
*
* Filter implementations need to call filters "below" *after* they have
* made their adjustments. This allows lower filters to override "upper"
* actions. If a "lower" filter is unable to write, it needs to be able
* to disallow POLL_OUT.
*
* A filter without own restrictions/preferences should not modify
* the pollset. Filters, whose filter "below" is not connected, should
* also do no adjustments.
*
* Examples: a TLS handshake, while ongoing, might remove POLL_IN when it
* needs to write, or vice versa. An HTTP/2 filter might remove POLL_OUT when
* a stream window is exhausted and a WINDOW_UPDATE needs to be received first
* and add instead POLL_IN.
*
* @param cf the filter to ask
* @param data the easy handle the pollset is about
* @param ps the pollset (inout) for the easy handle
*/
typedef CURLcode Curl_cft_adjust_pollset(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct easy_pollset *ps);
typedef bool Curl_cft_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data);
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 */
size_t *pnwritten); /* how much sent */
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 */
size_t *pnread); /* how much received */
typedef bool Curl_cft_conn_is_alive(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *input_pending);
typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf,
struct Curl_easy *data);
/**
* Events/controls for connection filters, their arguments and
* return code handling. Filter callbacks are invoked "top down".
* Return code handling:
* "first fail" meaning that the first filter returning != CURLE_OK, will
* abort further event distribution and determine the result.
* "ignored" meaning return values are ignored and the event is distributed
* to all filters in the chain. Overall result is always CURLE_OK.
*/
/* data event arg1 arg2 return */
#define CF_CTRL_DATA_SETUP 4 /* 0 NULL first fail */
/* unused now 5 */
#define CF_CTRL_DATA_PAUSE 6 /* on/off NULL first fail */
#define CF_CTRL_DATA_DONE 7 /* premature NULL ignored */
#define CF_CTRL_DATA_DONE_SEND 8 /* 0 NULL ignored */
/* update conn info at connection and data */
#define CF_CTRL_CONN_INFO_UPDATE (256+0) /* 0 NULL ignored */
#define CF_CTRL_FORGET_SOCKET (256+1) /* 0 NULL ignored */
#define CF_CTRL_FLUSH (256+2) /* 0 NULL first fail */
/**
* Handle event/control for the filter.
* Implementations MUST NOT chain calls to cf->next.
*/
typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
struct Curl_easy *data,
int event, int arg1, void *arg2);
/**
* Queries to ask via a `Curl_cft_query *query` method on a cfilter chain.
* - MAX_CONCURRENT: the maximum number of parallel transfers the filter
* chain expects to handle at the same time.
* default: 1 if no filter overrides.
* - CONNECT_REPLY_MS: milliseconds until the first indication of a server
* response was received on a connect. For TCP, this
* reflects the time until the socket connected. On UDP
* this gives the time the first bytes from the server
* were received.
* -1 if not determined yet.
* - CF_QUERY_SOCKET: the socket used by the filter chain
* - 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.
* - CF_QUERY_ALPN_NEGOTIATED: The ALPN selected by the server as
null-terminated string or NULL if none
selected/handshake not done. Implemented by filter
types CF_TYPE_SSL or CF_TYPE_IP_CONNECT.
*/
/* query res1 res2 */
#define CF_QUERY_MAX_CONCURRENT 1 /* number - */
#define CF_QUERY_CONNECT_REPLY_MS 2 /* number - */
#define CF_QUERY_SOCKET 3 /* - curl_socket_t */
#define CF_QUERY_TIMER_CONNECT 4 /* - struct curltime */
#define CF_QUERY_TIMER_APPCONNECT 5 /* - struct curltime */
#define CF_QUERY_STREAM_ERROR 6 /* error code - */
#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_* - * */
#define CF_QUERY_ALPN_NEGOTIATED 15 /* - const char * */
/**
* Query the cfilter for properties. Filters ignorant of a query will
* pass it "down" the filter chain.
*/
typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf,
struct Curl_easy *data,
int query, int *pres1, void *pres2);
/**
* Type flags for connection filters. A filter can have none, one or
* many of those. Use to evaluate state/capabilities of a filter chain.
*
* CF_TYPE_IP_CONNECT: provides an IP connection or sth equivalent, like
* a CONNECT tunnel, a UNIX domain socket, a QUIC
* connection, etc.
* CF_TYPE_SSL: provide SSL/TLS
* CF_TYPE_MULTIPLEX: provides multiplexing of easy handles
* CF_TYPE_PROXY provides proxying
* CF_TYPE_HTTP implement a version of the HTTP protocol
*/
#define CF_TYPE_IP_CONNECT (1 << 0)
#define CF_TYPE_SSL (1 << 1)
#define CF_TYPE_MULTIPLEX (1 << 2)
#define CF_TYPE_PROXY (1 << 3)
#define CF_TYPE_HTTP (1 << 4)
/* A connection filter type, e.g. specific implementation. */
struct Curl_cftype {
const char *name; /* name of the filter type */
int flags; /* flags of filter type */
int log_level; /* log level for such filters */
Curl_cft_destroy_this *destroy; /* destroy resources of this cf */
Curl_cft_connect *do_connect; /* establish connection */
Curl_cft_close *do_close; /* close conn */
Curl_cft_shutdown *do_shutdown; /* shutdown conn */
Curl_cft_adjust_pollset *adjust_pollset; /* adjust transfer poll set */
Curl_cft_data_pending *has_data_pending;/* conn has data pending */
Curl_cft_send *do_send; /* send data */
Curl_cft_recv *do_recv; /* receive data */
Curl_cft_cntrl *cntrl; /* events/control */
Curl_cft_conn_is_alive *is_alive; /* FALSE if conn is dead, Jim! */
Curl_cft_conn_keep_alive *keep_alive; /* try to keep it alive */
Curl_cft_query *query; /* query filter chain */
};
/* A connection filter instance, e.g. registered at a connection */
struct Curl_cfilter {
const struct Curl_cftype *cft; /* the type providing implementation */
struct Curl_cfilter *next; /* next filter in chain */
void *ctx; /* filter type specific settings */
struct connectdata *conn; /* the connection this filter belongs to */
int sockindex; /* the index the filter is installed at */
BIT(connected); /* != 0 iff this filter is connected */
BIT(shutdown); /* != 0 iff this filter has shut down */
};
/* Default implementations for the type functions, implementing nop. */
void Curl_cf_def_destroy_this(struct Curl_cfilter *cf,
struct Curl_easy *data);
/* Default implementations for the type functions, implementing pass-through
* the filter chain. */
CURLcode 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);
CURLcode Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *buf, size_t len, bool eos,
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);
bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *input_pending);
CURLcode Curl_cf_def_conn_keep_alive(struct Curl_cfilter *cf,
struct Curl_easy *data);
CURLcode Curl_cf_def_query(struct Curl_cfilter *cf,
struct Curl_easy *data,
int query, int *pres1, void *pres2);
CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data, bool *done);
/**
* Create a new filter instance, unattached to the filter chain.
* Use Curl_conn_cf_add() to add it to the chain.
* @param pcf on success holds the created instance
* @param cft the filter type
* @param ctx the type specific context to use
*/
CURLcode Curl_cf_create(struct Curl_cfilter **pcf,
const struct Curl_cftype *cft,
void *ctx);
/**
* Add a filter instance to the `sockindex` filter chain at connection
* `conn`. The filter must not already be attached. It is inserted at
* the start of the chain (top).
*/
void Curl_conn_cf_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
struct Curl_cfilter *cf);
/**
* Insert a filter (chain) after `cf_at`.
* `cf_new` must not already be attached.
*/
void Curl_conn_cf_insert_after(struct Curl_cfilter *cf_at,
struct Curl_cfilter *cf_new);
/**
* Extract filter `*pcf` from its connection filter chain.
* Destroy `*pcf`, even if it was not part of the chain and NULL it.
* Returns TRUE of cf has been part of chain.
*/
bool Curl_conn_cf_discard(struct Curl_cfilter **pcf,
struct Curl_easy *data);
/**
* Discard all cfilters starting with `*pcf` and clearing it afterwards.
*/
void Curl_conn_cf_discard_chain(struct Curl_cfilter **pcf,
struct Curl_easy *data);
/**
* Remove and destroy all filters at chain `sockindex` on connection `conn`.
*/
void Curl_conn_cf_discard_all(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done);
void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data);
CURLcode Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const 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,
int event, int arg1, void *arg2);
/**
* Get the socket used by the filter chain starting at `cf`.
* Returns CURL_SOCKET_BAD if not available.
*/
curl_socket_t Curl_conn_cf_get_socket(struct Curl_cfilter *cf,
struct Curl_easy *data);
CURLcode Curl_conn_cf_get_ip_info(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *is_ipv6, struct ip_quadruple *ipquad);
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);
const char *Curl_conn_cf_get_alpn_negotiated(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
/**
* Bring the filter chain at `sockindex` for connection `data->conn` into
* connected state. Which will set `*done` to TRUE.
* This can be called on an already connected chain with no side effects.
* When not `blocking`, calls may return without error and `*done != TRUE`,
* while the individual filters negotiated the connection.
*/
CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex,
bool blocking, bool *done);
/**
* Check if a filter chain at `sockindex` for connection `conn` exists.
*/
bool Curl_conn_is_setup(struct connectdata *conn, int sockindex);
/**
* Check if the filter chain at `sockindex` for connection `conn` is
* completely connected.
*/
bool Curl_conn_is_connected(struct connectdata *conn, int sockindex);
/**
* Determine if we have reached the remote host on IP level, e.g.
* have a TCP connection. This turns TRUE before a possible SSL
* handshake has been started/done.
*/
bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex);
/**
* Determine if the connection is using SSL to the remote host
* (or will be once connected). This will return FALSE, if SSL
* is only used in proxying and not for the tunnel itself.
*/
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);
CURLcode Curl_conn_get_ip_info(struct Curl_easy *data,
struct connectdata *conn, int sockindex,
bool *is_ipv6, struct ip_quadruple *ipquad);
/**
* Connection provides multiplexing of easy handles at `socketindex`.
*/
bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex);
/**
* Return the HTTP version used on the FIRSTSOCKET connection filters
* or 0 if unknown. Value otherwise is 09, 10, 11, etc.
*/
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);
/* Get the negotiated ALPN protocol or NULL if none in play */
const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data,
struct connectdata *conn);
/**
* Close the filter chain at `sockindex` for connection `data->conn`.
* Filters remain in place and may be connected again afterwards.
*/
void Curl_conn_close(struct Curl_easy *data, int sockindex);
/**
* Shutdown the connection at `sockindex` non-blocking, using timeout
* from `data->set.shutdowntimeout`, default DEFAULT_SHUTDOWN_TIMEOUT_MS.
* Will return CURLE_OK and *done == FALSE if not finished.
*/
CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done);
/**
* Return if data is pending in some connection filter at chain
* `sockindex` for connection `data->conn`.
*/
bool Curl_conn_data_pending(struct Curl_easy *data,
int sockindex);
/**
* Return TRUE if any of the connection filters at chain `sockindex`
* have data still to send.
*/
bool Curl_conn_needs_flush(struct Curl_easy *data, int sockindex);
/**
* Flush any pending data on the connection filters at chain `sockindex`.
*/
CURLcode Curl_conn_flush(struct Curl_easy *data, int sockindex);
/**
* Return the socket used on data's connection for FIRSTSOCKET,
* querying filters if the whole chain has not connected yet.
* Returns CURL_SOCKET_BAD if not available.
*/
curl_socket_t Curl_conn_get_first_socket(struct Curl_easy *data);
/* 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 starting at `cf`.
*/
CURLcode Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct easy_pollset *ps);
/**
* Adjust pollset from filters installed at transfer's connection.
*/
CURLcode Curl_conn_adjust_pollset(struct Curl_easy *data,
struct connectdata *conn,
struct easy_pollset *ps);
/**
* Curl_poll() the filter chain at `cf` with timeout `timeout_ms`.
* Returns 0 on timeout, negative on error or number of sockets
* with requested poll events.
*/
int Curl_conn_cf_poll(struct Curl_cfilter *cf,
struct Curl_easy *data,
timediff_t timeout_ms);
/**
* 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 in `*pnread`or an error.
*/
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
* in `*pnwritten` or on error.
*/
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 wrapper 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
* a transfer.
*/
CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data);
/**
* Notify connection filters that the transfer represented by `data`
* is done with sending data (e.g. has uploaded everything).
*/
void Curl_conn_ev_data_done_send(struct Curl_easy *data);
/**
* Notify connection filters that the transfer represented by `data`
* is finished - eventually premature, e.g. before being complete.
*/
void Curl_conn_ev_data_done(struct Curl_easy *data, bool premature);
/**
* Notify connection filters that the transfer of data is paused/unpaused.
*/
CURLcode Curl_conn_ev_data_pause(struct Curl_easy *data, bool do_pause);
/**
* Check if FIRSTSOCKET's cfilter chain deems connection alive.
*/
bool Curl_conn_is_alive(struct Curl_easy *data, struct connectdata *conn,
bool *input_pending);
/**
* Try to upkeep the connection filters at sockindex.
*/
CURLcode Curl_conn_keep_alive(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
#ifdef UNITTESTS
void Curl_cf_def_close(struct Curl_cfilter *cf, struct Curl_easy *data);
#endif
/**
* 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
* expects to be able to handle at `sockindex`.
*/
size_t Curl_conn_get_max_concurrent(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
/**
* Get the underlying error code for a transfer stream or 0 if not known.
*/
int Curl_conn_get_stream_error(struct Curl_easy *data,
struct connectdata *conn,
int sockindex);
/**
* Get the index of the given socket in the connection's sockets.
* Useful in calling `Curl_conn_send()/Curl_conn_recv()` with the
* correct socket index.
*/
int Curl_conn_sockindex(struct Curl_easy *data, curl_socket_t sockfd);
/*
* Receive data on the connection, using FIRSTSOCKET/SECONDARYSOCKET.
* Will return CURLE_AGAIN iff blocked on receiving.
*/
CURLcode Curl_conn_recv(struct Curl_easy *data, int sockindex,
char *buf, size_t buffersize,
size_t *pnread);
/*
* Send data on the connection, using FIRSTSOCKET/SECONDARYSOCKET.
* Will return CURLE_AGAIN iff blocked on sending.
*/
CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
const void *buf, size_t blen, bool eos,
size_t *pnwritten);
/**
* Types and macros used to keep the current easy handle in filter calls,
* allowing for nested invocations. See #10336.
*
* `cf_call_data` is intended to be a member of the cfilter's `ctx` type.
* A filter defines the macro `CF_CTX_CALL_DATA` to give access to that.
*
* With all values 0, the default, this indicates that there is no cfilter
* call with `data` ongoing.
* Macro `CF_DATA_SAVE` preserves the current `cf_call_data` in a local
* variable and sets the `data` given, incrementing the `depth` counter.
*
* Macro `CF_DATA_RESTORE` restores the old values from the local variable,
* while checking that `depth` values are as expected (debug build), catching
* cases where a "lower" RESTORE was not called.
*
* Finally, macro `CF_DATA_CURRENT` gives the easy handle of the current
* invocation.
*/
struct cf_call_data {
struct Curl_easy *data;
#ifdef DEBUGBUILD
int depth;
#endif
};
/**
* define to access the `struct cf_call_data for a cfilter. Normally
* a member in the cfilter's `ctx`.
*
* #define CF_CTX_CALL_DATA(cf) -> struct cf_call_data instance
*/
#ifdef DEBUGBUILD
#define CF_DATA_SAVE(save, cf, data) \
do { \
(save) = CF_CTX_CALL_DATA(cf); \
DEBUGASSERT((save).data == NULL || (save).depth > 0); \
CF_CTX_CALL_DATA(cf).depth++; \
CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)CURL_UNCONST(data); \
} while(0)
#define CF_DATA_RESTORE(cf, save) \
do { \
DEBUGASSERT(CF_CTX_CALL_DATA(cf).depth == (save).depth + 1); \
DEBUGASSERT((save).data == NULL || (save).depth > 0); \
CF_CTX_CALL_DATA(cf) = (save); \
} while(0)
#else /* DEBUGBUILD */
#define CF_DATA_SAVE(save, cf, data) \
do { \
(save) = CF_CTX_CALL_DATA(cf); \
CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)CURL_UNCONST(data); \
} while(0)
#define CF_DATA_RESTORE(cf, save) \
do { \
CF_CTX_CALL_DATA(cf) = (save); \
} while(0)
#endif /* !DEBUGBUILD */
#define CF_DATA_CURRENT(cf) \
((cf)? (CF_CTX_CALL_DATA(cf).data) : NULL)
#endif /* HEADER_CURL_CFILTERS_H */
+93
View File
@@ -0,0 +1,93 @@
#ifndef HEADER_CURL_CONFIG_MAC_H
#define HEADER_CURL_CONFIG_MAC_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
/* =================================================================== */
/* Hand crafted config file for Mac OS 9 */
/* =================================================================== */
/* On macOS you must run configure to generate curl_config.h file */
/* =================================================================== */
#ifndef CURL_OS
#define CURL_OS "mac"
#endif
#include <ConditionalMacros.h>
#if TYPE_LONGLONG
#define HAVE_LONGLONG 1
#endif
/* Define if you want the built-in manual */
#define USE_MANUAL 1
#define HAVE_NETINET_IN_H 1
#define HAVE_NETDB_H 1
#define HAVE_ARPA_INET_H 1
#define HAVE_UNISTD_H 1
#define HAVE_NET_IF_H 1
#define HAVE_SYS_TYPES_H 1
#define HAVE_GETTIMEOFDAY 1
#define HAVE_FCNTL_H 1
#define HAVE_UTIME_H 1
#define HAVE_SYS_UTIME_H 1
#define HAVE_SYS_IOCTL_H 1
#define HAVE_ALARM 1
#define HAVE_FTRUNCATE 1
#define HAVE_UTIME 1
#define HAVE_SELECT 1
#define HAVE_SOCKET 1
#define HAVE_STRUCT_TIMEVAL 1
#define HAVE_SIGACTION 1
#define CURL_DISABLE_LDAP 1
#define HAVE_IOCTL_FIONBIO 1
#define SIZEOF_INT 4
#define SIZEOF_LONG 4
#define SIZEOF_SIZE_T 4
#ifdef HAVE_LONGLONG
#define SIZEOF_CURL_OFF_T 8
#else
#define SIZEOF_CURL_OFF_T 4
#endif
#define HAVE_RECV 1
#define RECV_TYPE_ARG1 int
#define RECV_TYPE_ARG2 void *
#define RECV_TYPE_ARG3 size_t
#define RECV_TYPE_ARG4 int
#define RECV_TYPE_RETV ssize_t
#define HAVE_SEND 1
#define SEND_TYPE_ARG1 int
#define SEND_QUAL_ARG2 const
#define SEND_TYPE_ARG2 void *
#define SEND_TYPE_ARG3 size_t
#define SEND_TYPE_ARG4 int
#define SEND_TYPE_RETV ssize_t
#endif /* HEADER_CURL_CONFIG_MAC_H */
+295
View File
@@ -0,0 +1,295 @@
#ifndef HEADER_CURL_CONFIG_OS400_H
#define HEADER_CURL_CONFIG_OS400_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
/* ================================================================ */
/* Hand crafted config file for OS/400 */
/* ================================================================ */
#pragma enum(int)
/* Define cpu-machine-OS */
#ifndef CURL_OS
#define CURL_OS "OS/400"
#endif
/* OS400 supports a 3-argument ASCII version of gethostbyaddr_r(), but its
* prototype is incompatible with the "standard" one (1st argument is not
* const). However, getaddrinfo() is supported (ASCII version defined as
* a local wrapper in setup-os400.h) in a threadsafe way: we can then
* configure getaddrinfo() as such and get rid of gethostbyname_r() without
* loss of threadsafeness. */
#undef HAVE_GETHOSTBYNAME_R
#undef HAVE_GETHOSTBYNAME_R_3
#undef HAVE_GETHOSTBYNAME_R_5
#undef HAVE_GETHOSTBYNAME_R_6
#define HAVE_GETADDRINFO
#define HAVE_GETADDRINFO_THREADSAFE
/* Define if you need the _REENTRANT define for some functions */
#undef NEED_REENTRANT
/* Define if you want to enable IPv6 support */
#define USE_IPV6
/* Define if struct sockaddr_in6 has the sin6_scope_id member */
#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1
/* Define this to 'int' if ssize_t is not an available typedefed type */
#undef ssize_t
/* Define to 1 if you have the alarm function. */
#define HAVE_ALARM 1
/* Define if you have the <arpa/inet.h> header file. */
#define HAVE_ARPA_INET_H
/* Define if you have the `closesocket' function. */
#undef HAVE_CLOSESOCKET
/* Define if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H
/* Define if you have the `geteuid' function. */
#define HAVE_GETEUID
/* Define if you have the `gethostname' function. */
#define HAVE_GETHOSTNAME
/* Define if you have the `getpass_r' function. */
#undef HAVE_GETPASS_R
/* Define to 1 if you have the getpeername function. */
#define HAVE_GETPEERNAME 1
/* Define if you have the `getpwuid' function. */
#define HAVE_GETPWUID
/* Define to 1 if you have the getsockname function. */
#define HAVE_GETSOCKNAME 1
/* Define if you have the `gettimeofday' function. */
#define HAVE_GETTIMEOFDAY
/* Define if you have the `timeval' struct. */
#define HAVE_STRUCT_TIMEVAL
/* Define if you have the <io.h> header file. */
#undef HAVE_IO_H
/* Define if you have GSS API. */
#define HAVE_GSSAPI
/* Define if you have the GNU gssapi libraries */
#undef HAVE_GSSGNU
/* Define if you have the <netdb.h> header file. */
#define HAVE_NETDB_H
/* Define if you have the <netinet/in.h> header file. */
#define HAVE_NETINET_IN_H
/* Define if you have the <net/if.h> header file. */
#define HAVE_NET_IF_H
/* Define if you have the <pwd.h> header file. */
#define HAVE_PWD_H
/* Define if you have the `select' function. */
#define HAVE_SELECT
/* Define if you have the `sigaction' function. */
#define HAVE_SIGACTION
/* Define if you have the `signal' function. */
#undef HAVE_SIGNAL
/* Define if you have the `socket' function. */
#define HAVE_SOCKET
/* The following define is needed on OS400 to enable strcmpi(), stricmp() and
strdup(). */
#define __cplusplus__strings__
/* Define if you have the `strcasecmp' function. */
#undef HAVE_STRCASECMP
/* Define if you have the `strcmpi' function. */
#define HAVE_STRCMPI
/* Define if you have the `stricmp' function. */
#define HAVE_STRICMP
/* Define if you have the `strdup' function. */
#define HAVE_STRDUP
/* Define if you have the <strings.h> header file. */
#define HAVE_STRINGS_H
/* Define if you have the <stropts.h> header file. */
#undef HAVE_STROPTS_H
/* Define if you have the <sys/param.h> header file. */
#define HAVE_SYS_PARAM_H
/* Define if you have the <sys/select.h> header file. */
#undef HAVE_SYS_SELECT_H
/* Define if you have the <sys/sockio.h> header file. */
#undef HAVE_SYS_SOCKIO_H
/* Define if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H
/* Define if you have the <sys/un.h> header file. */
#define HAVE_SYS_UN_H
/* Define if you have the <sys/ioctl.h> header file. */
#define HAVE_SYS_IOCTL_H
/* Define if you have the <termios.h> header file. */
#undef HAVE_TERMIOS_H
/* Define if you have the <termio.h> header file. */
#undef HAVE_TERMIO_H
/* Define if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H
/* The size of `int', as computed by sizeof. */
#define SIZEOF_INT 4
/* Define if the compiler supports the 'long long' data type. */
#define HAVE_LONGLONG
/* The size of a `long long', as computed by sizeof. */
#define SIZEOF_LONG_LONG 8
/* The size of `long', as computed by sizeof. */
#define SIZEOF_LONG 4
/* The size of `size_t', as computed by sizeof. */
#define SIZEOF_SIZE_T 4
/* The size of `curl_off_t', as computed by sizeof. */
#define SIZEOF_CURL_OFF_T 8
/* Define this if you have struct sockaddr_storage */
#define HAVE_STRUCT_SOCKADDR_STORAGE
/* Define if you have the ANSI C header files. */
#define STDC_HEADERS
/* Version number of package */
#undef VERSION
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Define for large files, on AIX-style hosts. */
#define _LARGE_FILES
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
/* Define to `unsigned' if <sys/types.h> does not define. */
#undef size_t
/* Define if you have a working ioctl FIONBIO function. */
#define HAVE_IOCTL_FIONBIO
/* Define if you have a working ioctl SIOCGIFADDR function. */
#define HAVE_IOCTL_SIOCGIFADDR
/* To disable LDAP */
#undef CURL_DISABLE_LDAP
/* Definition to make a library symbol externally visible. */
#define CURL_EXTERN_SYMBOL
/* Define if you have the ldap_url_parse procedure. */
/* #define HAVE_LDAP_URL_PARSE */ /* Disabled because of an IBM bug. */
/* Define if you have the recv function. */
#define HAVE_RECV
/* Define to the type of arg 1 for recv. */
#define RECV_TYPE_ARG1 int
/* Define to the type of arg 2 for recv. */
#define RECV_TYPE_ARG2 char *
/* Define to the type of arg 3 for recv. */
#define RECV_TYPE_ARG3 int
/* Define to the type of arg 4 for recv. */
#define RECV_TYPE_ARG4 int
/* Define to the function return type for recv. */
#define RECV_TYPE_RETV int
/* Define if you have the send function. */
#define HAVE_SEND
/* Define to the type of arg 1 for send. */
#define SEND_TYPE_ARG1 int
/* Define to the type qualifier of arg 2 for send. */
#define SEND_QUAL_ARG2
/* Define to the type of arg 2 for send. */
#define SEND_TYPE_ARG2 char *
/* Define to the type of arg 3 for send. */
#define SEND_TYPE_ARG3 int
/* Define to the type of arg 4 for send. */
#define SEND_TYPE_ARG4 int
/* Define to the function return type for send. */
#define SEND_TYPE_RETV int
/* Define to use the OS/400 crypto library. */
#define USE_OS400CRYPTO
/* Define to use Unix sockets. */
#define USE_UNIX_SOCKETS
/* Use the system keyring as the default CA bundle. */
#define CURL_CA_BUNDLE "/QIBM/UserData/ICSS/Cert/Server/DEFAULT.KDB"
/* ---------------------------------------------------------------- */
/* ADDITIONAL DEFINITIONS */
/* ---------------------------------------------------------------- */
/* The following must be defined BEFORE system header files inclusion. */
#define __ptr128 /* No teraspace. */
#define qadrt_use_fputc_inline /* Generate fputc() wrapper inline. */
#define qadrt_use_fread_inline /* Generate fread() wrapper inline. */
#define qadrt_use_fwrite_inline /* Generate fwrite() wrapper inline. */
#endif /* HEADER_CURL_CONFIG_OS400_H */
+133
View File
@@ -0,0 +1,133 @@
#ifndef HEADER_CURL_CONFIG_PLAN9_H
#define HEADER_CURL_CONFIG_PLAN9_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#define BUILDING_LIBCURL 1
#define CURL_CA_BUNDLE "/sys/lib/tls/ca.pem"
#define CURL_CA_PATH "/sys/lib/tls"
#define CURL_STATICLIB 1
#define USE_IPV6 1
#define CURL_DISABLE_LDAP 1
#define NEED_REENTRANT 1
#ifndef CURL_OS
#define CURL_OS "plan9"
#endif
#define STDC_HEADERS 1
#ifdef _BITS64
#error not implement
#else
#define SIZEOF_INT 4
#define SIZEOF_LONG 4
#define SIZEOF_OFF_T 8
#define SIZEOF_CURL_OFF_T 4 /* curl_off_t = timediff_t = int */
#define SIZEOF_SIZE_T 4
#define SIZEOF_TIME_T 4
#endif
#define HAVE_RECV 1
#define RECV_TYPE_ARG1 int
#define RECV_TYPE_ARG2 void *
#define RECV_TYPE_ARG3 int
#define RECV_TYPE_ARG4 int
#define RECV_TYPE_RETV int
#define HAVE_SELECT 1
#define HAVE_SEND 1
#define SEND_TYPE_ARG1 int
#define SEND_TYPE_ARG2 void *
#define SEND_QUAL_ARG2
#define SEND_TYPE_ARG3 int
#define SEND_TYPE_ARG4 int
#define SEND_TYPE_RETV int
#define HAVE_ALARM 1
#define HAVE_ARPA_INET_H 1
#define HAVE_BASENAME 1
#define HAVE_BOOL_T 1
#define HAVE_FCNTL 1
#define HAVE_FCNTL_H 1
#define HAVE_FREEADDRINFO 1
#define HAVE_FTRUNCATE 1
#define HAVE_GETADDRINFO 1
#define HAVE_GETEUID 1
#define HAVE_GETHOSTNAME 1
#define HAVE_GETPPID 1
#define HAVE_GETPWUID 1
#define HAVE_GETTIMEOFDAY 1
#define HAVE_GMTIME_R 1
#define HAVE_INET_NTOP 1
#define HAVE_INET_PTON 1
#define HAVE_LIBGEN_H 1
#define HAVE_LIBZ 1
#define HAVE_LOCALE_H 1
#define HAVE_LONGLONG 1
#define HAVE_NETDB_H 1
#define HAVE_NETINET_IN_H 1
#define HAVE_NETINET_TCP_H 1
#define HAVE_PWD_H 1
#define HAVE_SYS_SELECT_H 1
#define USE_OPENSSL 1
#define HAVE_PIPE 1
#define HAVE_POLL 1
#define HAVE_POLL_H 1
#define HAVE_PTHREAD_H 1
#define HAVE_SETLOCALE 1
#define HAVE_SIGACTION 1
#define HAVE_SIGNAL 1
#define HAVE_SIGSETJMP 1
#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1
#define HAVE_SOCKET 1
#define HAVE_STDBOOL_H 1
#define HAVE_STRCASECMP 1
#define HAVE_STRDUP 1
#define HAVE_STRUCT_TIMEVAL 1
#define HAVE_SYS_IOCTL_H 1
#define HAVE_SYS_PARAM_H 1
#define HAVE_SYS_RESOURCE_H 1
#define HAVE_SYS_TYPES_H 1
#define HAVE_SYS_UN_H 1
#define HAVE_TERMIOS_H 1
#define HAVE_UNISTD_H 1
#define HAVE_UTIME 1
#define HAVE_UTIME_H 1
#define HAVE_POSIX_STRERROR_R 1
#define HAVE_STRERROR_R 1
#define USE_MANUAL 1
#define __attribute__(x)
#ifndef __cplusplus
#undef inline
#endif
#endif /* HEADER_CURL_CONFIG_PLAN9_H */
+234
View File
@@ -0,0 +1,234 @@
#ifndef HEADER_CURL_CONFIG_RISCOS_H
#define HEADER_CURL_CONFIG_RISCOS_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
/* ================================================================ */
/* Hand crafted config file for RISC OS */
/* ================================================================ */
/* Define cpu-machine-OS */
#ifndef CURL_OS
#define CURL_OS "ARM-RISC OS"
#endif
/* Define if you want the built-in manual */
#define USE_MANUAL
/* Define if you have the gethostbyname_r() function with 3 arguments */
#undef HAVE_GETHOSTBYNAME_R_3
/* Define if you have the gethostbyname_r() function with 5 arguments */
#undef HAVE_GETHOSTBYNAME_R_5
/* Define if you have the gethostbyname_r() function with 6 arguments */
#undef HAVE_GETHOSTBYNAME_R_6
/* Define if you need the _REENTRANT define for some functions */
#undef NEED_REENTRANT
/* Define if you want to enable IPv6 support */
#undef USE_IPV6
/* Define if struct sockaddr_in6 has the sin6_scope_id member */
#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1
/* Define this to 'int' if ssize_t is not an available typedefed type */
#undef ssize_t
/* Define if you have the alarm function. */
#define HAVE_ALARM
/* Define if you have the <arpa/inet.h> header file. */
#define HAVE_ARPA_INET_H
/* Define if you have the `closesocket' function. */
#undef HAVE_CLOSESOCKET
/* Define if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H
/* Define if you have the `ftruncate' function. */
#define HAVE_FTRUNCATE
/* Define if getaddrinfo exists and works */
#define HAVE_GETADDRINFO
/* Define if you have the `geteuid' function. */
#undef HAVE_GETEUID
/* Define if you have the `gethostbyname_r' function. */
#undef HAVE_GETHOSTBYNAME_R
/* Define if you have the `gethostname' function. */
#define HAVE_GETHOSTNAME
/* Define if you have the `getpass_r' function. */
#undef HAVE_GETPASS_R
/* Define if you have the `getpwuid' function. */
#undef HAVE_GETPWUID
/* Define if you have the `gettimeofday' function. */
#define HAVE_GETTIMEOFDAY
/* Define if you have the `timeval' struct. */
#define HAVE_STRUCT_TIMEVAL
/* Define if you have the <io.h> header file. */
#undef HAVE_IO_H
/* Define if you have the <netdb.h> header file. */
#define HAVE_NETDB_H
/* Define if you have the <netinet/in.h> header file. */
#define HAVE_NETINET_IN_H
/* Define if you have the <net/if.h> header file. */
#define HAVE_NET_IF_H
/* Define if you have the <pwd.h> header file. */
#undef HAVE_PWD_H
/* Define if you have the `select' function. */
#define HAVE_SELECT
/* Define if you have the `sigaction' function. */
#undef HAVE_SIGACTION
/* Define if you have the `signal' function. */
#define HAVE_SIGNAL
/* Define if you have the `socket' function. */
#define HAVE_SOCKET
/* Define if you have the `strcasecmp' function. */
#undef HAVE_STRCASECMP
/* Define if you have the `strcmpi' function. */
#undef HAVE_STRCMPI
/* Define if you have the `strdup' function. */
#define HAVE_STRDUP
/* Define if you have the `stricmp' function. */
#define HAVE_STRICMP
/* Define if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define if you have the <sys/param.h> header file. */
#undef HAVE_SYS_PARAM_H
/* Define if you have the <sys/select.h> header file. */
#undef HAVE_SYS_SELECT_H
/* Define if you have the <sys/sockio.h> header file. */
#undef HAVE_SYS_SOCKIO_H
/* Define if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H
/* Define if you have the <termios.h> header file. */
#define HAVE_TERMIOS_H
/* Define if you have the <termio.h> header file. */
#undef HAVE_TERMIO_H
/* Define if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H
/* The size of `int', as computed by sizeof. */
#define SIZEOF_INT 4
/* The size of `long long', as computed by sizeof. */
#undef SIZEOF_LONG_LONG
/* The size of `size_t', as computed by sizeof. */
#define SIZEOF_SIZE_T 4
/* Define if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Version number of package */
#undef VERSION
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
/* Define to `unsigned' if <sys/types.h> does not define. */
#undef size_t
/* Define to `int' if <sys/types.h> does not define. */
#undef ssize_t
/* Define if you have a working ioctl FIONBIO function. */
#define HAVE_IOCTL_FIONBIO
/* to disable LDAP */
#define CURL_DISABLE_LDAP
/* Define if you have the recv function. */
#define HAVE_RECV 1
/* Define to the type of arg 1 for recv. */
#define RECV_TYPE_ARG1 int
/* Define to the type of arg 2 for recv. */
#define RECV_TYPE_ARG2 void *
/* Define to the type of arg 3 for recv. */
#define RECV_TYPE_ARG3 size_t
/* Define to the type of arg 4 for recv. */
#define RECV_TYPE_ARG4 int
/* Define to the function return type for recv. */
#define RECV_TYPE_RETV ssize_t
/* Define if you have the send function. */
#define HAVE_SEND 1
/* Define to the type of arg 1 for send. */
#define SEND_TYPE_ARG1 int
/* Define to the type qualifier of arg 2 for send. */
#define SEND_QUAL_ARG2 const
/* Define to the type of arg 2 for send. */
#define SEND_TYPE_ARG2 void *
/* Define to the type of arg 3 for send. */
#define SEND_TYPE_ARG3 size_t
/* Define to the type of arg 4 for send. */
#define SEND_TYPE_ARG4 int
/* Define to the function return type for send. */
#define SEND_TYPE_RETV ssize_t
#endif /* HEADER_CURL_CONFIG_RISCOS_H */
+493
View File
@@ -0,0 +1,493 @@
#ifndef HEADER_CURL_CONFIG_WIN32_H
#define HEADER_CURL_CONFIG_WIN32_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
/* ================================================================ */
/* Hand crafted config file for Windows */
/* ================================================================ */
#ifndef UNDER_CE
/* Define some minimum and default build targets for Visual Studio */
#ifdef _MSC_VER
/* VS2012 default target settings and minimum build target check. */
# if _MSC_VER >= 1700
/* The minimum and default build targets for VS2012 are Vista and 8,
respectively, unless Update 1 is installed and the v110_xp toolset
is chosen. */
# ifdef _USING_V110_SDK71_
# define VS2012_MIN_TARGET 0x0501 /* XP */
# define VS2012_DEF_TARGET 0x0501 /* XP */
# else
# define VS2012_MIN_TARGET 0x0600 /* Vista */
# define VS2012_DEF_TARGET 0x0602 /* 8 */
# endif
# ifndef _WIN32_WINNT
# define _WIN32_WINNT VS2012_DEF_TARGET
# endif
# ifndef WINVER
# define WINVER VS2012_DEF_TARGET
# endif
# if (_WIN32_WINNT < VS2012_MIN_TARGET) || (WINVER < VS2012_MIN_TARGET)
# ifdef _USING_V110_SDK71_
# error VS2012 does not support build targets prior to Windows XP
# else
# error VS2012 does not support build targets prior to Windows Vista
# endif
# endif
/* Default target settings and minimum build target check for
VS2008 and VS2010 */
# else
# define VS2008_MIN_TARGET 0x0501 /* XP */
/* VS2008 default build target is Windows Vista (0x0600).
We override default target to be Windows XP. */
# define VS2008_DEF_TARGET 0x0501 /* XP */
# ifndef _WIN32_WINNT
# define _WIN32_WINNT VS2008_DEF_TARGET
# endif
# ifndef WINVER
# define WINVER VS2008_DEF_TARGET
# endif
# if (_WIN32_WINNT < VS2008_MIN_TARGET) || (WINVER < VS2008_MIN_TARGET)
# error VS2008 does not support build targets prior to Windows XP
# endif
# endif
#endif /* _MSC_VER */
#endif /* UNDER_CE */
/* ---------------------------------------------------------------- */
/* HEADER FILES */
/* ---------------------------------------------------------------- */
/* Define if you have the <arpa/inet.h> header file. */
/* #define HAVE_ARPA_INET_H 1 */
#ifndef UNDER_CE
/* Define if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1 /* exists on __MINGW32CE__ */
/* Define if you have the <io.h> header file. */
#define HAVE_IO_H 1 /* exists on __MINGW32CE__ */
/* Define if you have the <locale.h> header file. */
#define HAVE_LOCALE_H 1
#endif
/* Define if you have the <netdb.h> header file. */
/* #define HAVE_NETDB_H 1 */
/* Define if you have the <netinet/in.h> header file. */
/* #define HAVE_NETINET_IN_H 1 */
/* Define to 1 if you have the <stdbool.h> header file. */
#ifndef UNDER_CE
#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || defined(__MINGW32__)
#define HAVE_STDBOOL_H 1 /* exists on __MINGW32CE__ */
#endif
#endif
/* Define to 1 if you have the <stdint.h> header file. */
#if (defined(_MSC_VER) && (_MSC_VER >= 1600)) || defined(__MINGW32__)
#define HAVE_STDINT_H 1
#endif
/* Define if you have the <sys/param.h> header file. */
#ifdef __MINGW32__
#define HAVE_SYS_PARAM_H 1
#endif
/* Define if you have the <sys/select.h> header file. */
/* #define HAVE_SYS_SELECT_H 1 */
/* Define if you have the <sys/sockio.h> header file. */
/* #define HAVE_SYS_SOCKIO_H 1 */
/* Define if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define if you have the <sys/utime.h> header file. */
#define HAVE_SYS_UTIME_H 1
/* Define if you have the <termio.h> header file. */
/* #define HAVE_TERMIO_H 1 */
/* Define if you have the <termios.h> header file. */
/* #define HAVE_TERMIOS_H 1 */
/* Define if you have the <unistd.h> header file. */
#ifdef __MINGW32__
#define HAVE_UNISTD_H 1
#endif
/* Define to 1 if you have the <libgen.h> header file. */
#ifdef __MINGW32__
#define HAVE_LIBGEN_H 1
#endif
/* ---------------------------------------------------------------- */
/* OTHER HEADER INFO */
/* ---------------------------------------------------------------- */
/* Define if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define to 1 if bool is an available type. */
#ifndef UNDER_CE
#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || defined(__MINGW32__)
#define HAVE_BOOL_T 1 /* exists on __MINGW32CE__ */
#endif
#endif
/* ---------------------------------------------------------------- */
/* FUNCTIONS */
/* ---------------------------------------------------------------- */
/* Define if you have the closesocket function. */
#define HAVE_CLOSESOCKET 1
/* Define if you have the ftruncate function. */
#ifdef __MINGW32__
#define HAVE_FTRUNCATE 1
#endif
/* Define to 1 if you have the `getpeername' function. */
#define HAVE_GETPEERNAME 1
/* Define to 1 if you have the getsockname function. */
#define HAVE_GETSOCKNAME 1
/* Define if you have the gethostname function. */
#define HAVE_GETHOSTNAME 1
/* Define if you have the gettimeofday function. */
#ifdef __MINGW32__
#define HAVE_GETTIMEOFDAY 1
#endif
/* Define if you have the ioctlsocket function. */
#define HAVE_IOCTLSOCKET 1
/* Define if you have a working ioctlsocket FIONBIO function. */
#define HAVE_IOCTLSOCKET_FIONBIO 1
/* Define if you have the select function. */
#define HAVE_SELECT 1
#ifndef UNDER_CE
/* Define if you have the setlocale function. */
#define HAVE_SETLOCALE 1
/* Define if you have the setmode function. */
#define HAVE_SETMODE 1
/* Define if you have the _setmode function. */
#define HAVE__SETMODE 1
#endif
/* Define if you have the socket function. */
#define HAVE_SOCKET 1
/* Define if you have the strdup function. */
#define HAVE_STRDUP 1
/* Define if you have the utime function. */
#define HAVE_UTIME 1
/* Define if you have the recv function. */
#define HAVE_RECV 1
/* Define to the type of arg 1 for recv. */
#define RECV_TYPE_ARG1 SOCKET
/* Define to the type of arg 2 for recv. */
#define RECV_TYPE_ARG2 char *
/* Define to the type of arg 3 for recv. */
#define RECV_TYPE_ARG3 int
/* Define to the type of arg 4 for recv. */
#define RECV_TYPE_ARG4 int
/* Define to the function return type for recv. */
#define RECV_TYPE_RETV int
/* Define if you have the send function. */
#define HAVE_SEND 1
/* Define to the type of arg 1 for send. */
#define SEND_TYPE_ARG1 SOCKET
/* Define to the type qualifier of arg 2 for send. */
#define SEND_QUAL_ARG2 const
/* Define to the type of arg 2 for send. */
#define SEND_TYPE_ARG2 char *
/* Define to the type of arg 3 for send. */
#define SEND_TYPE_ARG3 int
/* Define to the type of arg 4 for send. */
#define SEND_TYPE_ARG4 int
/* Define to the function return type for send. */
#define SEND_TYPE_RETV int
/* Define to 1 if you have the snprintf function. */
#if (defined(_MSC_VER) && (_MSC_VER >= 1900)) || defined(__MINGW32__)
#define HAVE_SNPRINTF 1
#endif
/* Must always use local implementations on Windows. */
/* Define to 1 if you have an IPv6 capable working inet_ntop function. */
/* #undef HAVE_INET_NTOP */
/* Define to 1 if you have an IPv6 capable working inet_pton function. */
/* #undef HAVE_INET_PTON */
/* Define to 1 if you have the `basename' function. */
#ifdef __MINGW32__
#define HAVE_BASENAME 1
#endif
/* Define to 1 if you have the signal function. */
#ifndef UNDER_CE
#define HAVE_SIGNAL 1
#endif
/* ---------------------------------------------------------------- */
/* TYPEDEF REPLACEMENTS */
/* ---------------------------------------------------------------- */
/* Define if ssize_t is not an available 'typedefed' type. */
#ifndef _SSIZE_T_DEFINED
# ifdef __MINGW32__
# elif defined(_WIN64)
# define _SSIZE_T_DEFINED
# define ssize_t __int64
# else
# define _SSIZE_T_DEFINED
# define ssize_t int
# endif
#endif
/* ---------------------------------------------------------------- */
/* TYPE SIZES */
/* ---------------------------------------------------------------- */
/* Define to the size of `int', as computed by sizeof. */
#define SIZEOF_INT 4
/* Define to the size of `long long', as computed by sizeof. */
/* #define SIZEOF_LONG_LONG 8 */
/* Define to the size of `long', as computed by sizeof. */
#define SIZEOF_LONG 4
/* Define to the size of `size_t', as computed by sizeof. */
#ifdef _WIN64
# define SIZEOF_SIZE_T 8
#else
# define SIZEOF_SIZE_T 4
#endif
/* Define to the size of `curl_off_t', as computed by sizeof. */
#define SIZEOF_CURL_OFF_T 8
/* ---------------------------------------------------------------- */
/* COMPILER SPECIFIC */
/* ---------------------------------------------------------------- */
/* Define to nothing if compiler does not support 'const' qualifier. */
/* #define const */
/* Define to nothing if compiler does not support 'volatile' qualifier. */
/* #define volatile */
/* Windows should not have HAVE_GMTIME_R defined */
/* #undef HAVE_GMTIME_R */
/* Define if the compiler supports the 'long long' data type. */
#if defined(_MSC_VER) || defined(__MINGW32__)
#define HAVE_LONGLONG 1
#endif
/* Default to 64-bit time_t unless _USE_32BIT_TIME_T is defined */
#if defined(_MSC_VER) || defined(__MINGW32__)
# ifndef _USE_32BIT_TIME_T
# define SIZEOF_TIME_T 8
# else
# define SIZEOF_TIME_T 4
# endif
#endif
/* Windows XP is required for freeaddrinfo, getaddrinfo */
#ifndef UNDER_CE
#define HAVE_FREEADDRINFO 1
#define HAVE_GETADDRINFO 1
#define HAVE_GETADDRINFO_THREADSAFE 1
#endif
/* ---------------------------------------------------------------- */
/* STRUCT RELATED */
/* ---------------------------------------------------------------- */
/* Define if you have struct sockaddr_storage. */
#define HAVE_STRUCT_SOCKADDR_STORAGE 1
/* Define if you have struct timeval. */
#define HAVE_STRUCT_TIMEVAL 1
/* Define if struct sockaddr_in6 has the sin6_scope_id member. */
#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1
/* ---------------------------------------------------------------- */
/* LARGE FILE SUPPORT */
/* ---------------------------------------------------------------- */
#ifndef UNDER_CE
#if defined(_MSC_VER) || defined(__MINGW32__)
# define USE_WIN32_LARGE_FILES
/* Number of bits in a file offset, on hosts where this is settable. */
# ifdef __MINGW32__
# ifndef _FILE_OFFSET_BITS
# define _FILE_OFFSET_BITS 64
# endif
# endif
#endif
/* Define to the size of `off_t', as computed by sizeof. */
#if defined(__MINGW32__) && \
defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
# define SIZEOF_OFF_T 8
#else
# define SIZEOF_OFF_T 4
#endif
#endif /* UNDER_CE */
/* ---------------------------------------------------------------- */
/* DNS RESOLVER SPECIALTY */
/* ---------------------------------------------------------------- */
/*
* Undefine both USE_ARES and USE_THREADS_WIN32 for synchronous DNS.
*/
/* Define to enable c-ares asynchronous DNS lookups. */
/* #define USE_ARES 1 */
/* Default define to enable threaded asynchronous DNS lookups. */
#if !defined(USE_SYNC_DNS) && !defined(USE_ARES) && \
!defined(USE_THREADS_WIN32)
# define USE_THREADS_WIN32 1
#endif
#if defined(USE_ARES) && defined(USE_THREADS_WIN32)
# error "Only one DNS lookup specialty may be defined at most"
#endif
/* ---------------------------------------------------------------- */
/* LDAP SUPPORT */
/* ---------------------------------------------------------------- */
#ifdef CURL_HAS_OPENLDAP_LDAPSDK
#undef USE_WIN32_LDAP
#define HAVE_LDAP_URL_PARSE 1
#elif !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE)
#undef HAVE_LDAP_URL_PARSE
#define HAVE_LDAP_SSL 1
#define USE_WIN32_LDAP 1
#endif
/* Define to use the Windows crypto library. */
#ifndef CURL_WINDOWS_UWP
#define USE_WIN32_CRYPTO
#endif
/* Define to use Unix sockets. */
#ifndef UNDER_CE
#define USE_UNIX_SOCKETS
#endif
/* ---------------------------------------------------------------- */
/* ADDITIONAL DEFINITIONS */
/* ---------------------------------------------------------------- */
/* Define cpu-machine-OS */
#ifndef CURL_OS
# ifdef UNDER_CE
# ifdef _M_ARM
# define CURL_OS "arm-pc-win32ce"
# else
# define CURL_OS "i386-pc-win32ce"
# endif
# else /* !UNDER_CE */
# if defined(_M_IX86) || defined(__i386__) /* x86 (MSVC or gcc) */
# define CURL_OS "i386-pc-win32"
# elif defined(_M_X64) || defined(__x86_64__) /* x86_64 (VS2005+ or gcc) */
# define CURL_OS "x86_64-pc-win32"
# elif defined(_M_IA64) || defined(__ia64__) /* Itanium */
# define CURL_OS "ia64-pc-win32"
# elif defined(_M_ARM_NT) || defined(__arm__) /* ARMv7-Thumb2 */
# define CURL_OS "thumbv7a-pc-win32"
# elif defined(_M_ARM64) || defined(__aarch64__) /* ARM64 (Windows 10) */
# define CURL_OS "aarch64-pc-win32"
# else
# define CURL_OS "unknown-pc-win32"
# endif
# endif /* UNDER_CE */
#endif /* !CURL_OS */
/* ---------------------------------------------------------------- */
/* Windows CE */
/* ---------------------------------------------------------------- */
#ifdef UNDER_CE
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#define CURL_DISABLE_FILE 1
#define CURL_DISABLE_TELNET 1
#define CURL_DISABLE_LDAP 1
#ifndef _MSC_VER
/* !checksrc! disable BANNEDFUNC 1 */
extern int stat(const char *path, struct stat *buffer);
#endif
#endif /* UNDER_CE */
#endif /* HEADER_CURL_CONFIG_WIN32_H */
+928
View File
@@ -0,0 +1,928 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include <curl/curl.h>
#include "urldata.h"
#include "url.h"
#include "cfilters.h"
#include "progress.h"
#include "multiif.h"
#include "multi_ev.h"
#include "sendf.h"
#include "cshutdn.h"
#include "conncache.h"
#include "http_negotiate.h"
#include "http_ntlm.h"
#include "share.h"
#include "sigpipe.h"
#include "connect.h"
#include "select.h"
#include "curlx/strparse.h"
#include "uint-table.h"
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
#define CPOOL_IS_LOCKED(c) ((c) && (c)->locked)
#define CPOOL_LOCK(c,d) \
do { \
if((c)) { \
if(CURL_SHARE_KEEP_CONNECT((c)->share)) \
Curl_share_lock((d), CURL_LOCK_DATA_CONNECT, \
CURL_LOCK_ACCESS_SINGLE); \
DEBUGASSERT(!(c)->locked); \
(c)->locked = TRUE; \
} \
} while(0)
#define CPOOL_UNLOCK(c,d) \
do { \
if((c)) { \
DEBUGASSERT((c)->locked); \
(c)->locked = FALSE; \
if(CURL_SHARE_KEEP_CONNECT((c)->share)) \
Curl_share_unlock((d), CURL_LOCK_DATA_CONNECT); \
} \
} while(0)
/* A list of connections to the same destination. */
struct cpool_bundle {
struct Curl_llist conns; /* connections in the bundle */
size_t dest_len; /* total length of destination, including NUL */
char dest[1]; /* destination of bundle, allocated to keep dest_len bytes */
};
static void cpool_discard_conn(struct cpool *cpool,
struct Curl_easy *data,
struct connectdata *conn,
bool aborted);
static struct cpool_bundle *cpool_bundle_create(const char *dest)
{
struct cpool_bundle *bundle;
size_t dest_len = strlen(dest) + 1;
bundle = calloc(1, sizeof(*bundle) + dest_len - 1);
if(!bundle)
return NULL;
Curl_llist_init(&bundle->conns, NULL);
bundle->dest_len = dest_len;
memcpy(bundle->dest, dest, bundle->dest_len);
return bundle;
}
static void cpool_bundle_destroy(struct cpool_bundle *bundle)
{
DEBUGASSERT(!Curl_llist_count(&bundle->conns));
free(bundle);
}
/* Add a connection to a bundle */
static void cpool_bundle_add(struct cpool_bundle *bundle,
struct connectdata *conn)
{
DEBUGASSERT(!Curl_node_llist(&conn->cpool_node));
Curl_llist_append(&bundle->conns, conn, &conn->cpool_node);
conn->bits.in_cpool = TRUE;
}
/* Remove a connection from a bundle */
static void cpool_bundle_remove(struct cpool_bundle *bundle,
struct connectdata *conn)
{
(void)bundle;
DEBUGASSERT(Curl_node_llist(&conn->cpool_node) == &bundle->conns);
Curl_node_remove(&conn->cpool_node);
conn->bits.in_cpool = FALSE;
}
static void cpool_bundle_free_entry(void *freethis)
{
cpool_bundle_destroy((struct cpool_bundle *)freethis);
}
void Curl_cpool_init(struct cpool *cpool,
struct Curl_easy *idata,
struct Curl_share *share,
size_t size)
{
Curl_hash_init(&cpool->dest2bundle, size, Curl_hash_str,
curlx_str_key_compare, cpool_bundle_free_entry);
DEBUGASSERT(idata);
cpool->idata = idata;
cpool->share = share;
cpool->initialised = TRUE;
}
/* Return the "first" connection in the pool or NULL. */
static struct connectdata *cpool_get_first(struct cpool *cpool)
{
struct Curl_hash_iterator iter;
struct Curl_hash_element *he;
struct cpool_bundle *bundle;
struct Curl_llist_node *conn_node;
Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
for(he = Curl_hash_next_element(&iter); he;
he = Curl_hash_next_element(&iter)) {
bundle = he->ptr;
conn_node = Curl_llist_head(&bundle->conns);
if(conn_node)
return Curl_node_elem(conn_node);
}
return NULL;
}
static struct cpool_bundle *cpool_find_bundle(struct cpool *cpool,
struct connectdata *conn)
{
return Curl_hash_pick(&cpool->dest2bundle,
conn->destination, strlen(conn->destination) + 1);
}
static void cpool_remove_bundle(struct cpool *cpool,
struct cpool_bundle *bundle)
{
if(!cpool)
return;
Curl_hash_delete(&cpool->dest2bundle, bundle->dest, bundle->dest_len);
}
static void cpool_remove_conn(struct cpool *cpool,
struct connectdata *conn)
{
struct Curl_llist *list = Curl_node_llist(&conn->cpool_node);
DEBUGASSERT(cpool);
if(list) {
/* The connection is certainly in the pool, but where? */
struct cpool_bundle *bundle = cpool_find_bundle(cpool, conn);
if(bundle && (list == &bundle->conns)) {
cpool_bundle_remove(bundle, conn);
if(!Curl_llist_count(&bundle->conns))
cpool_remove_bundle(cpool, bundle);
conn->bits.in_cpool = FALSE;
cpool->num_conn--;
}
else {
/* Should have been in the bundle list */
DEBUGASSERT(NULL);
}
}
}
void Curl_cpool_destroy(struct cpool *cpool)
{
if(cpool && cpool->initialised && cpool->idata) {
struct connectdata *conn;
SIGPIPE_VARIABLE(pipe_st);
CURL_TRC_M(cpool->idata, "%s[CPOOL] destroy, %zu connections",
cpool->share ? "[SHARE] " : "", cpool->num_conn);
/* Move all connections to the shutdown list */
sigpipe_init(&pipe_st);
CPOOL_LOCK(cpool, cpool->idata);
conn = cpool_get_first(cpool);
while(conn) {
cpool_remove_conn(cpool, conn);
sigpipe_apply(cpool->idata, &pipe_st);
connclose(conn, "kill all");
cpool_discard_conn(cpool, cpool->idata, conn, FALSE);
conn = cpool_get_first(cpool);
}
CPOOL_UNLOCK(cpool, cpool->idata);
sigpipe_restore(&pipe_st);
Curl_hash_destroy(&cpool->dest2bundle);
}
}
static struct cpool *cpool_get_instance(struct Curl_easy *data)
{
if(data) {
if(CURL_SHARE_KEEP_CONNECT(data->share))
return &data->share->cpool;
else if(data->multi_easy)
return &data->multi_easy->cpool;
else if(data->multi)
return &data->multi->cpool;
}
return NULL;
}
void Curl_cpool_xfer_init(struct Curl_easy *data)
{
struct cpool *cpool = cpool_get_instance(data);
DEBUGASSERT(cpool);
if(cpool) {
CPOOL_LOCK(cpool, data);
/* the identifier inside the connection cache */
data->id = cpool->next_easy_id++;
if(cpool->next_easy_id <= 0)
cpool->next_easy_id = 0;
data->state.lastconnect_id = -1;
CPOOL_UNLOCK(cpool, data);
}
else {
/* We should not get here, but in a non-debug build, do something */
data->id = 0;
data->state.lastconnect_id = -1;
}
}
static struct cpool_bundle *
cpool_add_bundle(struct cpool *cpool, struct connectdata *conn)
{
struct cpool_bundle *bundle;
bundle = cpool_bundle_create(conn->destination);
if(!bundle)
return NULL;
if(!Curl_hash_add(&cpool->dest2bundle,
bundle->dest, bundle->dest_len, bundle)) {
cpool_bundle_destroy(bundle);
return NULL;
}
return bundle;
}
static struct connectdata *
cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle)
{
struct Curl_llist_node *curr;
timediff_t highscore = -1;
timediff_t score;
struct curltime now;
struct connectdata *oldest_idle = NULL;
struct connectdata *conn;
now = curlx_now();
curr = Curl_llist_head(&bundle->conns);
while(curr) {
conn = Curl_node_elem(curr);
if(!CONN_INUSE(conn)) {
/* Set higher score for the age passed since the connection was used */
score = curlx_timediff(now, conn->lastused);
if(score > highscore) {
highscore = score;
oldest_idle = conn;
}
}
curr = Curl_node_next(curr);
}
return oldest_idle;
}
static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool)
{
struct Curl_hash_iterator iter;
struct Curl_llist_node *curr;
struct Curl_hash_element *he;
struct connectdata *oldest_idle = NULL;
struct cpool_bundle *bundle;
struct curltime now;
timediff_t highscore = -1;
timediff_t score;
now = curlx_now();
Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
for(he = Curl_hash_next_element(&iter); he;
he = Curl_hash_next_element(&iter)) {
struct connectdata *conn;
bundle = he->ptr;
for(curr = Curl_llist_head(&bundle->conns); curr;
curr = Curl_node_next(curr)) {
conn = Curl_node_elem(curr);
if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only)
continue;
/* Set higher score for the age passed since the connection was used */
score = curlx_timediff(now, conn->lastused);
if(score > highscore) {
highscore = score;
oldest_idle = conn;
}
}
}
return oldest_idle;
}
int Curl_cpool_check_limits(struct Curl_easy *data,
struct connectdata *conn)
{
struct cpool *cpool = cpool_get_instance(data);
struct cpool_bundle *bundle;
size_t dest_limit = 0;
size_t total_limit = 0;
size_t shutdowns;
int result = CPOOL_LIMIT_OK;
if(!cpool)
return CPOOL_LIMIT_OK;
if(cpool->idata->multi) {
dest_limit = cpool->idata->multi->max_host_connections;
total_limit = cpool->idata->multi->max_total_connections;
}
if(!dest_limit && !total_limit)
return CPOOL_LIMIT_OK;
CPOOL_LOCK(cpool, cpool->idata);
if(dest_limit) {
size_t live;
bundle = cpool_find_bundle(cpool, conn);
live = bundle ? Curl_llist_count(&bundle->conns) : 0;
shutdowns = Curl_cshutdn_dest_count(data, conn->destination);
while((live + shutdowns) >= dest_limit) {
if(shutdowns) {
/* close one connection in shutdown right away, if we can */
if(!Curl_cshutdn_close_oldest(data, conn->destination))
break;
}
else if(!bundle)
break;
else {
struct connectdata *oldest_idle = NULL;
/* The bundle is full. Extract the oldest connection that may
* be removed now, if there is one. */
oldest_idle = cpool_bundle_get_oldest_idle(bundle);
if(!oldest_idle)
break;
/* disconnect the old conn and continue */
CURL_TRC_M(data, "Discarding connection #%"
FMT_OFF_T " from %zu to reach destination "
"limit of %zu", oldest_idle->connection_id,
Curl_llist_count(&bundle->conns), dest_limit);
Curl_conn_terminate(cpool->idata, oldest_idle, FALSE);
/* in case the bundle was destroyed in disconnect, look it up again */
bundle = cpool_find_bundle(cpool, conn);
live = bundle ? Curl_llist_count(&bundle->conns) : 0;
}
shutdowns = Curl_cshutdn_dest_count(cpool->idata, conn->destination);
}
if((live + shutdowns) >= dest_limit) {
result = CPOOL_LIMIT_DEST;
goto out;
}
}
if(total_limit) {
shutdowns = Curl_cshutdn_count(cpool->idata);
while((cpool->num_conn + shutdowns) >= total_limit) {
if(shutdowns) {
/* close one connection in shutdown right away, if we can */
if(!Curl_cshutdn_close_oldest(data, NULL))
break;
}
else {
struct connectdata *oldest_idle = cpool_get_oldest_idle(cpool);
if(!oldest_idle)
break;
/* disconnect the old conn and continue */
CURL_TRC_M(data, "Discarding connection #%"
FMT_OFF_T " from %zu to reach total "
"limit of %zu",
oldest_idle->connection_id, cpool->num_conn, total_limit);
Curl_conn_terminate(cpool->idata, oldest_idle, FALSE);
}
shutdowns = Curl_cshutdn_count(cpool->idata);
}
if((cpool->num_conn + shutdowns) >= total_limit) {
result = CPOOL_LIMIT_TOTAL;
goto out;
}
}
out:
CPOOL_UNLOCK(cpool, cpool->idata);
return result;
}
CURLcode Curl_cpool_add(struct Curl_easy *data,
struct connectdata *conn)
{
CURLcode result = CURLE_OK;
struct cpool_bundle *bundle = NULL;
struct cpool *cpool = cpool_get_instance(data);
DEBUGASSERT(conn);
DEBUGASSERT(cpool);
if(!cpool)
return CURLE_FAILED_INIT;
CPOOL_LOCK(cpool, data);
bundle = cpool_find_bundle(cpool, conn);
if(!bundle) {
bundle = cpool_add_bundle(cpool, conn);
if(!bundle) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
}
cpool_bundle_add(bundle, conn);
conn->connection_id = cpool->next_connection_id++;
cpool->num_conn++;
CURL_TRC_M(data, "[CPOOL] added connection %" FMT_OFF_T ". "
"The cache now contains %zu members",
conn->connection_id, cpool->num_conn);
out:
CPOOL_UNLOCK(cpool, data);
return result;
}
/* This function iterates the entire connection pool and calls the function
func() with the connection pointer as the first argument and the supplied
'param' argument as the other.
The cpool lock is still held when the callback is called. It needs it,
so that it can safely continue traversing the lists once the callback
returns.
Returns TRUE if the loop was aborted due to the callback's return code.
Return 0 from func() to continue the loop, return 1 to abort it.
*/
static bool cpool_foreach(struct Curl_easy *data,
struct cpool *cpool,
void *param,
int (*func)(struct Curl_easy *data,
struct connectdata *conn, void *param))
{
struct Curl_hash_iterator iter;
struct Curl_hash_element *he;
if(!cpool)
return FALSE;
Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
he = Curl_hash_next_element(&iter);
while(he) {
struct Curl_llist_node *curr;
struct cpool_bundle *bundle = he->ptr;
he = Curl_hash_next_element(&iter);
curr = Curl_llist_head(&bundle->conns);
while(curr) {
/* Yes, we need to update curr before calling func(), because func()
might decide to remove the connection */
struct connectdata *conn = Curl_node_elem(curr);
curr = Curl_node_next(curr);
if(func(data, conn, param) == 1) {
return TRUE;
}
}
}
return FALSE;
}
/*
* A connection (already in the pool) has become idle. Do any
* cleanups in regard to the pool's limits.
*
* Return TRUE if idle connection kept in pool, FALSE if closed.
*/
bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
struct connectdata *conn)
{
unsigned int maxconnects;
struct connectdata *oldest_idle = NULL;
struct cpool *cpool = cpool_get_instance(data);
bool kept = TRUE;
if(!data->multi->maxconnects) {
unsigned int running = Curl_multi_xfers_running(data->multi);
maxconnects = (running <= UINT_MAX / 4) ? running * 4 : UINT_MAX;
}
else {
maxconnects = data->multi->maxconnects;
}
conn->lastused = curlx_now(); /* it was used up until now */
if(cpool && maxconnects) {
/* may be called form a callback already under lock */
bool do_lock = !CPOOL_IS_LOCKED(cpool);
if(do_lock)
CPOOL_LOCK(cpool, data);
if(cpool->num_conn > maxconnects) {
infof(data, "Connection pool is full, closing the oldest of %zu/%u",
cpool->num_conn, maxconnects);
oldest_idle = cpool_get_oldest_idle(cpool);
kept = (oldest_idle != conn);
if(oldest_idle) {
Curl_conn_terminate(data, oldest_idle, FALSE);
}
}
if(do_lock)
CPOOL_UNLOCK(cpool, data);
}
return kept;
}
bool Curl_cpool_find(struct Curl_easy *data,
const char *destination,
Curl_cpool_conn_match_cb *conn_cb,
Curl_cpool_done_match_cb *done_cb,
void *userdata)
{
struct cpool *cpool = cpool_get_instance(data);
struct cpool_bundle *bundle;
bool result = FALSE;
DEBUGASSERT(cpool);
DEBUGASSERT(conn_cb);
if(!cpool)
return FALSE;
CPOOL_LOCK(cpool, data);
bundle = Curl_hash_pick(&cpool->dest2bundle,
CURL_UNCONST(destination),
strlen(destination) + 1);
if(bundle) {
struct Curl_llist_node *curr = Curl_llist_head(&bundle->conns);
while(curr) {
struct connectdata *conn = Curl_node_elem(curr);
/* Get next node now. callback might discard current */
curr = Curl_node_next(curr);
if(conn_cb(conn, userdata)) {
result = TRUE;
break;
}
}
}
if(done_cb) {
result = done_cb(result, userdata);
}
CPOOL_UNLOCK(cpool, data);
return result;
}
static void cpool_discard_conn(struct cpool *cpool,
struct Curl_easy *data,
struct connectdata *conn,
bool aborted)
{
bool done = FALSE;
DEBUGASSERT(data);
DEBUGASSERT(!data->conn);
DEBUGASSERT(cpool);
DEBUGASSERT(!conn->bits.in_cpool);
/*
* If this connection is not marked to force-close, leave it open if there
* are other users of it
*/
if(CONN_INUSE(conn) && !aborted) {
CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T
" still in use by %u transfers", conn->connection_id,
CONN_ATTACHED(conn));
return;
}
/* treat the connection as aborted in CONNECT_ONLY situations, we do
* not know what the APP did with it. */
if(conn->connect_only)
aborted = TRUE;
conn->bits.aborted = aborted;
/* We do not shutdown dead connections. The term 'dead' can be misleading
* here, as we also mark errored connections/transfers as 'dead'.
* If we do a shutdown for an aborted transfer, the server might think
* it was successful otherwise (for example an ftps: upload). This is
* not what we want. */
if(aborted)
done = TRUE;
if(!done) {
/* Attempt to shutdown the connection right away. */
Curl_cshutdn_run_once(cpool->idata, conn, &done);
}
if(done || !data->multi)
Curl_cshutdn_terminate(cpool->idata, conn, FALSE);
else
Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn);
}
void Curl_conn_terminate(struct Curl_easy *data,
struct connectdata *conn,
bool aborted)
{
struct cpool *cpool = cpool_get_instance(data);
bool do_lock;
DEBUGASSERT(cpool);
DEBUGASSERT(data && !data->conn);
if(!cpool)
return;
/* If this connection is not marked to force-close, leave it open if there
* are other users of it */
if(CONN_INUSE(conn) && !aborted) {
DEBUGASSERT(0); /* does this ever happen? */
DEBUGF(infof(data, "Curl_disconnect when inuse: %u", CONN_ATTACHED(conn)));
return;
}
/* This method may be called while we are under lock, e.g. from a
* user callback in find. */
do_lock = !CPOOL_IS_LOCKED(cpool);
if(do_lock)
CPOOL_LOCK(cpool, data);
if(conn->bits.in_cpool) {
cpool_remove_conn(cpool, conn);
DEBUGASSERT(!conn->bits.in_cpool);
}
/* treat the connection as aborted in CONNECT_ONLY situations,
* so no graceful shutdown is attempted. */
if(conn->connect_only)
aborted = TRUE;
if(data->multi) {
/* Add it to the multi's cpool for shutdown handling */
infof(data, "%s connection #%" FMT_OFF_T,
aborted ? "closing" : "shutting down", conn->connection_id);
cpool_discard_conn(&data->multi->cpool, data, conn, aborted);
}
else {
/* No multi available, terminate */
infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id);
Curl_cshutdn_terminate(cpool->idata, conn, !aborted);
}
if(do_lock)
CPOOL_UNLOCK(cpool, data);
}
struct cpool_reaper_ctx {
struct curltime now;
};
static int cpool_reap_dead_cb(struct Curl_easy *data,
struct connectdata *conn, void *param)
{
struct cpool_reaper_ctx *rctx = param;
if((!CONN_INUSE(conn) && conn->bits.no_reuse) ||
Curl_conn_seems_dead(conn, data, &rctx->now)) {
/* stop the iteration here, pass back the connection that was pruned */
Curl_conn_terminate(data, conn, FALSE);
return 1;
}
return 0; /* continue iteration */
}
/*
* This function scans the data's connection pool for half-open/dead
* connections, closes and removes them.
* The cleanup is done at most once per second.
*
* When called, this transfer has no connection attached.
*/
void Curl_cpool_prune_dead(struct Curl_easy *data)
{
struct cpool *cpool = cpool_get_instance(data);
struct cpool_reaper_ctx rctx;
timediff_t elapsed;
if(!cpool)
return;
rctx.now = curlx_now();
CPOOL_LOCK(cpool, data);
elapsed = curlx_timediff(rctx.now, cpool->last_cleanup);
if(elapsed >= 1000L) {
while(cpool_foreach(data, cpool, &rctx, cpool_reap_dead_cb))
;
cpool->last_cleanup = rctx.now;
}
CPOOL_UNLOCK(cpool, data);
}
static int conn_upkeep(struct Curl_easy *data,
struct connectdata *conn,
void *param)
{
struct curltime *now = param;
Curl_conn_upkeep(data, conn, now);
return 0; /* continue iteration */
}
CURLcode Curl_cpool_upkeep(void *data)
{
struct cpool *cpool = cpool_get_instance(data);
struct curltime now = curlx_now();
if(!cpool)
return CURLE_OK;
CPOOL_LOCK(cpool, data);
cpool_foreach(data, cpool, &now, conn_upkeep);
CPOOL_UNLOCK(cpool, data);
return CURLE_OK;
}
struct cpool_find_ctx {
curl_off_t id;
struct connectdata *conn;
};
static int cpool_find_conn(struct Curl_easy *data,
struct connectdata *conn, void *param)
{
struct cpool_find_ctx *fctx = param;
(void)data;
if(conn->connection_id == fctx->id) {
fctx->conn = conn;
return 1;
}
return 0;
}
struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
curl_off_t conn_id)
{
struct cpool *cpool = cpool_get_instance(data);
struct cpool_find_ctx fctx;
if(!cpool)
return NULL;
fctx.id = conn_id;
fctx.conn = NULL;
CPOOL_LOCK(cpool, data);
cpool_foreach(data, cpool, &fctx, cpool_find_conn);
CPOOL_UNLOCK(cpool, data);
return fctx.conn;
}
struct cpool_do_conn_ctx {
curl_off_t id;
Curl_cpool_conn_do_cb *cb;
void *cbdata;
};
static int cpool_do_conn(struct Curl_easy *data,
struct connectdata *conn, void *param)
{
struct cpool_do_conn_ctx *dctx = param;
(void)data;
if(conn->connection_id == dctx->id) {
dctx->cb(conn, data, dctx->cbdata);
return 1;
}
return 0;
}
void Curl_cpool_do_by_id(struct Curl_easy *data, curl_off_t conn_id,
Curl_cpool_conn_do_cb *cb, void *cbdata)
{
struct cpool *cpool = cpool_get_instance(data);
struct cpool_do_conn_ctx dctx;
if(!cpool)
return;
dctx.id = conn_id;
dctx.cb = cb;
dctx.cbdata = cbdata;
CPOOL_LOCK(cpool, data);
cpool_foreach(data, cpool, &dctx, cpool_do_conn);
CPOOL_UNLOCK(cpool, data);
}
void Curl_cpool_do_locked(struct Curl_easy *data,
struct connectdata *conn,
Curl_cpool_conn_do_cb *cb, void *cbdata)
{
struct cpool *cpool = cpool_get_instance(data);
if(cpool) {
CPOOL_LOCK(cpool, data);
cb(conn, data, cbdata);
CPOOL_UNLOCK(cpool, data);
}
else
cb(conn, data, cbdata);
}
static int cpool_mark_stale(struct Curl_easy *data,
struct connectdata *conn, void *param)
{
(void)data;
(void)param;
conn->bits.no_reuse = TRUE;
return 0;
}
static int cpool_reap_no_reuse(struct Curl_easy *data,
struct connectdata *conn, void *param)
{
(void)data;
(void)param;
if(!CONN_INUSE(conn) && conn->bits.no_reuse) {
Curl_conn_terminate(data, conn, FALSE);
return 1;
}
return 0; /* continue iteration */
}
void Curl_cpool_nw_changed(struct Curl_easy *data)
{
struct cpool *cpool = cpool_get_instance(data);
if(cpool) {
CPOOL_LOCK(cpool, data);
cpool_foreach(data, cpool, NULL, cpool_mark_stale);
while(cpool_foreach(data, cpool, NULL, cpool_reap_no_reuse))
;
CPOOL_UNLOCK(cpool, data);
}
}
#if 0
/* Useful for debugging the connection pool */
void Curl_cpool_print(struct cpool *cpool)
{
struct Curl_hash_iterator iter;
struct Curl_llist_node *curr;
struct Curl_hash_element *he;
if(!cpool)
return;
curl_mfprintf(stderr, "=Bundle cache=\n");
Curl_hash_start_iterate(cpool->dest2bundle, &iter);
he = Curl_hash_next_element(&iter);
while(he) {
struct cpool_bundle *bundle;
struct connectdata *conn;
bundle = he->ptr;
curl_mfprintf(stderr, "%s -", he->key);
curr = Curl_llist_head(bundle->conns);
while(curr) {
conn = Curl_node_elem(curr);
curl_mfprintf(stderr, " [%p %d]", (void *)conn, conn->refcount);
curr = Curl_node_next(curr);
}
curl_mfprintf(stderr, "\n");
he = Curl_hash_next_element(&iter);
}
}
#endif
+170
View File
@@ -0,0 +1,170 @@
#ifndef HEADER_CURL_CONNCACHE_H
#define HEADER_CURL_CONNCACHE_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
*
* 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/curl.h>
#include "curlx/timeval.h"
struct connectdata;
struct Curl_easy;
struct curl_pollfds;
struct Curl_waitfds;
struct Curl_multi;
struct Curl_share;
/**
* Terminate the connection, e.g. close and destroy.
* If the connection is in a cpool, remove it.
* If a `cshutdn` is available (e.g. data has a multi handle),
* pass the connection to that for controlled shutdown.
* Otherwise terminate it right away.
* Takes ownership of `conn`.
* `data` should not be attached to a connection.
*/
void Curl_conn_terminate(struct Curl_easy *data,
struct connectdata *conn,
bool aborted);
struct cpool {
/* the pooled connections, bundled per destination */
struct Curl_hash dest2bundle;
size_t num_conn;
curl_off_t next_connection_id;
curl_off_t next_easy_id;
struct curltime last_cleanup;
struct Curl_easy *idata; /* internal handle for maintenance */
struct Curl_share *share; /* != NULL if pool belongs to share */
BIT(locked);
BIT(initialised);
};
/* Init the pool, pass multi only if pool is owned by it.
* Cannot fail.
*/
void Curl_cpool_init(struct cpool *cpool,
struct Curl_easy *idata,
struct Curl_share *share,
size_t size);
/* Destroy all connections and free all members */
void Curl_cpool_destroy(struct cpool *connc);
/* Init the transfer to be used within its connection pool.
* Assigns `data->id`. */
void Curl_cpool_xfer_init(struct Curl_easy *data);
/* Get the connection with the given id from `data`'s conn pool. */
struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
curl_off_t conn_id);
/* Add the connection to the pool. */
CURLcode Curl_cpool_add(struct Curl_easy *data,
struct connectdata *conn) WARN_UNUSED_RESULT;
/**
* Return if the pool has reached its configured limits for adding
* the given connection. Will try to discard the oldest, idle
* connections to make space.
*/
#define CPOOL_LIMIT_OK 0
#define CPOOL_LIMIT_DEST 1
#define CPOOL_LIMIT_TOTAL 2
int Curl_cpool_check_limits(struct Curl_easy *data,
struct connectdata *conn);
/* Return of conn is suitable. If so, stops iteration. */
typedef bool Curl_cpool_conn_match_cb(struct connectdata *conn,
void *userdata);
/* Act on the result of the find, may override it. */
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 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
connections were present.
*/
bool Curl_cpool_find(struct Curl_easy *data,
const char *destination,
Curl_cpool_conn_match_cb *conn_cb,
Curl_cpool_done_match_cb *done_cb,
void *userdata);
/*
* A connection (already in the pool) is now idle. Do any
* cleanups in regard to the pool's limits.
*
* Return TRUE if idle connection kept in pool, FALSE if closed.
*/
bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
struct connectdata *conn);
/**
* This function scans the data's connection pool for half-open/dead
* connections, closes and removes them.
* The cleanup is done at most once per second.
*
* When called, this transfer has no connection attached.
*/
void Curl_cpool_prune_dead(struct Curl_easy *data);
/**
* Perform upkeep actions on connections in the transfer's pool.
*/
CURLcode Curl_cpool_upkeep(void *data);
typedef void Curl_cpool_conn_do_cb(struct connectdata *conn,
struct Curl_easy *data,
void *cbdata);
/**
* Invoke the callback on the pool's connection with the
* given connection id (if it exists).
*/
void Curl_cpool_do_by_id(struct Curl_easy *data,
curl_off_t conn_id,
Curl_cpool_conn_do_cb *cb, void *cbdata);
/**
* Invoked the callback for the given data + connection under the
* connection pool's lock.
* The callback is always invoked, even if the transfer has no connection
* pool associated.
*/
void Curl_cpool_do_locked(struct Curl_easy *data,
struct connectdata *conn,
Curl_cpool_conn_do_cb *cb, void *cbdata);
/* Close all unused connections, prevent reuse of existing ones. */
void Curl_cpool_nw_changed(struct Curl_easy *data);
#endif /* HEADER_CURL_CONNCACHE_H */
+630
View File
@@ -0,0 +1,630 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h> /* <netinet/tcp.h> may need it */
#endif
#ifdef HAVE_SYS_UN_H
#include <sys/un.h> /* for sockaddr_un */
#endif
#ifdef HAVE_LINUX_TCP_H
#include <linux/tcp.h>
#elif defined(HAVE_NETINET_TCP_H)
#include <netinet/tcp.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
#ifdef HAVE_NETDB_H
#include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef __VMS
#include <in.h>
#include <inet.h>
#endif
#include "urldata.h"
#include "sendf.h"
#include "if2ip.h"
#include "strerror.h"
#include "cfilters.h"
#include "connect.h"
#include "cf-haproxy.h"
#include "cf-https-connect.h"
#include "cf-ip-happy.h"
#include "cf-socket.h"
#include "select.h"
#include "url.h" /* for Curl_safefree() */
#include "multiif.h"
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
#include "curlx/inet_ntop.h"
#include "curlx/inet_pton.h"
#include "vtls/vtls.h" /* for vtsl cfilters */
#include "progress.h"
#include "curlx/warnless.h"
#include "conncache.h"
#include "multihandle.h"
#include "share.h"
#include "http_proxy.h"
#include "socks.h"
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
#if !defined(CURL_DISABLE_ALTSVC) || defined(USE_HTTPSRR)
enum alpnid Curl_alpn2alpnid(const char *name, size_t len)
{
if(len == 2) {
if(curl_strnequal(name, "h1", 2))
return ALPN_h1;
if(curl_strnequal(name, "h2", 2))
return ALPN_h2;
if(curl_strnequal(name, "h3", 2))
return ALPN_h3;
}
else if(len == 8) {
if(curl_strnequal(name, "http/1.1", 8))
return ALPN_h1;
}
return ALPN_none; /* unknown, probably rubbish input */
}
#endif
/*
* Curl_timeleft() returns the amount of milliseconds left allowed for the
* transfer/connection. If the value is 0, there is no timeout (ie there is
* infinite time left). If the value is negative, the timeout time has already
* elapsed.
* @param data the transfer to check on
* @param nowp timestamp to use for calculation, NULL to use curlx_now()
* @param duringconnect TRUE iff connect timeout is also taken into account.
* @unittest: 1303
*/
timediff_t Curl_timeleft(struct Curl_easy *data,
struct curltime *nowp,
bool duringconnect)
{
timediff_t timeleft_ms = 0;
timediff_t ctimeleft_ms = 0;
struct curltime now;
/* The duration of a connect and the total transfer are calculated from two
different time-stamps. It can end up with the total timeout being reached
before the connect timeout expires and we must acknowledge whichever
timeout that is reached first. The total timeout is set per entire
operation, while the connect timeout is set per connect. */
if((!data->set.timeout || data->set.connect_only) && !duringconnect)
return 0; /* no timeout in place or checked, return "no limit" */
if(!nowp) {
now = curlx_now();
nowp = &now;
}
if(data->set.timeout) {
timeleft_ms = data->set.timeout -
curlx_timediff(*nowp, data->progress.t_startop);
if(!timeleft_ms)
timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
if(!duringconnect)
return timeleft_ms; /* no connect check, this is it */
}
if(duringconnect) {
timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ?
data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
ctimeleft_ms = ctimeout_ms -
curlx_timediff(*nowp, data->progress.t_startsingle);
if(!ctimeleft_ms)
ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
if(!timeleft_ms)
return ctimeleft_ms; /* no general timeout, this is it */
}
/* return minimal time left or max amount already expired */
return (ctimeleft_ms < timeleft_ms) ? ctimeleft_ms : timeleft_ms;
}
void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
int timeout_ms, struct curltime *nowp)
{
struct curltime now;
struct connectdata *conn = data->conn;
DEBUGASSERT(conn);
if(!nowp) {
now = curlx_now();
nowp = &now;
}
conn->shutdown.start[sockindex] = *nowp;
conn->shutdown.timeout_ms = (timeout_ms > 0) ?
(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)
Curl_expire_ex(data, nowp, conn->shutdown.timeout_ms,
EXPIRE_SHUTDOWN);
}
timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
struct curltime *nowp)
{
struct curltime now;
timediff_t left_ms;
if(!conn->shutdown.start[sockindex].tv_sec ||
(conn->shutdown.timeout_ms <= 0))
return 0; /* not started or no limits */
if(!nowp) {
now = curlx_now();
nowp = &now;
}
left_ms = conn->shutdown.timeout_ms -
curlx_timediff(*nowp, conn->shutdown.start[sockindex]);
return left_ms ? left_ms : -1;
}
timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn,
struct curltime *nowp)
{
timediff_t left_ms = 0, ms;
struct curltime now;
int i;
for(i = 0; conn->shutdown.timeout_ms && (i < 2); ++i) {
if(!conn->shutdown.start[i].tv_sec)
continue;
if(!nowp) {
now = curlx_now();
nowp = &now;
}
ms = Curl_shutdown_timeleft(conn, i, nowp);
if(ms && (!left_ms || ms < left_ms))
left_ms = ms;
}
return left_ms;
}
void Curl_shutdown_clear(struct Curl_easy *data, int sockindex)
{
struct curltime *pt = &data->conn->shutdown.start[sockindex];
memset(pt, 0, sizeof(*pt));
}
bool Curl_shutdown_started(struct Curl_easy *data, int sockindex)
{
struct curltime *pt = &data->conn->shutdown.start[sockindex];
return (pt->tv_sec > 0) || (pt->tv_usec > 0);
}
/* 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)
{
struct sockaddr_in *si = NULL;
#ifdef USE_IPV6
struct sockaddr_in6 *si6 = NULL;
#endif
#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
struct sockaddr_un *su = NULL;
#else
(void)salen;
#endif
switch(sa->sa_family) {
case AF_INET:
si = (struct sockaddr_in *)(void *) sa;
if(curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) {
unsigned short us_port = ntohs(si->sin_port);
*port = us_port;
return TRUE;
}
break;
#ifdef USE_IPV6
case AF_INET6:
si6 = (struct sockaddr_in6 *)(void *) sa;
if(curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr,
MAX_IPADR_LEN)) {
unsigned short us_port = ntohs(si6->sin6_port);
*port = us_port;
return TRUE;
}
break;
#endif
#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
case AF_UNIX:
if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) {
su = (struct sockaddr_un*)sa;
curl_msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
}
else
addr[0] = 0; /* socket with no name */
*port = 0;
return TRUE;
#endif
default:
break;
}
addr[0] = '\0';
*port = 0;
CURL_SETERRNO(SOCKEAFNOSUPPORT);
return FALSE;
}
/*
* Used to extract socket and connectdata struct for the most recent
* transfer on the given Curl_easy.
*
* The returned socket will be CURL_SOCKET_BAD in case of failure!
*/
curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
struct connectdata **connp)
{
DEBUGASSERT(data);
/* this works for an easy handle:
* - that has been used for curl_easy_perform()
* - that is associated with a multi handle, and whose connection
* was detached with CURLOPT_CONNECT_ONLY
*/
if(data->state.lastconnect_id != -1) {
struct connectdata *conn;
conn = Curl_cpool_get_conn(data, data->state.lastconnect_id);
if(!conn) {
data->state.lastconnect_id = -1;
return CURL_SOCKET_BAD;
}
if(connp)
/* only store this if the caller cares for it */
*connp = conn;
return conn->sock[FIRSTSOCKET];
}
return CURL_SOCKET_BAD;
}
/*
* Curl_conncontrol() marks streams or connection for closure.
*/
void Curl_conncontrol(struct connectdata *conn,
int ctrl /* see defines in header */
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
, const char *reason
#endif
)
{
/* close if a connection, or a stream that is not multiplexed. */
/* This function will be called both before and after this connection is
associated with a transfer. */
bool closeit, is_multiplex;
DEBUGASSERT(conn);
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
(void)reason; /* useful for debugging */
#endif
is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET);
closeit = (ctrl == CONNCTRL_CONNECTION) ||
((ctrl == CONNCTRL_STREAM) && !is_multiplex);
if((ctrl == CONNCTRL_STREAM) && is_multiplex)
; /* stream signal on multiplex conn never affects close state */
else if((bit)closeit != conn->bits.close) {
conn->bits.close = closeit; /* the only place in the source code that
should assign this bit */
}
}
typedef enum {
CF_SETUP_INIT,
CF_SETUP_CNNCT_EYEBALLS,
CF_SETUP_CNNCT_SOCKS,
CF_SETUP_CNNCT_HTTP_PROXY,
CF_SETUP_CNNCT_HAPROXY,
CF_SETUP_CNNCT_SSL,
CF_SETUP_DONE
} cf_setup_state;
struct cf_setup_ctx {
cf_setup_state state;
int ssl_mode;
int transport;
};
static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
struct Curl_dns_entry *dns = data->state.dns[cf->sockindex];
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
}
/* connect current sub-chain */
connect_sub_chain:
if(!dns)
return CURLE_FAILED_INIT;
if(cf->next && !cf->next->connected) {
result = Curl_conn_cf_connect(cf->next, data, done);
if(result || !*done)
return result;
}
if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
result = cf_ip_happy_insert_after(cf, data, ctx->transport);
if(result)
return result;
ctx->state = CF_SETUP_CNNCT_EYEBALLS;
if(!cf->next || !cf->next->connected)
goto connect_sub_chain;
}
/* sub-chain connected, do we need to add more? */
#ifndef CURL_DISABLE_PROXY
if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) {
result = Curl_cf_socks_proxy_insert_after(cf, data);
if(result)
return result;
ctx->state = CF_SETUP_CNNCT_SOCKS;
if(!cf->next || !cf->next->connected)
goto connect_sub_chain;
}
if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) {
#ifdef USE_SSL
if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype)
&& !Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
result = Curl_cf_ssl_proxy_insert_after(cf, data);
if(result)
return result;
}
#endif /* USE_SSL */
#ifndef CURL_DISABLE_HTTP
if(cf->conn->bits.tunnel_proxy) {
result = Curl_cf_http_proxy_insert_after(cf, data);
if(result)
return result;
}
#endif /* !CURL_DISABLE_HTTP */
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
if(!cf->next || !cf->next->connected)
goto connect_sub_chain;
}
#endif /* !CURL_DISABLE_PROXY */
if(ctx->state < CF_SETUP_CNNCT_HAPROXY) {
#ifndef CURL_DISABLE_PROXY
if(data->set.haproxyprotocol) {
if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
failf(data, "haproxy protocol not support with SSL "
"encryption in place (QUIC?)");
return CURLE_UNSUPPORTED_PROTOCOL;
}
result = Curl_cf_haproxy_insert_after(cf, data);
if(result)
return result;
}
#endif /* !CURL_DISABLE_PROXY */
ctx->state = CF_SETUP_CNNCT_HAPROXY;
if(!cf->next || !cf->next->connected)
goto connect_sub_chain;
}
if(ctx->state < CF_SETUP_CNNCT_SSL) {
#ifdef USE_SSL
if((ctx->ssl_mode == CURL_CF_SSL_ENABLE
|| (ctx->ssl_mode != CURL_CF_SSL_DISABLE
&& cf->conn->handler->flags & PROTOPT_SSL)) /* we want SSL */
&& !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
result = Curl_cf_ssl_insert_after(cf, data);
if(result)
return result;
}
#endif /* USE_SSL */
ctx->state = CF_SETUP_CNNCT_SSL;
if(!cf->next || !cf->next->connected)
goto connect_sub_chain;
}
ctx->state = CF_SETUP_DONE;
cf->connected = TRUE;
*done = TRUE;
return CURLE_OK;
}
static void cf_setup_close(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURL_TRC_CF(data, cf, "close");
cf->connected = FALSE;
ctx->state = CF_SETUP_INIT;
if(cf->next) {
cf->next->cft->do_close(cf->next, data);
Curl_conn_cf_discard_chain(&cf->next, data);
}
}
static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_setup_ctx *ctx = cf->ctx;
(void)data;
CURL_TRC_CF(data, cf, "destroy");
Curl_safefree(ctx);
}
struct Curl_cftype Curl_cft_setup = {
"SETUP",
0,
CURL_LOG_LVL_NONE,
cf_setup_destroy,
cf_setup_connect,
cf_setup_close,
Curl_cf_def_shutdown,
Curl_cf_def_adjust_pollset,
Curl_cf_def_data_pending,
Curl_cf_def_send,
Curl_cf_def_recv,
Curl_cf_def_cntrl,
Curl_cf_def_conn_is_alive,
Curl_cf_def_conn_keep_alive,
Curl_cf_def_query,
};
static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
int transport,
int ssl_mode)
{
struct Curl_cfilter *cf = NULL;
struct cf_setup_ctx *ctx;
CURLcode result = CURLE_OK;
(void)data;
ctx = calloc(1, sizeof(*ctx));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
ctx->state = CF_SETUP_INIT;
ctx->ssl_mode = ssl_mode;
ctx->transport = transport;
result = Curl_cf_create(&cf, &Curl_cft_setup, ctx);
if(result)
goto out;
ctx = NULL;
out:
*pcf = result ? NULL : cf;
if(ctx) {
free(ctx);
}
return result;
}
static CURLcode cf_setup_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
int transport,
int ssl_mode)
{
struct Curl_cfilter *cf;
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
result = cf_setup_create(&cf, data, transport, ssl_mode);
if(result)
goto out;
Curl_conn_cf_add(data, conn, sockindex, cf);
out:
return result;
}
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
int transport,
int ssl_mode)
{
struct Curl_cfilter *cf;
CURLcode result;
DEBUGASSERT(data);
result = cf_setup_create(&cf, data, transport, ssl_mode);
if(result)
goto out;
Curl_conn_cf_insert_after(cf_at, cf);
out:
return result;
}
CURLcode Curl_conn_setup(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
struct Curl_dns_entry *dns,
int ssl_mode)
{
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
DEBUGASSERT(conn->handler);
DEBUGASSERT(dns);
Curl_resolv_unlink(data, &data->state.dns[sockindex]);
data->state.dns[sockindex] = dns;
#ifndef CURL_DISABLE_HTTP
if(!conn->cfilter[sockindex] &&
conn->handler->protocol == CURLPROTO_HTTPS) {
DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE);
result = Curl_cf_https_setup(data, conn, sockindex);
if(result)
goto out;
}
#endif /* !CURL_DISABLE_HTTP */
/* Still no cfilter set, apply default. */
if(!conn->cfilter[sockindex]) {
result = cf_setup_add(data, conn, sockindex,
conn->transport_wanted, ssl_mode);
if(result)
goto out;
}
DEBUGASSERT(conn->cfilter[sockindex]);
out:
if(result)
Curl_resolv_unlink(data, &data->state.dns[sockindex]);
return result;
}
void Curl_conn_set_multiplex(struct connectdata *conn)
{
if(!conn->bits.multiplex) {
conn->bits.multiplex = TRUE;
if(conn->attached_multi) {
Curl_multi_connchanged(conn->attached_multi);
}
}
}
+131
View File
@@ -0,0 +1,131 @@
#ifndef HEADER_CURL_CONNECT_H
#define HEADER_CURL_CONNECT_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include "curlx/nonblock.h" /* for curlx_nonblock() */
#include "sockaddr.h"
#include "curlx/timeval.h"
struct Curl_dns_entry;
struct ip_quadruple;
enum alpnid Curl_alpn2alpnid(const char *name, size_t len);
/* generic function that returns how much time there is left to run, according
to the timeouts set */
timediff_t Curl_timeleft(struct Curl_easy *data,
struct curltime *nowp,
bool duringconnect);
#define DEFAULT_CONNECT_TIMEOUT 300000 /* milliseconds == five minutes */
#define DEFAULT_SHUTDOWN_TIMEOUT_MS (2 * 1000)
void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
int timeout_ms, struct curltime *nowp);
/* return how much time there is left to shutdown the connection at
* sockindex. Returns 0 if there is no limit or shutdown has not started. */
timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
struct curltime *nowp);
/* return how much time there is left to shutdown the connection.
* Returns 0 if there is no limit or shutdown has not started. */
timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn,
struct curltime *nowp);
void Curl_shutdown_clear(struct Curl_easy *data, int sockindex);
/* TRUE iff shutdown has been started */
bool Curl_shutdown_started(struct Curl_easy *data, int sockindex);
/*
* Used to extract socket and connectdata struct for the most recent
* transfer on the given Curl_easy.
*
* The returned socket will be CURL_SOCKET_BAD in case of failure!
*/
curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
struct connectdata **connp);
bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
char *addr, int *port);
/*
* Curl_conncontrol() marks the end of a connection/stream. The 'closeit'
* argument specifies if it is the end of a connection or a stream.
*
* For stream-based protocols (such as HTTP/2), a stream close will not cause
* a connection close. Other protocols will close the connection for both
* cases.
*
* It sets the bit.close bit to TRUE (with an explanation for debug builds),
* when the connection will close.
*/
#define CONNCTRL_KEEP 0 /* undo a marked closure */
#define CONNCTRL_CONNECTION 1
#define CONNCTRL_STREAM 2
void Curl_conncontrol(struct connectdata *conn,
int closeit
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
, const char *reason
#endif
);
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
#define streamclose(x,y) Curl_conncontrol(x, CONNCTRL_STREAM, y)
#define connclose(x,y) Curl_conncontrol(x, CONNCTRL_CONNECTION, y)
#define connkeep(x,y) Curl_conncontrol(x, CONNCTRL_KEEP, y)
#else /* if !DEBUGBUILD || CURL_DISABLE_VERBOSE_STRINGS */
#define streamclose(x,y) Curl_conncontrol(x, CONNCTRL_STREAM)
#define connclose(x,y) Curl_conncontrol(x, CONNCTRL_CONNECTION)
#define connkeep(x,y) Curl_conncontrol(x, CONNCTRL_KEEP)
#endif
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
int transport,
int ssl_mode);
/**
* Setup the cfilters at `sockindex` in connection `conn`.
* If no filter chain is installed yet, inspects the configuration
* in `data` and `conn? to install a suitable filter chain.
*/
CURLcode Curl_conn_setup(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
struct Curl_dns_entry *dns,
int ssl_mode);
/* Set conn to allow multiplexing. */
void Curl_conn_set_multiplex(struct connectdata *conn);
extern struct Curl_cftype Curl_cft_setup;
#endif /* HEADER_CURL_CONNECT_H */
+860
View File
@@ -0,0 +1,860 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include "urldata.h"
#include <curl/curl.h>
#include <stddef.h>
#ifdef HAVE_LIBZ
#include <zlib.h>
#endif
#ifdef HAVE_BROTLI
#if defined(__GNUC__) || defined(__clang__)
/* Ignore -Wvla warnings in brotli headers */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wvla"
#endif
#include <brotli/decode.h>
#if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop
#endif
#endif
#ifdef HAVE_ZSTD
#include <zstd.h>
#endif
#include "sendf.h"
#include "http.h"
#include "content_encoding.h"
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
#define CONTENT_ENCODING_DEFAULT "identity"
#ifndef CURL_DISABLE_HTTP
/* allow no more than 5 "chained" compression steps */
#define MAX_ENCODE_STACK 5
#if defined(HAVE_LIBZ) || defined(HAVE_BROTLI) || defined(HAVE_ZSTD)
#define DECOMPRESS_BUFFER_SIZE 16384 /* buffer size for decompressed data */
#endif
#ifdef HAVE_LIBZ
#if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1252)
#error "requires zlib 1.2.5.2 or newer"
#endif
typedef enum {
ZLIB_UNINIT, /* uninitialized */
ZLIB_INIT, /* initialized */
ZLIB_INFLATING, /* inflating started. */
ZLIB_EXTERNAL_TRAILER, /* reading external trailer */
ZLIB_INIT_GZIP /* initialized in transparent gzip mode */
} zlibInitState;
/* Deflate and gzip writer. */
struct zlib_writer {
struct Curl_cwriter super;
zlibInitState zlib_init; /* zlib init state */
char buffer[DECOMPRESS_BUFFER_SIZE]; /* Put the decompressed data here. */
uInt trailerlen; /* Remaining trailer byte count. */
z_stream z; /* State structure for zlib. */
};
static voidpf
zalloc_cb(voidpf opaque, unsigned int items, unsigned int size)
{
(void)opaque;
/* not a typo, keep it calloc() */
return (voidpf) calloc(items, size);
}
static void
zfree_cb(voidpf opaque, voidpf ptr)
{
(void)opaque;
free(ptr);
}
static CURLcode
process_zlib_error(struct Curl_easy *data, z_stream *z)
{
if(z->msg)
failf(data, "Error while processing content unencoding: %s",
z->msg);
else
failf(data, "Error while processing content unencoding: "
"Unknown failure within decompression software.");
return CURLE_BAD_CONTENT_ENCODING;
}
static CURLcode
exit_zlib(struct Curl_easy *data,
z_stream *z, zlibInitState *zlib_init, CURLcode result)
{
if(*zlib_init != ZLIB_UNINIT) {
if(inflateEnd(z) != Z_OK && result == CURLE_OK)
result = process_zlib_error(data, z);
*zlib_init = ZLIB_UNINIT;
}
return result;
}
static CURLcode process_trailer(struct Curl_easy *data,
struct zlib_writer *zp)
{
z_stream *z = &zp->z;
CURLcode result = CURLE_OK;
uInt len = z->avail_in < zp->trailerlen ? z->avail_in : zp->trailerlen;
/* Consume expected trailer bytes. Terminate stream if exhausted.
Issue an error if unexpected bytes follow. */
zp->trailerlen -= len;
z->avail_in -= len;
z->next_in += len;
if(z->avail_in)
result = CURLE_WRITE_ERROR;
if(result || !zp->trailerlen)
result = exit_zlib(data, z, &zp->zlib_init, result);
else {
/* Only occurs for gzip with zlib < 1.2.0.4 or raw deflate. */
zp->zlib_init = ZLIB_EXTERNAL_TRAILER;
}
return result;
}
static CURLcode inflate_stream(struct Curl_easy *data,
struct Curl_cwriter *writer, int type,
zlibInitState started)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
uInt nread = z->avail_in;
z_const Bytef *orig_in = z->next_in;
bool done = FALSE;
CURLcode result = CURLE_OK; /* Curl_client_write status */
/* Check state. */
if(zp->zlib_init != ZLIB_INIT &&
zp->zlib_init != ZLIB_INFLATING &&
zp->zlib_init != ZLIB_INIT_GZIP)
return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR);
/* because the buffer size is fixed, iteratively decompress and transfer to
the client via next_write function. */
while(!done) {
int status; /* zlib status */
done = TRUE;
/* (re)set buffer for decompressed output for every iteration */
z->next_out = (Bytef *) zp->buffer;
z->avail_out = DECOMPRESS_BUFFER_SIZE;
status = inflate(z, Z_BLOCK);
/* Flush output data if some. */
if(z->avail_out != DECOMPRESS_BUFFER_SIZE) {
if(status == Z_OK || status == Z_STREAM_END) {
zp->zlib_init = started; /* Data started. */
result = Curl_cwriter_write(data, writer->next, type, zp->buffer,
DECOMPRESS_BUFFER_SIZE - z->avail_out);
if(result) {
exit_zlib(data, z, &zp->zlib_init, result);
break;
}
}
}
/* Dispatch by inflate() status. */
switch(status) {
case Z_OK:
/* Always loop: there may be unflushed latched data in zlib state. */
done = FALSE;
break;
case Z_BUF_ERROR:
/* No more data to flush: just exit loop. */
break;
case Z_STREAM_END:
result = process_trailer(data, zp);
break;
case Z_DATA_ERROR:
/* some servers seem to not generate zlib headers, so this is an attempt
to fix and continue anyway */
if(zp->zlib_init == ZLIB_INIT) {
if(inflateReset2(z, -MAX_WBITS) == Z_OK) {
z->next_in = orig_in;
z->avail_in = nread;
zp->zlib_init = ZLIB_INFLATING;
zp->trailerlen = 4; /* Tolerate up to 4 unknown trailer bytes. */
done = FALSE;
break;
}
zp->zlib_init = ZLIB_UNINIT; /* inflateEnd() already called. */
}
result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z));
break;
default:
result = exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z));
break;
}
}
/* We are about to leave this call so the `nread' data bytes will not be seen
again. If we are in a state that would wrongly allow restart in raw mode
at the next call, assume output has already started. */
if(nread && zp->zlib_init == ZLIB_INIT)
zp->zlib_init = started; /* Cannot restart anymore. */
return result;
}
/* Deflate handler. */
static CURLcode deflate_do_init(struct Curl_easy *data,
struct Curl_cwriter *writer)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
/* Initialize zlib */
z->zalloc = (alloc_func) zalloc_cb;
z->zfree = (free_func) zfree_cb;
if(inflateInit(z) != Z_OK)
return process_zlib_error(data, z);
zp->zlib_init = ZLIB_INIT;
return CURLE_OK;
}
static CURLcode deflate_do_write(struct Curl_easy *data,
struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
if(!(type & CLIENTWRITE_BODY) || !nbytes)
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
/* Set the compressed input when this function is called */
z->next_in = (z_const Bytef *)buf;
z->avail_in = (uInt)nbytes;
if(zp->zlib_init == ZLIB_EXTERNAL_TRAILER)
return process_trailer(data, zp);
/* Now uncompress the data */
return inflate_stream(data, writer, type, ZLIB_INFLATING);
}
static void deflate_do_close(struct Curl_easy *data,
struct Curl_cwriter *writer)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
exit_zlib(data, z, &zp->zlib_init, CURLE_OK);
}
static const struct Curl_cwtype deflate_encoding = {
"deflate",
NULL,
deflate_do_init,
deflate_do_write,
deflate_do_close,
sizeof(struct zlib_writer)
};
/* Gzip handler. */
static CURLcode gzip_do_init(struct Curl_easy *data,
struct Curl_cwriter *writer)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
/* Initialize zlib */
z->zalloc = (alloc_func) zalloc_cb;
z->zfree = (free_func) zfree_cb;
if(inflateInit2(z, MAX_WBITS + 32) != Z_OK)
return process_zlib_error(data, z);
zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */
return CURLE_OK;
}
static CURLcode gzip_do_write(struct Curl_easy *data,
struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
if(!(type & CLIENTWRITE_BODY) || !nbytes)
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
if(zp->zlib_init == ZLIB_INIT_GZIP) {
/* Let zlib handle the gzip decompression entirely */
z->next_in = (z_const Bytef *)buf;
z->avail_in = (uInt)nbytes;
/* Now uncompress the data */
return inflate_stream(data, writer, type, ZLIB_INIT_GZIP);
}
/* We are running with an old version: return error. */
return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR);
}
static void gzip_do_close(struct Curl_easy *data,
struct Curl_cwriter *writer)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
exit_zlib(data, z, &zp->zlib_init, CURLE_OK);
}
static const struct Curl_cwtype gzip_encoding = {
"gzip",
"x-gzip",
gzip_do_init,
gzip_do_write,
gzip_do_close,
sizeof(struct zlib_writer)
};
#endif /* HAVE_LIBZ */
#ifdef HAVE_BROTLI
/* Brotli writer. */
struct brotli_writer {
struct Curl_cwriter super;
char buffer[DECOMPRESS_BUFFER_SIZE];
BrotliDecoderState *br; /* State structure for brotli. */
};
static CURLcode brotli_map_error(BrotliDecoderErrorCode be)
{
switch(be) {
case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE:
case BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE:
case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET:
case BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME:
case BROTLI_DECODER_ERROR_FORMAT_CL_SPACE:
case BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE:
case BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT:
case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1:
case BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2:
case BROTLI_DECODER_ERROR_FORMAT_TRANSFORM:
case BROTLI_DECODER_ERROR_FORMAT_DICTIONARY:
case BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS:
case BROTLI_DECODER_ERROR_FORMAT_PADDING_1:
case BROTLI_DECODER_ERROR_FORMAT_PADDING_2:
#ifdef BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY
case BROTLI_DECODER_ERROR_COMPOUND_DICTIONARY:
#endif
#ifdef BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET
case BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET:
#endif
case BROTLI_DECODER_ERROR_INVALID_ARGUMENTS:
return CURLE_BAD_CONTENT_ENCODING;
case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES:
case BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS:
case BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP:
case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1:
case BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2:
case BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES:
return CURLE_OUT_OF_MEMORY;
default:
break;
}
return CURLE_WRITE_ERROR;
}
static CURLcode brotli_do_init(struct Curl_easy *data,
struct Curl_cwriter *writer)
{
struct brotli_writer *bp = (struct brotli_writer *) writer;
(void)data;
bp->br = BrotliDecoderCreateInstance(NULL, NULL, NULL);
return bp->br ? CURLE_OK : CURLE_OUT_OF_MEMORY;
}
static CURLcode brotli_do_write(struct Curl_easy *data,
struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes)
{
struct brotli_writer *bp = (struct brotli_writer *) writer;
const uint8_t *src = (const uint8_t *) buf;
uint8_t *dst;
size_t dstleft;
CURLcode result = CURLE_OK;
BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
if(!(type & CLIENTWRITE_BODY) || !nbytes)
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
if(!bp->br)
return CURLE_WRITE_ERROR; /* Stream already ended. */
while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) &&
result == CURLE_OK) {
dst = (uint8_t *) bp->buffer;
dstleft = DECOMPRESS_BUFFER_SIZE;
r = BrotliDecoderDecompressStream(bp->br,
&nbytes, &src, &dstleft, &dst, NULL);
result = Curl_cwriter_write(data, writer->next, type,
bp->buffer, DECOMPRESS_BUFFER_SIZE - dstleft);
if(result)
break;
switch(r) {
case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
break;
case BROTLI_DECODER_RESULT_SUCCESS:
BrotliDecoderDestroyInstance(bp->br);
bp->br = NULL;
if(nbytes)
result = CURLE_WRITE_ERROR;
break;
default:
result = brotli_map_error(BrotliDecoderGetErrorCode(bp->br));
break;
}
}
return result;
}
static void brotli_do_close(struct Curl_easy *data,
struct Curl_cwriter *writer)
{
struct brotli_writer *bp = (struct brotli_writer *) writer;
(void)data;
if(bp->br) {
BrotliDecoderDestroyInstance(bp->br);
bp->br = NULL;
}
}
static const struct Curl_cwtype brotli_encoding = {
"br",
NULL,
brotli_do_init,
brotli_do_write,
brotli_do_close,
sizeof(struct brotli_writer)
};
#endif
#ifdef HAVE_ZSTD
/* Zstd writer. */
struct zstd_writer {
struct Curl_cwriter super;
ZSTD_DStream *zds; /* State structure for zstd. */
char buffer[DECOMPRESS_BUFFER_SIZE];
};
#ifdef ZSTD_STATIC_LINKING_ONLY
static void *Curl_zstd_alloc(void *opaque, size_t size)
{
(void)opaque;
return Curl_cmalloc(size);
}
static void Curl_zstd_free(void *opaque, void *address)
{
(void)opaque;
Curl_cfree(address);
}
#endif
static CURLcode zstd_do_init(struct Curl_easy *data,
struct Curl_cwriter *writer)
{
struct zstd_writer *zp = (struct zstd_writer *) writer;
(void)data;
#ifdef ZSTD_STATIC_LINKING_ONLY
zp->zds = ZSTD_createDStream_advanced((ZSTD_customMem) {
.customAlloc = Curl_zstd_alloc,
.customFree = Curl_zstd_free,
.opaque = NULL
});
#else
zp->zds = ZSTD_createDStream();
#endif
return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY;
}
static CURLcode zstd_do_write(struct Curl_easy *data,
struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes)
{
CURLcode result = CURLE_OK;
struct zstd_writer *zp = (struct zstd_writer *) writer;
ZSTD_inBuffer in;
ZSTD_outBuffer out;
size_t errorCode;
if(!(type & CLIENTWRITE_BODY) || !nbytes)
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
in.pos = 0;
in.src = buf;
in.size = nbytes;
for(;;) {
out.pos = 0;
out.dst = zp->buffer;
out.size = DECOMPRESS_BUFFER_SIZE;
errorCode = ZSTD_decompressStream(zp->zds, &out, &in);
if(ZSTD_isError(errorCode)) {
return CURLE_BAD_CONTENT_ENCODING;
}
if(out.pos > 0) {
result = Curl_cwriter_write(data, writer->next, type,
zp->buffer, out.pos);
if(result)
break;
}
if((in.pos == nbytes) && (out.pos < out.size))
break;
}
return result;
}
static void zstd_do_close(struct Curl_easy *data,
struct Curl_cwriter *writer)
{
struct zstd_writer *zp = (struct zstd_writer *) writer;
(void)data;
if(zp->zds) {
ZSTD_freeDStream(zp->zds);
zp->zds = NULL;
}
}
static const struct Curl_cwtype zstd_encoding = {
"zstd",
NULL,
zstd_do_init,
zstd_do_write,
zstd_do_close,
sizeof(struct zstd_writer)
};
#endif
/* Identity handler. */
static const struct Curl_cwtype identity_encoding = {
"identity",
"none",
Curl_cwriter_def_init,
Curl_cwriter_def_write,
Curl_cwriter_def_close,
sizeof(struct Curl_cwriter)
};
/* supported general content decoders. */
static const struct Curl_cwtype * const general_unencoders[] = {
&identity_encoding,
#ifdef HAVE_LIBZ
&deflate_encoding,
&gzip_encoding,
#endif
#ifdef HAVE_BROTLI
&brotli_encoding,
#endif
#ifdef HAVE_ZSTD
&zstd_encoding,
#endif
NULL
};
/* supported content decoders only for transfer encodings */
static const struct Curl_cwtype * const transfer_unencoders[] = {
#ifndef CURL_DISABLE_HTTP
&Curl_httpchunk_unencoder,
#endif
NULL
};
/* Provide a list of comma-separated names of supported encodings.
*/
void Curl_all_content_encodings(char *buf, size_t blen)
{
size_t len = 0;
const struct Curl_cwtype * const *cep;
const struct Curl_cwtype *ce;
DEBUGASSERT(buf);
DEBUGASSERT(blen);
buf[0] = 0;
for(cep = general_unencoders; *cep; cep++) {
ce = *cep;
if(!curl_strequal(ce->name, CONTENT_ENCODING_DEFAULT))
len += strlen(ce->name) + 2;
}
if(!len) {
if(blen >= sizeof(CONTENT_ENCODING_DEFAULT))
strcpy(buf, CONTENT_ENCODING_DEFAULT);
}
else if(blen > len) {
char *p = buf;
for(cep = general_unencoders; *cep; cep++) {
ce = *cep;
if(!curl_strequal(ce->name, CONTENT_ENCODING_DEFAULT)) {
strcpy(p, ce->name);
p += strlen(p);
*p++ = ',';
*p++ = ' ';
}
}
p[-2] = '\0';
}
}
/* Deferred error dummy writer. */
static CURLcode error_do_init(struct Curl_easy *data,
struct Curl_cwriter *writer)
{
(void)data;
(void)writer;
return CURLE_OK;
}
static CURLcode error_do_write(struct Curl_easy *data,
struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes)
{
(void)writer;
(void)buf;
(void)nbytes;
if(!(type & CLIENTWRITE_BODY) || !nbytes)
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
else {
char all[256];
(void)Curl_all_content_encodings(all, sizeof(all));
failf(data, "Unrecognized content encoding type. "
"libcurl understands %s content encodings.", all);
}
return CURLE_BAD_CONTENT_ENCODING;
}
static void error_do_close(struct Curl_easy *data,
struct Curl_cwriter *writer)
{
(void)data;
(void)writer;
}
static const struct Curl_cwtype error_writer = {
"ce-error",
NULL,
error_do_init,
error_do_write,
error_do_close,
sizeof(struct Curl_cwriter)
};
/* Find the content encoding by name. */
static const struct Curl_cwtype *find_unencode_writer(const char *name,
size_t len,
Curl_cwriter_phase phase)
{
const struct Curl_cwtype * const *cep;
if(phase == CURL_CW_TRANSFER_DECODE) {
for(cep = transfer_unencoders; *cep; cep++) {
const struct Curl_cwtype *ce = *cep;
if((curl_strnequal(name, ce->name, len) && !ce->name[len]) ||
(ce->alias && curl_strnequal(name, ce->alias, len)
&& !ce->alias[len]))
return ce;
}
}
/* look among the general decoders */
for(cep = general_unencoders; *cep; cep++) {
const struct Curl_cwtype *ce = *cep;
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;
}
/* Setup the unencoding stack from the Content-Encoding header value.
* See RFC 7231 section 3.1.2.2. */
CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
const char *enclist, int is_transfer)
{
Curl_cwriter_phase phase = is_transfer ?
CURL_CW_TRANSFER_DECODE : CURL_CW_CONTENT_DECODE;
CURLcode result;
bool has_chunked = FALSE;
do {
const char *name;
size_t namelen;
bool is_chunked = FALSE;
/* Parse a single encoding name. */
while(ISBLANK(*enclist) || *enclist == ',')
enclist++;
name = enclist;
for(namelen = 0; *enclist && *enclist != ','; enclist++)
if(*enclist > ' ')
namelen = enclist - name + 1;
if(namelen) {
const struct Curl_cwtype *cwt;
struct Curl_cwriter *writer;
CURL_TRC_WRITE(data, "looking for %s decoder: %.*s",
is_transfer ? "transfer" : "content", (int)namelen, name);
is_chunked = (is_transfer && (namelen == 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 = curl_strnequal(name, "identity", 8);
/* not requested, ignore */
CURL_TRC_WRITE(data, "decoder not requested, ignored: %.*s",
(int)namelen, name);
if(is_transfer && !data->set.http_te_skip) {
if(has_chunked)
failf(data, "A Transfer-Encoding (%.*s) was listed after chunked",
(int)namelen, name);
else if(is_identity)
continue;
else
failf(data, "Unsolicited Transfer-Encoding (%.*s) found",
(int)namelen, name);
return CURLE_BAD_CONTENT_ENCODING;
}
return CURLE_OK;
}
if(Curl_cwriter_count(data, phase) + 1 >= MAX_ENCODE_STACK) {
failf(data, "Reject response due to more than %u content encodings",
MAX_ENCODE_STACK);
return CURLE_BAD_CONTENT_ENCODING;
}
cwt = find_unencode_writer(name, namelen, phase);
if(cwt && is_chunked && Curl_cwriter_get_by_type(data, cwt)) {
/* A 'chunked' transfer encoding has already been added.
* Ignore duplicates. See #13451.
* Also RFC 9112, ch. 6.1:
* "A sender MUST NOT apply the chunked transfer coding more than
* once to a message body."
*/
CURL_TRC_WRITE(data, "ignoring duplicate 'chunked' decoder");
return CURLE_OK;
}
if(is_transfer && !is_chunked &&
Curl_cwriter_get_by_name(data, "chunked")) {
/* RFC 9112, ch. 6.1:
* "If any transfer coding other than chunked is applied to a
* response's content, the sender MUST either apply chunked as the
* final transfer coding or terminate the message by closing the
* connection."
* "chunked" must be the last added to be the first in its phase,
* reject this.
*/
failf(data, "Reject response due to 'chunked' not being the last "
"Transfer-Encoding");
return CURLE_BAD_CONTENT_ENCODING;
}
if(!cwt)
cwt = &error_writer; /* Defer error at use. */
result = Curl_cwriter_create(&writer, data, cwt, phase);
CURL_TRC_WRITE(data, "added %s decoder %s -> %d",
is_transfer ? "transfer" : "content", cwt->name, result);
if(result)
return result;
result = Curl_cwriter_add(data, writer);
if(result) {
Curl_cwriter_free(data, writer);
return result;
}
if(is_chunked)
has_chunked = TRUE;
}
} while(*enclist);
return CURLE_OK;
}
#else
/* Stubs for builds without HTTP. */
CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
const char *enclist, int is_transfer)
{
(void)data;
(void)enclist;
(void)is_transfer;
return CURLE_NOT_BUILT_IN;
}
void Curl_all_content_encodings(char *buf, size_t blen)
{
DEBUGASSERT(buf);
DEBUGASSERT(blen);
if(blen < sizeof(CONTENT_ENCODING_DEFAULT))
buf[0] = 0;
else
strcpy(buf, CONTENT_ENCODING_DEFAULT);
}
#endif /* CURL_DISABLE_HTTP */
+34
View File
@@ -0,0 +1,34 @@
#ifndef HEADER_CURL_CONTENT_ENCODING_H
#define HEADER_CURL_CONTENT_ENCODING_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
struct Curl_cwriter;
void Curl_all_content_encodings(char *buf, size_t blen);
CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
const char *enclist, int is_transfer);
#endif /* HEADER_CURL_CONTENT_ENCODING_H */
+1692
View File
File diff suppressed because it is too large Load Diff
+142
View File
@@ -0,0 +1,142 @@
#ifndef HEADER_CURL_COOKIE_H
#define HEADER_CURL_COOKIE_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include <curl/curl.h>
#include "llist.h"
struct Cookie {
struct Curl_llist_node node; /* for the main cookie list */
struct Curl_llist_node getnode; /* for getlist */
char *name; /* <this> = value */
char *value; /* name = <this> */
char *path; /* path = <this> which is in Set-Cookie: */
char *spath; /* sanitized cookie path */
char *domain; /* domain = <this> */
curl_off_t expires; /* expires = <this> */
unsigned int creationtime; /* time when the cookie was written */
BIT(tailmatch); /* tail-match the domain name */
BIT(secure); /* the 'secure' keyword was used */
BIT(livecookie); /* updated from a server, not a stored file */
BIT(httponly); /* the httponly directive is present */
BIT(prefix_secure); /* secure prefix is set */
BIT(prefix_host); /* host prefix is set */
};
/*
* Available cookie prefixes, as defined in
* draft-ietf-httpbis-rfc6265bis-02
*/
#define COOKIE_PREFIX__SECURE (1<<0)
#define COOKIE_PREFIX__HOST (1<<1)
#define COOKIE_HASH_SIZE 63
struct CookieInfo {
/* linked lists of cookies we know of */
struct Curl_llist cookielist[COOKIE_HASH_SIZE];
curl_off_t next_expiration; /* the next time at which expiration happens */
unsigned int numcookies; /* number of cookies in the "jar" */
unsigned int lastct; /* last creation-time used in the jar */
BIT(running); /* state info, for cookie adding information */
BIT(newsession); /* new session, discard session cookies on load */
};
/* The maximum sizes we accept for cookies. RFC 6265 section 6.1 says
"general-use user agents SHOULD provide each of the following minimum
capabilities":
- At least 4096 bytes per cookie (as measured by the sum of the length of
the cookie's name, value, and attributes).
In the 6265bis draft document section 5.4 it is phrased even stronger: "If
the sum of the lengths of the name string and the value string is more than
4096 octets, abort these steps and ignore the set-cookie-string entirely."
*/
/** Limits for INCOMING cookies **/
/* The longest we allow a line to be when reading a cookie from an HTTP header
or from a cookie jar */
#define MAX_COOKIE_LINE 5000
/* Maximum length of an incoming cookie name or content we deal with. Longer
cookies are ignored. */
#define MAX_NAME 4096
/* Maximum number of Set-Cookie: lines accepted in a single response. If more
such header lines are received, they are ignored. This value must be less
than 256 since an unsigned char is used to count. */
#define MAX_SET_COOKIE_AMOUNT 50
/** Limits for OUTGOING cookies **/
/* Maximum size for an outgoing cookie line libcurl will use in an http
request. This is the default maximum length used in some versions of Apache
httpd. */
#define MAX_COOKIE_HEADER_LEN 8190
/* Maximum number of cookies libcurl will send in a single request, even if
there might be more cookies that match. One reason to cap the number is to
keep the maximum HTTP request within the maximum allowed size. */
#define MAX_COOKIE_SEND_AMOUNT 150
struct Curl_easy;
struct connectdata;
/*
* Add a cookie to the internal list of cookies. The domain and path arguments
* are only used if the header boolean is TRUE.
*/
bool Curl_secure_context(struct connectdata *conn, const char *host);
struct Cookie *Curl_cookie_add(struct Curl_easy *data,
struct CookieInfo *c, bool header,
bool noexpiry, const char *lineptr,
const char *domain, const char *path,
bool secure);
int Curl_cookie_getlist(struct Curl_easy *data, struct connectdata *conn,
const char *host, struct Curl_llist *list);
void Curl_cookie_clearall(struct CookieInfo *cookies);
void Curl_cookie_clearsess(struct CookieInfo *cookies);
#if defined(CURL_DISABLE_HTTP) || defined(CURL_DISABLE_COOKIES)
#define Curl_cookie_list(x) NULL
#define Curl_cookie_loadfiles(x) Curl_nop_stmt
#define Curl_cookie_init(x,y,z,w) NULL
#define Curl_cookie_cleanup(x) Curl_nop_stmt
#define Curl_flush_cookies(x,y) Curl_nop_stmt
#else
void Curl_flush_cookies(struct Curl_easy *data, bool cleanup);
void Curl_cookie_cleanup(struct CookieInfo *c);
struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
const char *file, struct CookieInfo *inc,
bool newsession);
struct curl_slist *Curl_cookie_list(struct Curl_easy *data);
void Curl_cookie_loadfiles(struct Curl_easy *data);
#endif
#endif /* HEADER_CURL_COOKIE_H */
+588
View File
@@ -0,0 +1,588 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include <curl/curl.h>
#include "urldata.h"
#include "url.h"
#include "cfilters.h"
#include "progress.h"
#include "multiif.h"
#include "multi_ev.h"
#include "sendf.h"
#include "cshutdn.h"
#include "http_negotiate.h"
#include "http_ntlm.h"
#include "sigpipe.h"
#include "connect.h"
#include "select.h"
#include "curlx/strparse.h"
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
static void cshutdn_run_conn_handler(struct Curl_easy *data,
struct connectdata *conn)
{
if(!conn->bits.shutdown_handler) {
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
* handle, set an overall short timeout so we do not hang for the
* default 120 seconds. */
if(data->state.internal) {
data->set.timeout = DEFAULT_SHUTDOWN_TIMEOUT_MS;
(void)Curl_pgrsTime(data, TIMER_STARTOP);
}
/* This is set if protocol-specific cleanups should be made */
DEBUGF(infof(data, "connection #%" FMT_OFF_T
", shutdown protocol handler (aborted=%d)",
conn->connection_id, conn->bits.aborted));
/* There are protocol handlers that block on retrieving
* server responses here (FTP). Set a short timeout. */
conn->handler->disconnect(data, conn, conn->bits.aborted);
}
conn->bits.shutdown_handler = TRUE;
}
}
static void cshutdn_run_once(struct Curl_easy *data,
struct connectdata *conn,
bool *done)
{
CURLcode r1, r2;
bool done1, done2;
/* We expect to be attached when called */
DEBUGASSERT(data->conn == conn);
cshutdn_run_conn_handler(data, conn);
if(conn->bits.shutdown_filters) {
*done = TRUE;
return;
}
if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET))
r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1);
else {
r1 = CURLE_OK;
done1 = TRUE;
}
if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET))
r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2);
else {
r2 = CURLE_OK;
done2 = TRUE;
}
/* we are done when any failed or both report success */
*done = (r1 || r2 || (done1 && done2));
if(*done)
conn->bits.shutdown_filters = TRUE;
}
void Curl_cshutdn_run_once(struct Curl_easy *data,
struct connectdata *conn,
bool *done)
{
DEBUGASSERT(!data->conn);
Curl_attach_connection(data, conn);
cshutdn_run_once(data, conn, done);
CURL_TRC_M(data, "[SHUTDOWN] shutdown, done=%d", *done);
Curl_detach_connection(data);
}
void Curl_cshutdn_terminate(struct Curl_easy *data,
struct connectdata *conn,
bool do_shutdown)
{
struct Curl_easy *admin = data;
bool done;
/* there must be a connection to close */
DEBUGASSERT(conn);
/* it must be removed from the connection pool */
DEBUGASSERT(!conn->bits.in_cpool);
/* the transfer must be detached from the connection */
DEBUGASSERT(data && !data->conn);
/* If we can obtain an internal admin handle, use that to attach
* and terminate the connection. Some protocol will try to mess with
* `data` during shutdown and we do not want that with a `data` from
* the application. */
if(data->multi && data->multi->admin)
admin = data->multi->admin;
Curl_attach_connection(admin, conn);
cshutdn_run_conn_handler(admin, conn);
if(do_shutdown) {
/* Make a last attempt to shutdown handlers and filters, if
* not done so already. */
cshutdn_run_once(admin, conn, &done);
}
CURL_TRC_M(admin, "[SHUTDOWN] %sclosing connection #%" FMT_OFF_T,
conn->bits.shutdown_filters ? "" : "force ",
conn->connection_id);
Curl_conn_close(admin, SECONDARYSOCKET);
Curl_conn_close(admin, FIRSTSOCKET);
Curl_detach_connection(admin);
if(data->multi)
Curl_multi_ev_conn_done(data->multi, data, conn);
Curl_conn_free(admin, conn);
if(data->multi) {
CURL_TRC_M(data, "[SHUTDOWN] trigger multi connchanged");
Curl_multi_connchanged(data->multi);
}
}
static bool cshutdn_destroy_oldest(struct cshutdn *cshutdn,
struct Curl_easy *data,
const char *destination)
{
struct Curl_llist_node *e;
struct connectdata *conn;
e = Curl_llist_head(&cshutdn->list);
while(e) {
conn = Curl_node_elem(e);
if(!destination || !strcmp(destination, conn->destination))
break;
e = Curl_node_next(e);
}
if(e) {
SIGPIPE_VARIABLE(pipe_st);
conn = Curl_node_elem(e);
Curl_node_remove(e);
sigpipe_init(&pipe_st);
sigpipe_apply(data, &pipe_st);
Curl_cshutdn_terminate(data, conn, FALSE);
sigpipe_restore(&pipe_st);
return TRUE;
}
return FALSE;
}
bool Curl_cshutdn_close_oldest(struct Curl_easy *data,
const char *destination)
{
if(data && data->multi) {
struct cshutdn *csd = &data->multi->cshutdn;
return cshutdn_destroy_oldest(csd, data, destination);
}
return FALSE;
}
#define NUM_POLLS_ON_STACK 10
static CURLcode cshutdn_wait(struct cshutdn *cshutdn,
struct Curl_easy *data,
int timeout_ms)
{
struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
struct curl_pollfds cpfds;
CURLcode result;
Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
result = Curl_cshutdn_add_pollfds(cshutdn, data, &cpfds);
if(result)
goto out;
Curl_poll(cpfds.pfds, cpfds.n, CURLMIN(timeout_ms, 1000));
out:
Curl_pollfds_cleanup(&cpfds);
return result;
}
static void cshutdn_perform(struct cshutdn *cshutdn,
struct Curl_easy *data)
{
struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list);
struct Curl_llist_node *enext;
struct connectdata *conn;
struct curltime *nowp = NULL;
struct curltime now;
timediff_t next_expire_ms = 0, ms;
bool done;
if(!e)
return;
CURL_TRC_M(data, "[SHUTDOWN] perform on %zu connections",
Curl_llist_count(&cshutdn->list));
while(e) {
enext = Curl_node_next(e);
conn = Curl_node_elem(e);
Curl_cshutdn_run_once(data, conn, &done);
if(done) {
Curl_node_remove(e);
Curl_cshutdn_terminate(data, conn, FALSE);
}
else {
/* idata has one timer list, but maybe more than one connection.
* Set EXPIRE_SHUTDOWN to the smallest time left for all. */
if(!nowp) {
now = curlx_now();
nowp = &now;
}
ms = Curl_conn_shutdown_timeleft(conn, nowp);
if(ms && ms < next_expire_ms)
next_expire_ms = ms;
}
e = enext;
}
if(next_expire_ms)
Curl_expire_ex(data, nowp, next_expire_ms, EXPIRE_SHUTDOWN);
}
static void cshutdn_terminate_all(struct cshutdn *cshutdn,
struct Curl_easy *data,
int timeout_ms)
{
struct curltime started = curlx_now();
struct Curl_llist_node *e;
SIGPIPE_VARIABLE(pipe_st);
DEBUGASSERT(cshutdn);
DEBUGASSERT(data);
CURL_TRC_M(data, "[SHUTDOWN] shutdown all");
sigpipe_init(&pipe_st);
sigpipe_apply(data, &pipe_st);
while(Curl_llist_head(&cshutdn->list)) {
timediff_t timespent;
int remain_ms;
cshutdn_perform(cshutdn, data);
if(!Curl_llist_head(&cshutdn->list)) {
CURL_TRC_M(data, "[SHUTDOWN] shutdown finished cleanly");
break;
}
/* wait for activity, timeout or "nothing" */
timespent = curlx_timediff(curlx_now(), started);
if(timespent >= (timediff_t)timeout_ms) {
CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, %s",
(timeout_ms > 0) ? "timeout" : "best effort done");
break;
}
remain_ms = timeout_ms - (int)timespent;
if(cshutdn_wait(cshutdn, data, remain_ms)) {
CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, aborted");
break;
}
}
/* Terminate any remaining. */
e = Curl_llist_head(&cshutdn->list);
while(e) {
struct connectdata *conn = Curl_node_elem(e);
Curl_node_remove(e);
Curl_cshutdn_terminate(data, conn, FALSE);
e = Curl_llist_head(&cshutdn->list);
}
DEBUGASSERT(!Curl_llist_count(&cshutdn->list));
sigpipe_restore(&pipe_st);
}
int Curl_cshutdn_init(struct cshutdn *cshutdn,
struct Curl_multi *multi)
{
DEBUGASSERT(multi);
cshutdn->multi = multi;
Curl_llist_init(&cshutdn->list, NULL);
cshutdn->initialised = TRUE;
return 0; /* good */
}
void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
struct Curl_easy *data)
{
if(cshutdn->initialised && data) {
int timeout_ms = 0;
/* Just for testing, run graceful shutdown */
#ifdef DEBUGBUILD
{
const char *p = getenv("CURL_GRACEFUL_SHUTDOWN");
if(p) {
curl_off_t l;
if(!curlx_str_number(&p, &l, INT_MAX))
timeout_ms = (int)l;
}
}
#endif
CURL_TRC_M(data, "[SHUTDOWN] destroy, %zu connections, timeout=%dms",
Curl_llist_count(&cshutdn->list), timeout_ms);
cshutdn_terminate_all(cshutdn, data, timeout_ms);
}
cshutdn->multi = NULL;
}
size_t Curl_cshutdn_count(struct Curl_easy *data)
{
if(data && data->multi) {
struct cshutdn *csd = &data->multi->cshutdn;
return Curl_llist_count(&csd->list);
}
return 0;
}
size_t Curl_cshutdn_dest_count(struct Curl_easy *data,
const char *destination)
{
if(data && data->multi) {
struct cshutdn *csd = &data->multi->cshutdn;
size_t n = 0;
struct Curl_llist_node *e = Curl_llist_head(&csd->list);
while(e) {
struct connectdata *conn = Curl_node_elem(e);
if(!strcmp(destination, conn->destination))
++n;
e = Curl_node_next(e);
}
return n;
}
return 0;
}
static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn,
struct Curl_easy *data,
struct connectdata *conn)
{
CURLMcode mresult;
DEBUGASSERT(cshutdn);
DEBUGASSERT(cshutdn->multi->socket_cb);
Curl_attach_connection(data, conn);
mresult = Curl_multi_ev_assess_conn(cshutdn->multi, data, conn);
Curl_detach_connection(data);
return mresult;
}
void Curl_cshutdn_add(struct cshutdn *cshutdn,
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) ?
(size_t)cshutdn->multi->max_total_connections : 0;
/* Add the connection to our shutdown list for non-blocking shutdown
* during multi processing. */
if(max_total > 0 && (max_total <=
(conns_in_pool + Curl_llist_count(&cshutdn->list)))) {
CURL_TRC_M(data, "[SHUTDOWN] discarding oldest shutdown connection "
"due to connection limit of %zu", max_total);
cshutdn_destroy_oldest(cshutdn, data, NULL);
}
if(cshutdn->multi->socket_cb) {
if(cshutdn_update_ev(cshutdn, data, conn)) {
CURL_TRC_M(data, "[SHUTDOWN] update events failed, discarding #%"
FMT_OFF_T, conn->connection_id);
Curl_cshutdn_terminate(data, conn, FALSE);
return;
}
}
Curl_llist_append(&cshutdn->list, conn, &conn->cshutdn_node);
CURL_TRC_M(data, "[SHUTDOWN] added #%" FMT_OFF_T
" to shutdowns, now %zu conns in shutdown",
conn->connection_id, Curl_llist_count(&cshutdn->list));
}
static void cshutdn_multi_socket(struct cshutdn *cshutdn,
struct Curl_easy *data,
curl_socket_t s)
{
struct Curl_llist_node *e;
struct connectdata *conn;
bool done;
DEBUGASSERT(cshutdn->multi->socket_cb);
e = Curl_llist_head(&cshutdn->list);
while(e) {
conn = Curl_node_elem(e);
if(s == conn->sock[FIRSTSOCKET] || s == conn->sock[SECONDARYSOCKET]) {
Curl_cshutdn_run_once(data, conn, &done);
if(done || cshutdn_update_ev(cshutdn, data, conn)) {
Curl_node_remove(e);
Curl_cshutdn_terminate(data, conn, FALSE);
}
break;
}
e = Curl_node_next(e);
}
}
void Curl_cshutdn_perform(struct cshutdn *cshutdn,
struct Curl_easy *data,
curl_socket_t s)
{
if((s == CURL_SOCKET_TIMEOUT) || (!cshutdn->multi->socket_cb))
cshutdn_perform(cshutdn, data);
else
cshutdn_multi_socket(cshutdn, data, s);
}
/* return fd_set info about the shutdown connections */
void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
struct Curl_easy *data,
fd_set *read_fd_set, fd_set *write_fd_set,
int *maxfd)
{
if(Curl_llist_head(&cshutdn->list)) {
struct Curl_llist_node *e;
struct easy_pollset ps;
Curl_pollset_init(&ps);
for(e = Curl_llist_head(&cshutdn->list); e;
e = Curl_node_next(e)) {
unsigned int i;
struct connectdata *conn = Curl_node_elem(e);
CURLcode result;
Curl_pollset_reset(&ps);
Curl_attach_connection(data, conn);
result = Curl_conn_adjust_pollset(data, conn, &ps);
Curl_detach_connection(data);
if(result)
continue;
for(i = 0; i < ps.n; i++) {
#ifdef __DJGPP__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warith-conversion"
#endif
if(ps.actions[i] & CURL_POLL_IN)
FD_SET(ps.sockets[i], read_fd_set);
if(ps.actions[i] & CURL_POLL_OUT)
FD_SET(ps.sockets[i], write_fd_set);
#ifdef __DJGPP__
#pragma GCC diagnostic pop
#endif
if((ps.actions[i] & (CURL_POLL_OUT | CURL_POLL_IN)) &&
((int)ps.sockets[i] > *maxfd))
*maxfd = (int)ps.sockets[i];
}
}
Curl_pollset_cleanup(&ps);
}
}
/* return information about the shutdown connections */
unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
struct Curl_easy *data,
struct Curl_waitfds *cwfds)
{
unsigned int need = 0;
if(Curl_llist_head(&cshutdn->list)) {
struct Curl_llist_node *e;
struct easy_pollset ps;
struct connectdata *conn;
CURLcode result;
Curl_pollset_init(&ps);
for(e = Curl_llist_head(&cshutdn->list); e;
e = Curl_node_next(e)) {
conn = Curl_node_elem(e);
Curl_pollset_reset(&ps);
Curl_attach_connection(data, conn);
result = Curl_conn_adjust_pollset(data, conn, &ps);
Curl_detach_connection(data);
if(!result)
need += Curl_waitfds_add_ps(cwfds, &ps);
}
Curl_pollset_cleanup(&ps);
}
return need;
}
CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn,
struct Curl_easy *data,
struct curl_pollfds *cpfds)
{
CURLcode result = CURLE_OK;
if(Curl_llist_head(&cshutdn->list)) {
struct Curl_llist_node *e;
struct easy_pollset ps;
struct connectdata *conn;
Curl_pollset_init(&ps);
for(e = Curl_llist_head(&cshutdn->list); e;
e = Curl_node_next(e)) {
conn = Curl_node_elem(e);
Curl_pollset_reset(&ps);
Curl_attach_connection(data, conn);
result = Curl_conn_adjust_pollset(data, conn, &ps);
Curl_detach_connection(data);
if(!result)
result = Curl_pollfds_add_ps(cpfds, &ps);
if(result) {
Curl_pollset_cleanup(&ps);
Curl_pollfds_cleanup(cpfds);
goto out;
}
}
Curl_pollset_cleanup(&ps);
}
out:
return result;
}
+110
View File
@@ -0,0 +1,110 @@
#ifndef HEADER_CURL_CSHUTDN_H
#define HEADER_CURL_CSHUTDN_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
*
* 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/curl.h>
#include "curlx/timeval.h"
struct connectdata;
struct Curl_easy;
struct curl_pollfds;
struct Curl_waitfds;
struct Curl_multi;
struct Curl_share;
/* Run the shutdown of the connection once.
* Will shortly attach/detach `data` to `conn` while doing so.
* `done` will be set TRUE if any error was encountered or if
* the connection was shut down completely. */
void Curl_cshutdn_run_once(struct Curl_easy *data,
struct connectdata *conn,
bool *done);
/* Terminates the connection, e.g. closes and destroys it.
* If `run_shutdown` is TRUE, the shutdown will be run once before
* terminating it.
* Takes ownership of `conn`. */
void Curl_cshutdn_terminate(struct Curl_easy *data,
struct connectdata *conn,
bool run_shutdown);
/* A `cshutdown` is always owned by a multi handle to maintain
* the connections to be shut down. It registers timers and
* sockets to monitor via the multi handle. */
struct cshutdn {
struct Curl_llist list; /* connections being shut down */
struct Curl_multi *multi; /* the multi owning this */
BIT(initialised);
};
/* Init as part of the given multi handle. */
int Curl_cshutdn_init(struct cshutdn *cshutdn,
struct Curl_multi *multi);
/* Terminate all remaining connections and free resources. */
void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
struct Curl_easy *data);
/* Number of connections being shut down. */
size_t Curl_cshutdn_count(struct Curl_easy *data);
/* Number of connections to the destination being shut down. */
size_t Curl_cshutdn_dest_count(struct Curl_easy *data,
const char *destination);
/* Close the oldest connection in shutdown to destination or,
* when destination is NULL for any destination.
* Return TRUE if a connection has been closed. */
bool Curl_cshutdn_close_oldest(struct Curl_easy *data,
const char *destination);
/* Add a connection to have it shut down. Will terminate the oldest
* connection when total connection limit of multi is being reached. */
void Curl_cshutdn_add(struct cshutdn *cshutdn,
struct connectdata *conn,
size_t conns_in_pool);
/* Add sockets and POLLIN/OUT flags for connections being shut down. */
CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn,
struct Curl_easy *data,
struct curl_pollfds *cpfds);
unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
struct Curl_easy *data,
struct Curl_waitfds *cwfds);
void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
struct Curl_easy *data,
fd_set *read_fd_set, fd_set *write_fd_set,
int *maxfd);
/* Run shut down connections using socket. If socket is CURL_SOCKET_TIMEOUT,
* run maintenance on all connections. */
void Curl_cshutdn_perform(struct cshutdn *cshutdn,
struct Curl_easy *data,
curl_socket_t s);
#endif /* HEADER_CURL_CSHUTDN_H */
+590
View File
@@ -0,0 +1,590 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include <curl/curl.h>
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_IN6_H
# include <netinet/in6.h>
#endif
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_SYS_UN_H
# include <sys/un.h>
#endif
#ifdef __VMS
# include <in.h>
# include <inet.h>
#endif
#include <stddef.h>
#include "curl_addrinfo.h"
#include "fake_addrinfo.h"
#include "curlx/inet_pton.h"
#include "curlx/warnless.h"
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
/*
* Curl_freeaddrinfo()
*
* This is used to free a linked list of Curl_addrinfo structs along
* with all its associated allocated storage. This function should be
* called once for each successful call to Curl_getaddrinfo_ex() or to
* any function call which actually allocates a Curl_addrinfo struct.
*/
#if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
/* workaround icc 9.1 optimizer issue */
# define vqualifier volatile
#else
# define vqualifier
#endif
void
Curl_freeaddrinfo(struct Curl_addrinfo *cahead)
{
struct Curl_addrinfo *vqualifier canext;
struct Curl_addrinfo *ca;
for(ca = cahead; ca; ca = canext) {
canext = ca->ai_next;
free(ca);
}
}
#ifdef HAVE_GETADDRINFO
/*
* Curl_getaddrinfo_ex()
*
* This is a wrapper function around system's getaddrinfo(), with
* the only difference that instead of returning a linked list of
* addrinfo structs this one returns a linked list of Curl_addrinfo
* ones. The memory allocated by this function *MUST* be free'd with
* Curl_freeaddrinfo(). For each successful call to this function
* there must be an associated call later to Curl_freeaddrinfo().
*
* There should be no single call to system's getaddrinfo() in the
* whole library, any such call should be 'routed' through this one.
*/
int
Curl_getaddrinfo_ex(const char *nodename,
const char *servname,
const struct addrinfo *hints,
struct Curl_addrinfo **result)
{
const struct addrinfo *ai;
struct addrinfo *aihead;
struct Curl_addrinfo *cafirst = NULL;
struct Curl_addrinfo *calast = NULL;
struct Curl_addrinfo *ca;
size_t ss_size;
int error;
*result = NULL; /* assume failure */
error = CURL_GETADDRINFO(nodename, servname, hints, &aihead);
if(error)
return error;
/* traverse the addrinfo list */
for(ai = aihead; ai != NULL; ai = ai->ai_next) {
size_t namelen = ai->ai_canonname ? strlen(ai->ai_canonname) + 1 : 0;
/* ignore elements with unsupported address family, */
/* settle family-specific sockaddr structure size. */
if(ai->ai_family == AF_INET)
ss_size = sizeof(struct sockaddr_in);
#ifdef USE_IPV6
else if(ai->ai_family == AF_INET6)
ss_size = sizeof(struct sockaddr_in6);
#endif
else
continue;
/* ignore elements without required address info */
if(!ai->ai_addr || !(ai->ai_addrlen > 0))
continue;
/* ignore elements with bogus address size */
if((size_t)ai->ai_addrlen < ss_size)
continue;
ca = malloc(sizeof(struct Curl_addrinfo) + ss_size + namelen);
if(!ca) {
error = EAI_MEMORY;
break;
}
/* copy each structure member individually, member ordering, */
/* size, or padding might be different for each platform. */
ca->ai_flags = ai->ai_flags;
ca->ai_family = ai->ai_family;
ca->ai_socktype = ai->ai_socktype;
ca->ai_protocol = ai->ai_protocol;
ca->ai_addrlen = (curl_socklen_t)ss_size;
ca->ai_addr = NULL;
ca->ai_canonname = NULL;
ca->ai_next = NULL;
ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
memcpy(ca->ai_addr, ai->ai_addr, ss_size);
if(namelen) {
ca->ai_canonname = (void *)((char *)ca->ai_addr + ss_size);
memcpy(ca->ai_canonname, ai->ai_canonname, namelen);
}
/* if the return list is empty, this becomes the first element */
if(!cafirst)
cafirst = ca;
/* add this element last in the return list */
if(calast)
calast->ai_next = ca;
calast = ca;
}
/* destroy the addrinfo list */
if(aihead)
CURL_FREEADDRINFO(aihead);
/* if we failed, also destroy the Curl_addrinfo list */
if(error) {
Curl_freeaddrinfo(cafirst);
cafirst = NULL;
}
else if(!cafirst) {
#ifdef EAI_NONAME
/* rfc3493 conformant */
error = EAI_NONAME;
#else
/* rfc3493 obsoleted */
error = EAI_NODATA;
#endif
#ifdef USE_WINSOCK
SET_SOCKERRNO(error);
#endif
}
*result = cafirst;
/* This is not a CURLcode */
return error;
}
#endif /* HAVE_GETADDRINFO */
/*
* Curl_he2ai()
*
* This function returns a pointer to the first element of a newly allocated
* Curl_addrinfo struct linked list filled with the data of a given hostent.
* Curl_addrinfo is meant to work like the addrinfo struct does for an IPv6
* stack, but usable also for IPv4, all hosts and environments.
*
* The memory allocated by this function *MUST* be free'd later on calling
* Curl_freeaddrinfo(). For each successful call to this function there
* must be an associated call later to Curl_freeaddrinfo().
*
* Curl_addrinfo defined in "lib/curl_addrinfo.h"
*
* struct Curl_addrinfo {
* int ai_flags;
* int ai_family;
* int ai_socktype;
* int ai_protocol;
* curl_socklen_t ai_addrlen; * Follow rfc3493 struct addrinfo *
* char *ai_canonname;
* struct sockaddr *ai_addr;
* struct Curl_addrinfo *ai_next;
* };
*
* hostent defined in <netdb.h>
*
* struct hostent {
* char *h_name;
* char **h_aliases;
* int h_addrtype;
* int h_length;
* char **h_addr_list;
* };
*
* for backward compatibility:
*
* #define h_addr h_addr_list[0]
*/
#if !(defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE))
struct Curl_addrinfo *
Curl_he2ai(const struct hostent *he, int port)
{
struct Curl_addrinfo *ai;
struct Curl_addrinfo *prevai = NULL;
struct Curl_addrinfo *firstai = NULL;
struct sockaddr_in *addr;
#ifdef USE_IPV6
struct sockaddr_in6 *addr6;
#endif
CURLcode result = CURLE_OK;
int i;
char *curr;
if(!he)
/* no input == no output! */
return NULL;
DEBUGASSERT((he->h_name != NULL) && (he->h_addr_list != NULL));
for(i = 0; (curr = he->h_addr_list[i]) != NULL; i++) {
size_t ss_size;
size_t namelen = strlen(he->h_name) + 1; /* include null-terminator */
#ifdef USE_IPV6
if(he->h_addrtype == AF_INET6)
ss_size = sizeof(struct sockaddr_in6);
else
#endif
ss_size = sizeof(struct sockaddr_in);
/* allocate memory to hold the struct, the address and the name */
ai = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + namelen);
if(!ai) {
result = CURLE_OUT_OF_MEMORY;
break;
}
/* put the address after the struct */
ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
/* then put the name after the address */
ai->ai_canonname = (char *)ai->ai_addr + ss_size;
memcpy(ai->ai_canonname, he->h_name, namelen);
if(!firstai)
/* store the pointer we want to return from this function */
firstai = ai;
if(prevai)
/* make the previous entry point to this */
prevai->ai_next = ai;
ai->ai_family = he->h_addrtype;
/* we return all names as STREAM, so when using this address for TFTP
the type must be ignored and conn->socktype be used instead! */
ai->ai_socktype = SOCK_STREAM;
ai->ai_addrlen = (curl_socklen_t)ss_size;
/* leave the rest of the struct filled with zero */
switch(ai->ai_family) {
case AF_INET:
addr = (void *)ai->ai_addr; /* storage area for this info */
memcpy(&addr->sin_addr, curr, sizeof(struct in_addr));
addr->sin_family = (CURL_SA_FAMILY_T)(he->h_addrtype);
addr->sin_port = htons((unsigned short)port);
break;
#ifdef USE_IPV6
case AF_INET6:
addr6 = (void *)ai->ai_addr; /* storage area for this info */
memcpy(&addr6->sin6_addr, curr, sizeof(struct in6_addr));
addr6->sin6_family = (CURL_SA_FAMILY_T)(he->h_addrtype);
addr6->sin6_port = htons((unsigned short)port);
break;
#endif
}
prevai = ai;
}
if(result) {
Curl_freeaddrinfo(firstai);
firstai = NULL;
}
return firstai;
}
#endif
/*
* Curl_ip2addr()
*
* This function takes an Internet address, in binary form, as input parameter
* along with its address family and the string version of the address, and it
* returns a Curl_addrinfo chain filled in correctly with information for the
* given address/host
*/
struct Curl_addrinfo *
Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port)
{
struct Curl_addrinfo *ai;
size_t addrsize;
size_t namelen;
struct sockaddr_in *addr;
#ifdef USE_IPV6
struct sockaddr_in6 *addr6;
#endif
DEBUGASSERT(inaddr && hostname);
namelen = strlen(hostname) + 1;
if(af == AF_INET)
addrsize = sizeof(struct sockaddr_in);
#ifdef USE_IPV6
else if(af == AF_INET6)
addrsize = sizeof(struct sockaddr_in6);
#endif
else
return NULL;
/* allocate memory to hold the struct, the address and the name */
ai = calloc(1, sizeof(struct Curl_addrinfo) + addrsize + namelen);
if(!ai)
return NULL;
/* put the address after the struct */
ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
/* then put the name after the address */
ai->ai_canonname = (char *)ai->ai_addr + addrsize;
memcpy(ai->ai_canonname, hostname, namelen);
ai->ai_family = af;
ai->ai_socktype = SOCK_STREAM;
ai->ai_addrlen = (curl_socklen_t)addrsize;
/* leave the rest of the struct filled with zero */
switch(af) {
case AF_INET:
addr = (void *)ai->ai_addr; /* storage area for this info */
memcpy(&addr->sin_addr, inaddr, sizeof(struct in_addr));
addr->sin_family = (CURL_SA_FAMILY_T)af;
addr->sin_port = htons((unsigned short)port);
break;
#ifdef USE_IPV6
case AF_INET6:
addr6 = (void *)ai->ai_addr; /* storage area for this info */
memcpy(&addr6->sin6_addr, inaddr, sizeof(struct in6_addr));
addr6->sin6_family = (CURL_SA_FAMILY_T)af;
addr6->sin6_port = htons((unsigned short)port);
break;
#endif
}
return ai;
}
/*
* Given an IPv4 or IPv6 dotted string address, this converts it to a proper
* allocated Curl_addrinfo struct and returns it.
*/
struct Curl_addrinfo *Curl_str2addr(char *address, int port)
{
struct in_addr in;
if(curlx_inet_pton(AF_INET, address, &in) > 0)
/* This is a dotted IP address 123.123.123.123-style */
return Curl_ip2addr(AF_INET, &in, address, port);
#ifdef USE_IPV6
{
struct in6_addr in6;
if(curlx_inet_pton(AF_INET6, address, &in6) > 0)
/* This is a dotted IPv6 address ::1-style */
return Curl_ip2addr(AF_INET6, &in6, address, port);
}
#endif
return NULL; /* bad input format */
}
#ifdef USE_UNIX_SOCKETS
/**
* Given a path to a Unix domain socket, return a newly allocated Curl_addrinfo
* struct initialized with this path.
* Set '*longpath' to TRUE if the error is a too long path.
*/
struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath,
bool abstract)
{
struct Curl_addrinfo *ai;
struct sockaddr_un *sa_un;
size_t path_len;
*longpath = FALSE;
ai = calloc(1, sizeof(struct Curl_addrinfo) + sizeof(struct sockaddr_un));
if(!ai)
return NULL;
ai->ai_addr = (void *)((char *)ai + sizeof(struct Curl_addrinfo));
sa_un = (void *) ai->ai_addr;
sa_un->sun_family = AF_UNIX;
/* sun_path must be able to store the null-terminated path */
path_len = strlen(path) + 1;
if(path_len > sizeof(sa_un->sun_path)) {
free(ai);
*longpath = TRUE;
return NULL;
}
ai->ai_family = AF_UNIX;
ai->ai_socktype = SOCK_STREAM; /* assume reliable transport for HTTP */
ai->ai_addrlen = (curl_socklen_t)
((offsetof(struct sockaddr_un, sun_path) + path_len) & 0x7FFFFFFF);
/* Abstract Unix domain socket have NULL prefix instead of suffix */
if(abstract)
memcpy(sa_un->sun_path + 1, path, path_len - 1);
else
memcpy(sa_un->sun_path, path, path_len); /* copy NUL byte */
return ai;
}
#endif
#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) && \
defined(HAVE_FREEADDRINFO)
/*
* curl_dbg_freeaddrinfo()
*
* This is strictly for memory tracing and are using the same style as the
* family otherwise present in memdebug.c. I put these ones here since they
* require a bunch of structs I did not want to include in memdebug.c
*/
void
curl_dbg_freeaddrinfo(struct addrinfo *freethis,
int line, const char *source)
{
curl_dbg_log("ADDR %s:%d freeaddrinfo(%p)\n",
source, line, (void *)freethis);
#ifdef USE_LWIPSOCK
lwip_freeaddrinfo(freethis);
#elif defined(USE_FAKE_GETADDRINFO)
{
const char *env = getenv("CURL_DNS_SERVER");
if(env)
r_freeaddrinfo(freethis);
else
/* !checksrc! disable BANNEDFUNC 1 */
freeaddrinfo(freethis);
}
#else
/* !checksrc! disable BANNEDFUNC 1 */
freeaddrinfo(freethis);
#endif
}
#endif /* CURLDEBUG && HAVE_FREEADDRINFO */
#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO)
/*
* curl_dbg_getaddrinfo()
*
* This is strictly for memory tracing and are using the same style as the
* family otherwise present in memdebug.c. I put these ones here since they
* require a bunch of structs I did not want to include in memdebug.c
*/
int
curl_dbg_getaddrinfo(const char *hostname,
const char *service,
const struct addrinfo *hints,
struct addrinfo **result,
int line, const char *source)
{
#ifdef USE_LWIPSOCK
int res = lwip_getaddrinfo(hostname, service, hints, result);
#elif defined(USE_FAKE_GETADDRINFO)
int res;
const char *env = getenv("CURL_DNS_SERVER");
if(env)
res = r_getaddrinfo(hostname, service, hints, result);
else
/* !checksrc! disable BANNEDFUNC 1 */
res = getaddrinfo(hostname, service, hints, result);
#else
/* !checksrc! disable BANNEDFUNC 1 */
int res = getaddrinfo(hostname, service, hints, result);
#endif
if(res == 0)
/* success */
curl_dbg_log("ADDR %s:%d getaddrinfo() = %p\n",
source, line, (void *)*result);
else
curl_dbg_log("ADDR %s:%d getaddrinfo() failed\n",
source, line);
return res;
}
#endif /* CURLDEBUG && HAVE_GETADDRINFO */
#if defined(HAVE_GETADDRINFO) && defined(USE_RESOLVE_ON_IPS)
/*
* Work-arounds the sin6_port is always zero bug on iOS 9.3.2 and macOS
* 10.11.5.
*/
void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port)
{
struct Curl_addrinfo *ca;
struct sockaddr_in *addr;
#ifdef USE_IPV6
struct sockaddr_in6 *addr6;
#endif
for(ca = addrinfo; ca != NULL; ca = ca->ai_next) {
switch(ca->ai_family) {
case AF_INET:
addr = (void *)ca->ai_addr; /* storage area for this info */
addr->sin_port = htons((unsigned short)port);
break;
#ifdef USE_IPV6
case AF_INET6:
addr6 = (void *)ca->ai_addr; /* storage area for this info */
addr6->sin6_port = htons((unsigned short)port);
break;
#endif
}
}
}
#endif
+110
View File
@@ -0,0 +1,110 @@
#ifndef HEADER_CURL_ADDRINFO_H
#define HEADER_CURL_ADDRINFO_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_NETDB_H
# include <netdb.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef __VMS
# include <in.h>
# include <inet.h>
# include <stdlib.h>
#endif
/*
* Curl_addrinfo is our internal struct definition that we use to allow
* consistent internal handling of this data. We use this even when the system
* provides an addrinfo structure definition. We use this for all sorts of
* IPv4 and IPV6 builds.
*/
struct Curl_addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
curl_socklen_t ai_addrlen; /* Follow rfc3493 struct addrinfo */
char *ai_canonname;
struct sockaddr *ai_addr;
struct Curl_addrinfo *ai_next;
};
void
Curl_freeaddrinfo(struct Curl_addrinfo *cahead);
#ifdef HAVE_GETADDRINFO
int
Curl_getaddrinfo_ex(const char *nodename,
const char *servname,
const struct addrinfo *hints,
struct Curl_addrinfo **result);
#endif
#if !(defined(HAVE_GETADDRINFO) && defined(HAVE_GETADDRINFO_THREADSAFE))
struct Curl_addrinfo *
Curl_he2ai(const struct hostent *he, int port);
#endif
struct Curl_addrinfo *
Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port);
struct Curl_addrinfo *Curl_str2addr(char *dotted, int port);
#ifdef USE_UNIX_SOCKETS
struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath,
bool abstract);
#endif
#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) && \
defined(HAVE_FREEADDRINFO)
void
curl_dbg_freeaddrinfo(struct addrinfo *freethis, int line, const char *source);
#endif
#if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO)
int
curl_dbg_getaddrinfo(const char *hostname, const char *service,
const struct addrinfo *hints, struct addrinfo **result,
int line, const char *source);
#endif
#ifdef HAVE_GETADDRINFO
#ifdef USE_RESOLVE_ON_IPS
void Curl_addrinfo_set_port(struct Curl_addrinfo *addrinfo, int port);
#else
#define Curl_addrinfo_set_port(x,y)
#endif
#endif
#endif /* HEADER_CURL_ADDRINFO_H */
+813
View File
@@ -0,0 +1,813 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
/* Location of default ca bundle */
#cmakedefine CURL_CA_BUNDLE "${CURL_CA_BUNDLE}"
/* define "1" to use built-in ca store of TLS backend */
#cmakedefine CURL_CA_FALLBACK 1
/* Location of default ca path */
#cmakedefine CURL_CA_PATH "${CURL_CA_PATH}"
/* Default SSL backend */
#cmakedefine CURL_DEFAULT_SSL_BACKEND "${CURL_DEFAULT_SSL_BACKEND}"
/* disables alt-svc */
#cmakedefine CURL_DISABLE_ALTSVC 1
/* disables cookies support */
#cmakedefine CURL_DISABLE_COOKIES 1
/* disables Basic authentication */
#cmakedefine CURL_DISABLE_BASIC_AUTH 1
/* disables Bearer authentication */
#cmakedefine CURL_DISABLE_BEARER_AUTH 1
/* disables Digest authentication */
#cmakedefine CURL_DISABLE_DIGEST_AUTH 1
/* disables Kerberos authentication */
#cmakedefine CURL_DISABLE_KERBEROS_AUTH 1
/* disables negotiate authentication */
#cmakedefine CURL_DISABLE_NEGOTIATE_AUTH 1
/* disables aws-sigv4 */
#cmakedefine CURL_DISABLE_AWS 1
/* disables DICT */
#cmakedefine CURL_DISABLE_DICT 1
/* disables DNS-over-HTTPS */
#cmakedefine CURL_DISABLE_DOH 1
/* disables FILE */
#cmakedefine CURL_DISABLE_FILE 1
/* disables form api */
#cmakedefine CURL_DISABLE_FORM_API 1
/* disables FTP */
#cmakedefine CURL_DISABLE_FTP 1
/* disables curl_easy_options API for existing options to curl_easy_setopt */
#cmakedefine CURL_DISABLE_GETOPTIONS 1
/* disables GOPHER */
#cmakedefine CURL_DISABLE_GOPHER 1
/* disables headers-api support */
#cmakedefine CURL_DISABLE_HEADERS_API 1
/* disables HSTS support */
#cmakedefine CURL_DISABLE_HSTS 1
/* disables HTTP */
#cmakedefine CURL_DISABLE_HTTP 1
/* disabled all HTTP authentication methods */
#cmakedefine CURL_DISABLE_HTTP_AUTH 1
/* disables IMAP */
#cmakedefine CURL_DISABLE_IMAP 1
/* disables LDAP */
#cmakedefine CURL_DISABLE_LDAP 1
/* disables LDAPS */
#cmakedefine CURL_DISABLE_LDAPS 1
/* disables --libcurl option from the curl tool */
#cmakedefine CURL_DISABLE_LIBCURL_OPTION 1
/* disables MIME support */
#cmakedefine CURL_DISABLE_MIME 1
/* disables local binding support */
#cmakedefine CURL_DISABLE_BINDLOCAL 1
/* disables MQTT */
#cmakedefine CURL_DISABLE_MQTT 1
/* disables netrc parser */
#cmakedefine CURL_DISABLE_NETRC 1
/* disables NTLM support */
#cmakedefine CURL_DISABLE_NTLM 1
/* disables date parsing */
#cmakedefine CURL_DISABLE_PARSEDATE 1
/* disables POP3 */
#cmakedefine CURL_DISABLE_POP3 1
/* disables built-in progress meter */
#cmakedefine CURL_DISABLE_PROGRESS_METER 1
/* disables proxies */
#cmakedefine CURL_DISABLE_PROXY 1
/* disables IPFS from the curl tool */
#cmakedefine CURL_DISABLE_IPFS 1
/* disables RTSP */
#cmakedefine CURL_DISABLE_RTSP 1
/* disables SHA-512/256 hash algorithm */
#cmakedefine CURL_DISABLE_SHA512_256 1
/* disabled shuffle DNS feature */
#cmakedefine CURL_DISABLE_SHUFFLE_DNS 1
/* disables SMB */
#cmakedefine CURL_DISABLE_SMB 1
/* disables SMTP */
#cmakedefine CURL_DISABLE_SMTP 1
/* disabled WebSocket */
#cmakedefine CURL_DISABLE_WEBSOCKETS 1
/* disables use of socketpair for curl_multi_poll */
#cmakedefine CURL_DISABLE_SOCKETPAIR 1
/* disables TELNET */
#cmakedefine CURL_DISABLE_TELNET 1
/* disables TFTP */
#cmakedefine CURL_DISABLE_TFTP 1
/* disables verbose strings */
#cmakedefine CURL_DISABLE_VERBOSE_STRINGS 1
/* disables unsafe CA bundle search on Windows from the curl tool */
#cmakedefine CURL_DISABLE_CA_SEARCH 1
/* safe CA bundle search (within the curl tool directory) on Windows */
#cmakedefine CURL_CA_SEARCH_SAFE 1
/* to make a symbol visible */
#cmakedefine CURL_EXTERN_SYMBOL ${CURL_EXTERN_SYMBOL}
/* Ensure using CURL_EXTERN_SYMBOL is possible */
#ifndef CURL_EXTERN_SYMBOL
#define CURL_EXTERN_SYMBOL
#endif
/* Allow SMB to work on Windows */
#cmakedefine USE_WIN32_CRYPTO 1
/* Use Windows LDAP implementation */
#cmakedefine USE_WIN32_LDAP 1
/* Define if you want to enable IPv6 support */
#cmakedefine USE_IPV6 1
/* Define to 1 if you have the alarm function. */
#cmakedefine HAVE_ALARM 1
/* Define to 1 if you have the arc4random function. */
#cmakedefine HAVE_ARC4RANDOM 1
/* Define to 1 if you have the <arpa/inet.h> header file. */
#cmakedefine HAVE_ARPA_INET_H 1
/* Define to 1 if you have _Atomic support. */
#cmakedefine HAVE_ATOMIC 1
/* Define to 1 if you have the `accept4' function. */
#cmakedefine HAVE_ACCEPT4 1
/* Define to 1 if you have the `fnmatch' function. */
#cmakedefine HAVE_FNMATCH 1
/* Define to 1 if you have the `basename' function. */
#cmakedefine HAVE_BASENAME 1
/* Define to 1 if bool is an available type. */
#cmakedefine HAVE_BOOL_T 1
/* Define to 1 if you have the __builtin_available function. */
#cmakedefine HAVE_BUILTIN_AVAILABLE 1
/* Define to 1 if you have the clock_gettime function and monotonic timer. */
#cmakedefine HAVE_CLOCK_GETTIME_MONOTONIC 1
/* Define to 1 if you have the clock_gettime function and raw monotonic timer.
*/
#cmakedefine HAVE_CLOCK_GETTIME_MONOTONIC_RAW 1
/* Define to 1 if you have the `closesocket' function. */
#cmakedefine HAVE_CLOSESOCKET 1
/* Define to 1 if you have the `CloseSocket' function. */
#cmakedefine HAVE_CLOSESOCKET_CAMEL 1
/* Define to 1 if you have the <dirent.h> header file. */
#cmakedefine HAVE_DIRENT_H 1
/* Define to 1 if you have the `opendir' function. */
#cmakedefine HAVE_OPENDIR 1
/* Define to 1 if you have the fcntl function. */
#cmakedefine HAVE_FCNTL 1
/* Define to 1 if you have the <fcntl.h> header file. */
#cmakedefine HAVE_FCNTL_H 1
/* Define to 1 if you have a working fcntl O_NONBLOCK function. */
#cmakedefine HAVE_FCNTL_O_NONBLOCK 1
/* Define to 1 if you have the freeaddrinfo function. */
#cmakedefine HAVE_FREEADDRINFO 1
/* Define to 1 if you have the fseeko function. */
#cmakedefine HAVE_FSEEKO 1
/* Define to 1 if you have the fseeko declaration. */
#cmakedefine HAVE_DECL_FSEEKO 1
/* Define to 1 if you have the ftruncate function. */
#cmakedefine HAVE_FTRUNCATE 1
/* Define to 1 if you have a working getaddrinfo function. */
#cmakedefine HAVE_GETADDRINFO 1
/* Define to 1 if the getaddrinfo function is threadsafe. */
#cmakedefine HAVE_GETADDRINFO_THREADSAFE 1
/* Define to 1 if you have the `geteuid' function. */
#cmakedefine HAVE_GETEUID 1
/* Define to 1 if you have the `getppid' function. */
#cmakedefine HAVE_GETPPID 1
/* Define to 1 if you have the gethostbyname_r function. */
#cmakedefine HAVE_GETHOSTBYNAME_R 1
/* gethostbyname_r() takes 3 args */
#cmakedefine HAVE_GETHOSTBYNAME_R_3 1
/* gethostbyname_r() takes 5 args */
#cmakedefine HAVE_GETHOSTBYNAME_R_5 1
/* gethostbyname_r() takes 6 args */
#cmakedefine HAVE_GETHOSTBYNAME_R_6 1
/* Define to 1 if you have the gethostname function. */
#cmakedefine HAVE_GETHOSTNAME 1
/* Define to 1 if you have a working getifaddrs function. */
#cmakedefine HAVE_GETIFADDRS 1
/* Define to 1 if you have the `getpass_r' function. */
#cmakedefine HAVE_GETPASS_R 1
/* Define to 1 if you have the `getpeername' function. */
#cmakedefine HAVE_GETPEERNAME 1
/* Define to 1 if you have the `getsockname' function. */
#cmakedefine HAVE_GETSOCKNAME 1
/* Define to 1 if you have the `if_nametoindex' function. */
#cmakedefine HAVE_IF_NAMETOINDEX 1
/* Define to 1 if you have the `getpwuid' function. */
#cmakedefine HAVE_GETPWUID 1
/* Define to 1 if you have the `getpwuid_r' function. */
#cmakedefine HAVE_GETPWUID_R 1
/* Define to 1 if you have the `getrlimit' function. */
#cmakedefine HAVE_GETRLIMIT 1
/* Define to 1 if you have the `gettimeofday' function. */
#cmakedefine HAVE_GETTIMEOFDAY 1
/* Define to 1 if you have a working glibc-style strerror_r function. */
#cmakedefine HAVE_GLIBC_STRERROR_R 1
/* Define to 1 if you have a working gmtime_r function. */
#cmakedefine HAVE_GMTIME_R 1
/* if you have the gssapi libraries */
#cmakedefine HAVE_GSSAPI 1
/* if you have the GNU gssapi libraries */
#cmakedefine HAVE_GSSGNU 1
/* MIT Kerberos version */
#cmakedefine CURL_KRB5_VERSION ${CURL_KRB5_VERSION}
/* Define to 1 if you have the <ifaddrs.h> header file. */
#cmakedefine HAVE_IFADDRS_H 1
/* Define to 1 if you have an IPv6 capable working inet_ntop function. */
#cmakedefine HAVE_INET_NTOP 1
/* Define to 1 if you have an IPv6 capable working inet_pton function. */
#cmakedefine HAVE_INET_PTON 1
/* Define to 1 if symbol `sa_family_t' exists */
#cmakedefine HAVE_SA_FAMILY_T 1
/* Define to 1 if you have the ioctlsocket function. */
#cmakedefine HAVE_IOCTLSOCKET 1
/* Define to 1 if you have the IoctlSocket camel case function. */
#cmakedefine HAVE_IOCTLSOCKET_CAMEL 1
/* Define to 1 if you have a working IoctlSocket camel case FIONBIO function.
*/
#cmakedefine HAVE_IOCTLSOCKET_CAMEL_FIONBIO 1
/* Define to 1 if you have a working ioctlsocket FIONBIO function. */
#cmakedefine HAVE_IOCTLSOCKET_FIONBIO 1
/* Define to 1 if you have a working ioctl FIONBIO function. */
#cmakedefine HAVE_IOCTL_FIONBIO 1
/* Define to 1 if you have a working ioctl SIOCGIFADDR function. */
#cmakedefine HAVE_IOCTL_SIOCGIFADDR 1
/* Define to 1 if you have the <io.h> header file. */
#cmakedefine HAVE_IO_H 1
/* Define to 1 if you have the lber.h header file. */
#cmakedefine HAVE_LBER_H 1
/* Use LDAPS implementation */
#cmakedefine HAVE_LDAP_SSL 1
/* Define to 1 if you have the ldap_ssl.h header file. */
#cmakedefine HAVE_LDAP_SSL_H 1
/* Define to 1 if you have the `ldap_url_parse' function. */
#cmakedefine HAVE_LDAP_URL_PARSE 1
/* Define to 1 if you have the <libgen.h> header file. */
#cmakedefine HAVE_LIBGEN_H 1
/* Define to 1 if you have the `idn2' library (-lidn2). */
#cmakedefine HAVE_LIBIDN2 1
/* Define to 1 if you have the idn2.h header file. */
#cmakedefine HAVE_IDN2_H 1
/* if zlib is available */
#cmakedefine HAVE_LIBZ 1
/* if brotli is available */
#cmakedefine HAVE_BROTLI 1
/* if zstd is available */
#cmakedefine HAVE_ZSTD 1
/* Define to 1 if you have the <locale.h> header file. */
#cmakedefine HAVE_LOCALE_H 1
/* Define to 1 if the compiler supports the 'long long' data type. */
#cmakedefine HAVE_LONGLONG 1
/* Define to 1 if you have the 'suseconds_t' data type. */
#cmakedefine HAVE_SUSECONDS_T 1
/* Define to 1 if you have the MSG_NOSIGNAL flag. */
#cmakedefine HAVE_MSG_NOSIGNAL 1
/* Define to 1 if you have the <netdb.h> header file. */
#cmakedefine HAVE_NETDB_H 1
/* Define to 1 if you have the <netinet/in.h> header file. */
#cmakedefine HAVE_NETINET_IN_H 1
/* Define to 1 if you have the <netinet/in6.h> header file. */
#cmakedefine HAVE_NETINET_IN6_H 1
/* Define to 1 if you have the <netinet/tcp.h> header file. */
#cmakedefine HAVE_NETINET_TCP_H 1
/* Define to 1 if you have the <netinet/udp.h> header file. */
#cmakedefine HAVE_NETINET_UDP_H 1
/* Define to 1 if you have the <linux/tcp.h> header file. */
#cmakedefine HAVE_LINUX_TCP_H 1
/* Define to 1 if you have the <net/if.h> header file. */
#cmakedefine HAVE_NET_IF_H 1
/* Define to 1 if you have the `pipe' function. */
#cmakedefine HAVE_PIPE 1
/* Define to 1 if you have the `pipe2' function. */
#cmakedefine HAVE_PIPE2 1
/* Define to 1 if you have the `eventfd' function. */
#cmakedefine HAVE_EVENTFD 1
/* If you have poll */
#cmakedefine HAVE_POLL 1
/* If you have realpath */
#cmakedefine HAVE_REALPATH 1
/* Define to 1 if you have the <poll.h> header file. */
#cmakedefine HAVE_POLL_H 1
/* Define to 1 if you have a working POSIX-style strerror_r function. */
#cmakedefine HAVE_POSIX_STRERROR_R 1
/* Define to 1 if you have the <pthread.h> header file */
#cmakedefine HAVE_PTHREAD_H 1
/* Define to 1 if you have the <pwd.h> header file. */
#cmakedefine HAVE_PWD_H 1
/* Define to 1 if OpenSSL has the `SSL_set0_wbio` function. */
#cmakedefine HAVE_SSL_SET0_WBIO 1
/* Define to 1 if you have the recv function. */
#cmakedefine HAVE_RECV 1
/* Define to 1 if you have the select function. */
#cmakedefine HAVE_SELECT 1
/* Define to 1 if you have the sched_yield function. */
#cmakedefine HAVE_SCHED_YIELD 1
/* Define to 1 if you have the send function. */
#cmakedefine HAVE_SEND 1
/* Define to 1 if you have the sendmsg function. */
#cmakedefine HAVE_SENDMSG 1
/* Define to 1 if you have the sendmmsg function. */
#cmakedefine HAVE_SENDMMSG 1
/* Define to 1 if you have the <stdint.h> header file. */
#cmakedefine HAVE_STDINT_H 1
/* Define to 1 if you have the 'fsetxattr' function. */
#cmakedefine HAVE_FSETXATTR 1
/* fsetxattr() takes 5 args */
#cmakedefine HAVE_FSETXATTR_5 1
/* fsetxattr() takes 6 args */
#cmakedefine HAVE_FSETXATTR_6 1
/* Define to 1 if you have the `setlocale' function. */
#cmakedefine HAVE_SETLOCALE 1
/* Define to 1 if you have the `setmode' function. */
#cmakedefine HAVE_SETMODE 1
/* Define to 1 if you have the `_setmode' function. */
#cmakedefine HAVE__SETMODE 1
/* Define to 1 if you have the `setrlimit' function. */
#cmakedefine HAVE_SETRLIMIT 1
/* Define to 1 if you have a working setsockopt SO_NONBLOCK function. */
#cmakedefine HAVE_SETSOCKOPT_SO_NONBLOCK 1
/* Define to 1 if you have the sigaction function. */
#cmakedefine HAVE_SIGACTION 1
/* Define to 1 if you have the siginterrupt function. */
#cmakedefine HAVE_SIGINTERRUPT 1
/* Define to 1 if you have the signal function. */
#cmakedefine HAVE_SIGNAL 1
/* Define to 1 if you have the sigsetjmp function or macro. */
#cmakedefine HAVE_SIGSETJMP 1
/* Define to 1 if you have the `snprintf' function. */
#cmakedefine HAVE_SNPRINTF 1
/* Define to 1 if struct sockaddr_in6 has the sin6_scope_id member */
#cmakedefine HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1
/* Define to 1 if you have the `socket' function. */
#cmakedefine HAVE_SOCKET 1
/* Define to 1 if you have the <proto/bsdsocket.h> header file. */
#cmakedefine HAVE_PROTO_BSDSOCKET_H 1
/* Define to 1 if you have the socketpair function. */
#cmakedefine HAVE_SOCKETPAIR 1
/* Define to 1 if you have the <stdatomic.h> header file. */
#cmakedefine HAVE_STDATOMIC_H 1
/* Define to 1 if you have the <stdbool.h> header file. */
#cmakedefine HAVE_STDBOOL_H 1
/* Define to 1 if you have the strcasecmp function. */
#cmakedefine HAVE_STRCASECMP 1
/* Define to 1 if you have the strcmpi function. */
#cmakedefine HAVE_STRCMPI 1
/* Define to 1 if you have the strdup function. */
#cmakedefine HAVE_STRDUP 1
/* Define to 1 if you have the strerror_r function. */
#cmakedefine HAVE_STRERROR_R 1
/* Define to 1 if you have the stricmp function. */
#cmakedefine HAVE_STRICMP 1
/* Define to 1 if you have the <strings.h> header file. */
#cmakedefine HAVE_STRINGS_H 1
/* Define to 1 if you have the <stropts.h> header file. */
#cmakedefine HAVE_STROPTS_H 1
/* Define to 1 if you have the memrchr function. */
#cmakedefine HAVE_MEMRCHR 1
/* if struct sockaddr_storage is defined */
#cmakedefine HAVE_STRUCT_SOCKADDR_STORAGE 1
/* Define to 1 if you have the timeval struct. */
#cmakedefine HAVE_STRUCT_TIMEVAL 1
/* Define to 1 if you have the <sys/eventfd.h> header file. */
#cmakedefine HAVE_SYS_EVENTFD_H 1
/* Define to 1 if you have the <sys/filio.h> header file. */
#cmakedefine HAVE_SYS_FILIO_H 1
/* Define to 1 if you have the <sys/ioctl.h> header file. */
#cmakedefine HAVE_SYS_IOCTL_H 1
/* Define to 1 if you have the <sys/param.h> header file. */
#cmakedefine HAVE_SYS_PARAM_H 1
/* Define to 1 if you have the <sys/poll.h> header file. */
#cmakedefine HAVE_SYS_POLL_H 1
/* Define to 1 if you have the <sys/resource.h> header file. */
#cmakedefine HAVE_SYS_RESOURCE_H 1
/* Define to 1 if you have the <sys/select.h> header file. */
#cmakedefine HAVE_SYS_SELECT_H 1
/* Define to 1 if you have the <sys/sockio.h> header file. */
#cmakedefine HAVE_SYS_SOCKIO_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#cmakedefine HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <sys/un.h> header file. */
#cmakedefine HAVE_SYS_UN_H 1
/* Define to 1 if you have the <sys/utime.h> header file. */
#cmakedefine HAVE_SYS_UTIME_H 1
/* Define to 1 if you have the <termios.h> header file. */
#cmakedefine HAVE_TERMIOS_H 1
/* Define to 1 if you have the <termio.h> header file. */
#cmakedefine HAVE_TERMIO_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#cmakedefine HAVE_UNISTD_H 1
/* Define to 1 if you have the `utime' function. */
#cmakedefine HAVE_UTIME 1
/* Define to 1 if you have the `utimes' function. */
#cmakedefine HAVE_UTIMES 1
/* Define to 1 if you have the <utime.h> header file. */
#cmakedefine HAVE_UTIME_H 1
/* Define this symbol if your OS supports changing the contents of argv */
#cmakedefine HAVE_WRITABLE_ARGV 1
/* Define this if time_t is unsigned */
#cmakedefine HAVE_TIME_T_UNSIGNED 1
/* Define to 1 if _REENTRANT preprocessor symbol must be defined. */
#cmakedefine NEED_REENTRANT 1
/* cpu-machine-OS */
#cmakedefine CURL_OS ${CURL_OS}
/*
Note: SIZEOF_* variables are fetched with CMake through check_type_size().
As per CMake documentation on CheckTypeSize, C preprocessor code is
generated by CMake into SIZEOF_*_CODE. This is what we use in the
following statements.
Reference: https://cmake.org/cmake/help/latest/module/CheckTypeSize.html
*/
/* The size of `int', as computed by sizeof. */
${SIZEOF_INT_CODE}
/* The size of `long', as computed by sizeof. */
${SIZEOF_LONG_CODE}
/* The size of `long long', as computed by sizeof. */
${SIZEOF_LONG_LONG_CODE}
/* The size of `off_t', as computed by sizeof. */
${SIZEOF_OFF_T_CODE}
/* The size of `curl_off_t', as computed by sizeof. */
${SIZEOF_CURL_OFF_T_CODE}
/* The size of `curl_socket_t', as computed by sizeof. */
${SIZEOF_CURL_SOCKET_T_CODE}
/* The size of `size_t', as computed by sizeof. */
${SIZEOF_SIZE_T_CODE}
/* The size of `time_t', as computed by sizeof. */
${SIZEOF_TIME_T_CODE}
/* Define to 1 if you have the ANSI C header files. */
#cmakedefine STDC_HEADERS 1
/* Define if you want to enable c-ares support */
#cmakedefine USE_ARES 1
/* Define if you want to enable POSIX threaded DNS lookup */
#cmakedefine USE_THREADS_POSIX 1
/* Define if you want to enable Win32 threaded DNS lookup */
#cmakedefine USE_THREADS_WIN32 1
/* if GnuTLS is enabled */
#cmakedefine USE_GNUTLS 1
/* if SSL session export support is available */
#cmakedefine USE_SSLS_EXPORT 1
/* if mbedTLS is enabled */
#cmakedefine USE_MBEDTLS 1
/* if mbedTLS <4 has the mbedtls_des_crypt_ecb function. */
#cmakedefine HAVE_MBEDTLS_DES_CRYPT_ECB 1
/* if Rustls is enabled */
#cmakedefine USE_RUSTLS 1
/* if wolfSSL is enabled */
#cmakedefine USE_WOLFSSL 1
/* if wolfSSL has the wolfSSL_get_peer_certificate function. */
#cmakedefine HAVE_WOLFSSL_GET_PEER_CERTIFICATE 1
/* if wolfSSL has the wolfSSL_UseALPN function. */
#cmakedefine HAVE_WOLFSSL_USEALPN 1
/* if wolfSSL has the wolfSSL_DES_ecb_encrypt function. */
#cmakedefine HAVE_WOLFSSL_DES_ECB_ENCRYPT 1
/* if wolfSSL has the wolfSSL_BIO_new function. */
#cmakedefine HAVE_WOLFSSL_BIO_NEW 1
/* if wolfSSL has the wolfSSL_BIO_set_shutdown function. */
#cmakedefine HAVE_WOLFSSL_BIO_SET_SHUTDOWN 1
/* if libssh is in use */
#cmakedefine USE_LIBSSH 1
/* if libssh2 is in use */
#cmakedefine USE_LIBSSH2 1
/* if libpsl is in use */
#cmakedefine USE_LIBPSL 1
/* if you want to use OpenLDAP code instead of legacy ldap implementation */
#cmakedefine USE_OPENLDAP 1
/* if OpenSSL is in use */
#cmakedefine USE_OPENSSL 1
/* if AmiSSL is in use */
#cmakedefine USE_AMISSL 1
/* if librtmp/rtmpdump is in use */
#cmakedefine USE_LIBRTMP 1
/* if GSASL is in use */
#cmakedefine USE_GSASL 1
/* if libuv is in use */
#cmakedefine USE_LIBUV 1
/* Define to 1 if you have the <uv.h> header file. */
#cmakedefine HAVE_UV_H 1
/* Define to 1 if you do not want the OpenSSL configuration to be loaded
automatically */
#cmakedefine CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG 1
/* to enable NGHTTP2 */
#cmakedefine USE_NGHTTP2 1
/* to enable NGTCP2 */
#cmakedefine USE_NGTCP2 1
/* to enable NGHTTP3 */
#cmakedefine USE_NGHTTP3 1
/* to enable quiche */
#cmakedefine USE_QUICHE 1
/* to enable openssl + nghttp3 */
#cmakedefine USE_OPENSSL_QUIC 1
/* to enable openssl + ngtcp2 + nghttp3 */
#cmakedefine OPENSSL_QUIC_API2 1
/* Define to 1 if you have the quiche_conn_set_qlog_fd function. */
#cmakedefine HAVE_QUICHE_CONN_SET_QLOG_FD 1
/* if Unix domain sockets are enabled */
#cmakedefine USE_UNIX_SOCKETS 1
/* Define to 1 if you are building a Windows target with large file support. */
#cmakedefine USE_WIN32_LARGE_FILES 1
/* to enable SSPI support */
#cmakedefine USE_WINDOWS_SSPI 1
/* to enable Windows SSL */
#cmakedefine USE_SCHANNEL 1
/* if Watt-32 is in use */
#cmakedefine USE_WATT32 1
/* enable multiple SSL backends */
#cmakedefine CURL_WITH_MULTI_SSL 1
/* Number of bits in a file offset, on hosts where this is settable. */
#cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS}
/* the signed version of size_t */
#cmakedefine ssize_t ${ssize_t}
/* Define to 1 if you have the mach_absolute_time function. */
#cmakedefine HAVE_MACH_ABSOLUTE_TIME 1
/* to enable Windows IDN */
#cmakedefine USE_WIN32_IDN 1
/* to enable Apple IDN */
#cmakedefine USE_APPLE_IDN 1
/* to enable Apple OS-native certificate verification */
#cmakedefine USE_APPLE_SECTRUST 1
/* Define to 1 if OpenSSL has the SSL_CTX_set_srp_username function. */
#cmakedefine HAVE_OPENSSL_SRP 1
/* Define to 1 if GnuTLS has the gnutls_srp_verifier function. */
#cmakedefine HAVE_GNUTLS_SRP 1
/* Define to 1 to enable TLS-SRP support. */
#cmakedefine USE_TLS_SRP 1
/* Define to 1 to query for HTTPSRR when using DoH */
#cmakedefine USE_HTTPSRR 1
/* if ECH support is available */
#cmakedefine USE_ECH 1
/* Define to 1 if you have the wolfSSL_CTX_GenerateEchConfig function. */
#cmakedefine HAVE_WOLFSSL_CTX_GENERATEECHCONFIG 1
/* Define to 1 if you have the SSL_set1_ech_config_list function. */
#cmakedefine HAVE_SSL_SET1_ECH_CONFIG_LIST 1
/* Define to 1 if OpenSSL has the DES_ecb_encrypt function. */
#cmakedefine HAVE_DES_ECB_ENCRYPT 1
+944
View File
@@ -0,0 +1,944 @@
/* lib/curl_config.h.in. Generated from configure.ac by autoheader. */
/* !checksrc! disable COPYRIGHT all */
/* Location of default ca bundle */
#undef CURL_CA_BUNDLE
/* define "1" to use OpenSSL's built-in CA store */
#undef CURL_CA_FALLBACK
/* Location of default ca path */
#undef CURL_CA_PATH
/* If safe CA bundle search is enabled */
#undef CURL_CA_SEARCH_SAFE
/* Default SSL backend */
#undef CURL_DEFAULT_SSL_BACKEND
/* disable alt-svc */
#undef CURL_DISABLE_ALTSVC
/* to disable AWS sig support */
#undef CURL_DISABLE_AWS
/* to disable basic authentication */
#undef CURL_DISABLE_BASIC_AUTH
/* to disable bearer authentication */
#undef CURL_DISABLE_BEARER_AUTH
/* disable local binding support */
#undef CURL_DISABLE_BINDLOCAL
/* If unsafe CA bundle search in PATH on Windows is disabled */
#undef CURL_DISABLE_CA_SEARCH
/* to disable cookies support */
#undef CURL_DISABLE_COOKIES
/* to disable DICT */
#undef CURL_DISABLE_DICT
/* to disable digest authentication */
#undef CURL_DISABLE_DIGEST_AUTH
/* disable DoH */
#undef CURL_DISABLE_DOH
/* to disable FILE */
#undef CURL_DISABLE_FILE
/* disable form API */
#undef CURL_DISABLE_FORM_API
/* to disable FTP */
#undef CURL_DISABLE_FTP
/* to disable curl_easy_options */
#undef CURL_DISABLE_GETOPTIONS
/* to disable Gopher */
#undef CURL_DISABLE_GOPHER
/* disable headers-api */
#undef CURL_DISABLE_HEADERS_API
/* disable alt-svc */
#undef CURL_DISABLE_HSTS
/* to disable HTTP */
#undef CURL_DISABLE_HTTP
/* disable HTTP authentication */
#undef CURL_DISABLE_HTTP_AUTH
/* to disable IMAP */
#undef CURL_DISABLE_IMAP
/* to disable IPFS */
#undef CURL_DISABLE_IPFS
/* to disable kerberos authentication */
#undef CURL_DISABLE_KERBEROS_AUTH
/* to disable LDAP */
#undef CURL_DISABLE_LDAP
/* to disable LDAPS */
#undef CURL_DISABLE_LDAPS
/* to disable --libcurl C code generation option */
#undef CURL_DISABLE_LIBCURL_OPTION
/* disable mime API */
#undef CURL_DISABLE_MIME
/* to disable MQTT */
#undef CURL_DISABLE_MQTT
/* to disable negotiate authentication */
#undef CURL_DISABLE_NEGOTIATE_AUTH
/* disable netrc parsing */
#undef CURL_DISABLE_NETRC
/* to disable NTLM support */
#undef CURL_DISABLE_NTLM
/* if the OpenSSL configuration won't be loaded automatically */
#undef CURL_DISABLE_OPENSSL_AUTO_LOAD_CONFIG
/* disable date parsing */
#undef CURL_DISABLE_PARSEDATE
/* to disable POP3 */
#undef CURL_DISABLE_POP3
/* disable progress-meter */
#undef CURL_DISABLE_PROGRESS_METER
/* to disable proxies */
#undef CURL_DISABLE_PROXY
/* to disable RTSP */
#undef CURL_DISABLE_RTSP
/* disable SHA-512/256 hash algorithm */
#undef CURL_DISABLE_SHA512_256
/* disable DNS shuffling */
#undef CURL_DISABLE_SHUFFLE_DNS
/* to disable SMB/CIFS */
#undef CURL_DISABLE_SMB
/* to disable SMTP */
#undef CURL_DISABLE_SMTP
/* to disable socketpair support */
#undef CURL_DISABLE_SOCKETPAIR
/* to disable TELNET */
#undef CURL_DISABLE_TELNET
/* to disable TFTP */
#undef CURL_DISABLE_TFTP
/* to disable verbose strings */
#undef CURL_DISABLE_VERBOSE_STRINGS
/* disable WebSockets */
#undef CURL_DISABLE_WEBSOCKETS
/* Definition to make a library symbol externally visible. */
#undef CURL_EXTERN_SYMBOL
/* MIT Kerberos version */
#undef CURL_KRB5_VERSION
/* cpu-machine-OS */
#undef CURL_OS
/* built with multiple SSL backends */
#undef CURL_WITH_MULTI_SSL
/* Define to the type of arg 2 for gethostname. */
#undef GETHOSTNAME_TYPE_ARG2
/* Define to 1 if you have the `accept4' function. */
#undef HAVE_ACCEPT4
/* Define to 1 if you have the alarm function. */
#undef HAVE_ALARM
/* Define to 1 if you have the `arc4random' function. */
#undef HAVE_ARC4RANDOM
/* Define to 1 if you have the <arpa/inet.h> header file. */
#undef HAVE_ARPA_INET_H
/* Define to 1 if you have _Atomic support. */
#undef HAVE_ATOMIC
/* Define to 1 if you have the basename function. */
#undef HAVE_BASENAME
/* Define to 1 if bool is an available type. */
#undef HAVE_BOOL_T
/* if BROTLI is in use */
#undef HAVE_BROTLI
/* Define to 1 if you have the <brotli/decode.h> header file. */
#undef HAVE_BROTLI_DECODE_H
/* Define to 1 if you have the __builtin_available function. */
#undef HAVE_BUILTIN_AVAILABLE
/* Define to 1 if you have the clock_gettime function and monotonic timer. */
#undef HAVE_CLOCK_GETTIME_MONOTONIC
/* Define to 1 if you have the clock_gettime function and raw monotonic timer.
*/
#undef HAVE_CLOCK_GETTIME_MONOTONIC_RAW
/* Define to 1 if you have the closesocket function. */
#undef HAVE_CLOSESOCKET
/* Define to 1 if you have the CloseSocket camel case function. */
#undef HAVE_CLOSESOCKET_CAMEL
/* Define to 1 if you have the fseeko declaration */
#undef HAVE_DECL_FSEEKO
/* if you have the function DES_ecb_encrypt */
#undef HAVE_DES_ECB_ENCRYPT
/* if you have <dirent.h> */
#undef HAVE_DIRENT_H
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if you have the `eventfd' function. */
#undef HAVE_EVENTFD
/* Define to 1 if you have the fcntl function. */
#undef HAVE_FCNTL
/* Define to 1 if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
/* Define to 1 if you have a working fcntl O_NONBLOCK function. */
#undef HAVE_FCNTL_O_NONBLOCK
/* Define to 1 if you have the `fnmatch' function. */
#undef HAVE_FNMATCH
/* Define to 1 if you have the freeaddrinfo function. */
#undef HAVE_FREEADDRINFO
/* Define to 1 if you have the `fseeko' function. */
#undef HAVE_FSEEKO
/* Define to 1 if you have the fsetxattr function. */
#undef HAVE_FSETXATTR
/* fsetxattr() takes 5 args */
#undef HAVE_FSETXATTR_5
/* fsetxattr() takes 6 args */
#undef HAVE_FSETXATTR_6
/* Define to 1 if you have the ftruncate function. */
#undef HAVE_FTRUNCATE
/* Define to 1 if you have a working getaddrinfo function. */
#undef HAVE_GETADDRINFO
/* Define to 1 if the getaddrinfo function is threadsafe. */
#undef HAVE_GETADDRINFO_THREADSAFE
/* Define to 1 if you have the `geteuid' function. */
#undef HAVE_GETEUID
/* Define to 1 if you have the gethostbyname_r function. */
#undef HAVE_GETHOSTBYNAME_R
/* gethostbyname_r() takes 3 args */
#undef HAVE_GETHOSTBYNAME_R_3
/* gethostbyname_r() takes 5 args */
#undef HAVE_GETHOSTBYNAME_R_5
/* gethostbyname_r() takes 6 args */
#undef HAVE_GETHOSTBYNAME_R_6
/* Define to 1 if you have the gethostname function. */
#undef HAVE_GETHOSTNAME
/* Define to 1 if you have a working getifaddrs function. */
#undef HAVE_GETIFADDRS
/* Define to 1 if you have the `getpass_r' function. */
#undef HAVE_GETPASS_R
/* Define to 1 if you have the getpeername function. */
#undef HAVE_GETPEERNAME
/* Define to 1 if you have the `getppid' function. */
#undef HAVE_GETPPID
/* Define to 1 if you have the `getpwuid' function. */
#undef HAVE_GETPWUID
/* Define to 1 if you have the `getpwuid_r' function. */
#undef HAVE_GETPWUID_R
/* Define to 1 if you have the `getrlimit' function. */
#undef HAVE_GETRLIMIT
/* Define to 1 if you have the getsockname function. */
#undef HAVE_GETSOCKNAME
/* Define to 1 if you have the `gettimeofday' function. */
#undef HAVE_GETTIMEOFDAY
/* Define to 1 if you have a working glibc-style strerror_r function. */
#undef HAVE_GLIBC_STRERROR_R
/* Define to 1 if you have a working gmtime_r function. */
#undef HAVE_GMTIME_R
/* if you have the function gnutls_srp_verifier */
#undef HAVE_GNUTLS_SRP
/* Define to 1 if you have the <gsasl.h> header file. */
#undef HAVE_GSASL_H
/* if you have GSS-API libraries */
#undef HAVE_GSSAPI
/* Define to 1 if you have the <gssapi/gssapi_generic.h> header file. */
#undef HAVE_GSSAPI_GSSAPI_GENERIC_H
/* Define to 1 if you have the <gssapi/gssapi.h> header file. */
#undef HAVE_GSSAPI_GSSAPI_H
/* Define to 1 if you have the <gssapi/gssapi_krb5.h> header file. */
#undef HAVE_GSSAPI_GSSAPI_KRB5_H
/* if you have GNU GSS */
#undef HAVE_GSSGNU
/* Define to 1 if you have the <idn2.h> header file. */
#undef HAVE_IDN2_H
/* Define to 1 if you have the <ifaddrs.h> header file. */
#undef HAVE_IFADDRS_H
/* Define to 1 if you have the `if_nametoindex' function. */
#undef HAVE_IF_NAMETOINDEX
/* Define to 1 if you have an IPv6 capable working inet_ntop function. */
#undef HAVE_INET_NTOP
/* Define to 1 if you have an IPv6 capable working inet_pton function. */
#undef HAVE_INET_PTON
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the ioctlsocket function. */
#undef HAVE_IOCTLSOCKET
/* Define to 1 if you have the IoctlSocket camel case function. */
#undef HAVE_IOCTLSOCKET_CAMEL
/* Define to 1 if you have a working IoctlSocket camel case FIONBIO function.
*/
#undef HAVE_IOCTLSOCKET_CAMEL_FIONBIO
/* Define to 1 if you have a working ioctlsocket FIONBIO function. */
#undef HAVE_IOCTLSOCKET_FIONBIO
/* Define to 1 if you have a working ioctl FIONBIO function. */
#undef HAVE_IOCTL_FIONBIO
/* Define to 1 if you have a working ioctl SIOCGIFADDR function. */
#undef HAVE_IOCTL_SIOCGIFADDR
/* Define to 1 if you have the <io.h> header file. */
#undef HAVE_IO_H
/* Define to 1 if you have the lber.h header file. */
#undef HAVE_LBER_H
/* Define to 1 if you have the ldap.h header file. */
#undef HAVE_LDAP_H
/* Define to 1 if you have the `ldap_init_fd' function. */
#undef HAVE_LDAP_INIT_FD
/* Use LDAPS implementation */
#undef HAVE_LDAP_SSL
/* Define to 1 if you have the ldap_ssl.h header file. */
#undef HAVE_LDAP_SSL_H
/* Define to 1 if you have the `ldap_url_parse' function. */
#undef HAVE_LDAP_URL_PARSE
/* Define to 1 if you have the `brotlidec' library (-lbrotlidec). */
#undef HAVE_LIBBROTLIDEC
/* Define to 1 if you have the <libgen.h> header file. */
#undef HAVE_LIBGEN_H
/* Define to 1 if you have the `idn2' library (-lidn2). */
#undef HAVE_LIBIDN2
/* Define to 1 if you have the <libpsl.h> header file. */
#undef HAVE_LIBPSL_H
/* Define to 1 if you have the <librtmp/rtmp.h> header file. */
#undef HAVE_LIBRTMP_RTMP_H
/* Define to 1 if you have the `ssh' library (-lssh). */
#undef HAVE_LIBSSH
/* Define to 1 if you have the `ssh2' library (-lssh2). */
#undef HAVE_LIBSSH2
/* Define to 1 if you have the `ssl' library (-lssl). */
#undef HAVE_LIBSSL
/* if zlib is available */
#undef HAVE_LIBZ
/* Define to 1 if you have the `zstd' library (-lzstd). */
#undef HAVE_LIBZSTD
/* Define to 1 if you have the <linux/tcp.h> header file. */
#undef HAVE_LINUX_TCP_H
/* Define to 1 if you have the <locale.h> header file. */
#undef HAVE_LOCALE_H
/* Define to 1 if the compiler supports the 'long long' data type. */
#undef HAVE_LONGLONG
/* Define to 1 if you have the `mach_absolute_time' function. */
#undef HAVE_MACH_ABSOLUTE_TIME
/* Define to 1 if you have the `mbedtls_des_crypt_ecb' function. */
#undef HAVE_MBEDTLS_DES_CRYPT_ECB
/* Define to 1 if you have the memrchr function or macro. */
#undef HAVE_MEMRCHR
/* Define to 1 if you have the MSG_NOSIGNAL flag. */
#undef HAVE_MSG_NOSIGNAL
/* Define to 1 if you have the <netdb.h> header file. */
#undef HAVE_NETDB_H
/* Define to 1 if you have the <netinet/in6.h> header file. */
#undef HAVE_NETINET_IN6_H
/* Define to 1 if you have the <netinet/in.h> header file. */
#undef HAVE_NETINET_IN_H
/* Define to 1 if you have the <netinet/tcp.h> header file. */
#undef HAVE_NETINET_TCP_H
/* Define to 1 if you have the <netinet/udp.h> header file. */
#undef HAVE_NETINET_UDP_H
/* Define to 1 if you have the <net/if.h> header file. */
#undef HAVE_NET_IF_H
/* Define to 1 if you have the <nghttp2/nghttp2.h> header file. */
#undef HAVE_NGHTTP2_NGHTTP2_H
/* Define to 1 if you have the <nghttp3/nghttp3.h> header file. */
#undef HAVE_NGHTTP3_NGHTTP3_H
/* Define to 1 if you have the <ngtcp2/ngtcp2_crypto.h> header file. */
#undef HAVE_NGTCP2_NGTCP2_CRYPTO_H
/* Define to 1 if you have the <ngtcp2/ngtcp2.h> header file. */
#undef HAVE_NGTCP2_NGTCP2_H
/* if you have opendir */
#undef HAVE_OPENDIR
/* Define to 1 if you have the <openssl/crypto.h> header file. */
#undef HAVE_OPENSSL_CRYPTO_H
/* Define to 1 if you have the <openssl/err.h> header file. */
#undef HAVE_OPENSSL_ERR_H
/* Define to 1 if you have the <openssl/pem.h> header file. */
#undef HAVE_OPENSSL_PEM_H
/* Define to 1 if you have the <openssl/rsa.h> header file. */
#undef HAVE_OPENSSL_RSA_H
/* if you have the functions SSL_CTX_set_srp_username and
SSL_CTX_set_srp_password */
#undef HAVE_OPENSSL_SRP
/* Define to 1 if you have the <openssl/ssl.h> header file. */
#undef HAVE_OPENSSL_SSL_H
/* Define to 1 if you have the <openssl/x509.h> header file. */
#undef HAVE_OPENSSL_X509_H
/* Define to 1 if you have the `pipe' function. */
#undef HAVE_PIPE
/* Define to 1 if you have the `pipe2' function. */
#undef HAVE_PIPE2
/* Define to 1 if you have the `poll' function. */
#undef HAVE_POLL
/* Define to 1 if you have the <poll.h> header file. */
#undef HAVE_POLL_H
/* Define to 1 if you have a working POSIX-style strerror_r function. */
#undef HAVE_POSIX_STRERROR_R
/* Define to 1 if you have the <proto/bsdsocket.h> header file. */
#undef HAVE_PROTO_BSDSOCKET_H
/* if you have <pthread.h> */
#undef HAVE_PTHREAD_H
/* Define to 1 if you have the <pwd.h> header file. */
#undef HAVE_PWD_H
/* Define to 1 if you have the `quiche_conn_set_qlog_fd' function. */
#undef HAVE_QUICHE_CONN_SET_QLOG_FD
/* Define to 1 if you have the <quiche.h> header file. */
#undef HAVE_QUICHE_H
/* Define to 1 if you have the `realpath' function. */
#undef HAVE_REALPATH
/* Define to 1 if you have the recv function. */
#undef HAVE_RECV
/* Define to 1 if symbol `sa_family_t' exists */
#undef HAVE_SA_FAMILY_T
/* Define to 1 if you have the `sched_yield' function. */
#undef HAVE_SCHED_YIELD
/* Define to 1 if you have the select function. */
#undef HAVE_SELECT
/* Define to 1 if you have the send function. */
#undef HAVE_SEND
/* Define to 1 if you have the `sendmmsg' function. */
#undef HAVE_SENDMMSG
/* Define to 1 if you have the `sendmsg' function. */
#undef HAVE_SENDMSG
/* Define to 1 if you have the `setlocale' function. */
#undef HAVE_SETLOCALE
/* Define to 1 if you have the `setmode' function. */
#undef HAVE_SETMODE
/* Define to 1 if you have the `setrlimit' function. */
#undef HAVE_SETRLIMIT
/* Define to 1 if you have the sigaction function. */
#undef HAVE_SIGACTION
/* Define to 1 if you have the siginterrupt function. */
#undef HAVE_SIGINTERRUPT
/* Define to 1 if you have the signal function. */
#undef HAVE_SIGNAL
/* Define to 1 if you have the sigsetjmp function or macro. */
#undef HAVE_SIGSETJMP
/* Define to 1 if you have the `snprintf' function. */
#undef HAVE_SNPRINTF
/* Define to 1 if struct sockaddr_in6 has the sin6_scope_id member */
#undef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
/* Define to 1 if you have the socket function. */
#undef HAVE_SOCKET
/* Define to 1 if you have the socketpair function. */
#undef HAVE_SOCKETPAIR
/* Define to 1 if you have the `SSL_set0_wbio' function. */
#undef HAVE_SSL_SET0_WBIO
/* Define to 1 if you have the `SSL_set1_ech_config_list' function. */
#undef HAVE_SSL_SET1_ECH_CONFIG_LIST
/* Define to 1 if you have the `SSL_set_quic_tls_cbs' function. */
#undef HAVE_SSL_SET_QUIC_TLS_CBS
/* Define to 1 if you have the `SSL_set_quic_use_legacy_codepoint' function.
*/
#undef HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT
/* Define to 1 if you have the <stdatomic.h> header file. */
#undef HAVE_STDATOMIC_H
/* Define to 1 if you have the <stdbool.h> header file. */
#undef HAVE_STDBOOL_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdio.h> header file. */
#undef HAVE_STDIO_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the strcasecmp function. */
#undef HAVE_STRCASECMP
/* Define to 1 if you have the strcmpi function. */
#undef HAVE_STRCMPI
/* Define to 1 if you have the strdup function. */
#undef HAVE_STRDUP
/* Define to 1 if you have the strerror_r function. */
#undef HAVE_STRERROR_R
/* Define to 1 if you have the stricmp function. */
#undef HAVE_STRICMP
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if you have the <stropts.h> header file. */
#undef HAVE_STROPTS_H
/* if struct sockaddr_storage is defined */
#undef HAVE_STRUCT_SOCKADDR_STORAGE
/* Define to 1 if you have the timeval struct. */
#undef HAVE_STRUCT_TIMEVAL
/* Define to 1 if suseconds_t is an available type. */
#undef HAVE_SUSECONDS_T
/* Define to 1 if you have the <sys/eventfd.h> header file. */
#undef HAVE_SYS_EVENTFD_H
/* Define to 1 if you have the <sys/filio.h> header file. */
#undef HAVE_SYS_FILIO_H
/* Define to 1 if you have the <sys/ioctl.h> header file. */
#undef HAVE_SYS_IOCTL_H
/* Define to 1 if you have the <sys/param.h> header file. */
#undef HAVE_SYS_PARAM_H
/* Define to 1 if you have the <sys/poll.h> header file. */
#undef HAVE_SYS_POLL_H
/* Define to 1 if you have the <sys/resource.h> header file. */
#undef HAVE_SYS_RESOURCE_H
/* Define to 1 if you have the <sys/select.h> header file. */
#undef HAVE_SYS_SELECT_H
/* Define to 1 if you have the <sys/sockio.h> header file. */
#undef HAVE_SYS_SOCKIO_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <sys/un.h> header file. */
#undef HAVE_SYS_UN_H
/* Define to 1 if you have the <sys/utime.h> header file. */
#undef HAVE_SYS_UTIME_H
/* Define to 1 if you have the <sys/xattr.h> header file. */
#undef HAVE_SYS_XATTR_H
/* Define to 1 if you have the <termios.h> header file. */
#undef HAVE_TERMIOS_H
/* Define to 1 if you have the <termio.h> header file. */
#undef HAVE_TERMIO_H
/* Define this if time_t is unsigned */
#undef HAVE_TIME_T_UNSIGNED
/* Define to 1 if you have the <unicode/uidna.h> header file. */
#undef HAVE_UNICODE_UIDNA_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Define to 1 if you have the `utime' function. */
#undef HAVE_UTIME
/* Define to 1 if you have the `utimes' function. */
#undef HAVE_UTIMES
/* Define to 1 if you have the <utime.h> header file. */
#undef HAVE_UTIME_H
/* Define to 1 if you have the <uv.h> header file. */
#undef HAVE_UV_H
/* Define to 1 if you have the `wolfSSL_BIO_new' function. */
#undef HAVE_WOLFSSL_BIO_NEW
/* Define to 1 if you have the `wolfSSL_BIO_set_shutdown' function. */
#undef HAVE_WOLFSSL_BIO_SET_SHUTDOWN
/* Define to 1 if you have the `wolfSSL_CTX_GenerateEchConfig' function. */
#undef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
/* Define to 1 if you have the `wolfSSL_DES_ecb_encrypt' function. */
#undef HAVE_WOLFSSL_DES_ECB_ENCRYPT
/* Define to 1 if you have the `wolfSSL_get_peer_certificate' function. */
#undef HAVE_WOLFSSL_GET_PEER_CERTIFICATE
/* Define to 1 if you have the `wolfSSL_set_quic_use_legacy_codepoint'
function. */
#undef HAVE_WOLFSSL_SET_QUIC_USE_LEGACY_CODEPOINT
/* Define to 1 if you have the `wolfSSL_UseALPN' function. */
#undef HAVE_WOLFSSL_USEALPN
/* Define this symbol if your OS supports changing the contents of argv */
#undef HAVE_WRITABLE_ARGV
/* if libzstd is in use */
#undef HAVE_ZSTD
/* Define to 1 if you have the <zstd.h> header file. */
#undef HAVE_ZSTD_H
/* Define to 1 if you have the `_setmode' function. */
#undef HAVE__SETMODE
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#undef LT_OBJDIR
/* Define to 1 if you need the lber.h header file even with ldap.h */
#undef NEED_LBER_H
/* Define to 1 if _REENTRANT preprocessor symbol must be defined. */
#undef NEED_REENTRANT
/* Define to 1 if _THREAD_SAFE preprocessor symbol must be defined. */
#undef NEED_THREAD_SAFE
/* openssl with new QUIC API */
#undef OPENSSL_QUIC_API2
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* Size of curl_off_t in number of bytes */
#undef SIZEOF_CURL_OFF_T
/* Size of curl_socket_t in number of bytes */
#undef SIZEOF_CURL_SOCKET_T
/* Size of int in number of bytes */
#undef SIZEOF_INT
/* Size of long in number of bytes */
#undef SIZEOF_LONG
/* Size of long long in number of bytes */
#undef SIZEOF_LONG_LONG
/* Size of off_t in number of bytes */
#undef SIZEOF_OFF_T
/* Size of size_t in number of bytes */
#undef SIZEOF_SIZE_T
/* Size of time_t in number of bytes */
#undef SIZEOF_TIME_T
/* Define to 1 if all of the C90 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
#undef STDC_HEADERS
/* if AmiSSL is in use */
#undef USE_AMISSL
/* if AppleIDN */
#undef USE_APPLE_IDN
/* enable Apple OS certificate validation */
#undef USE_APPLE_SECTRUST
/* Define to enable c-ares support */
#undef USE_ARES
/* if ECH support is available */
#undef USE_ECH
/* if GnuTLS is enabled */
#undef USE_GNUTLS
/* GSASL support enabled */
#undef USE_GSASL
/* enable HTTPS RR support */
#undef USE_HTTPSRR
/* Define if you want to enable IPv6 support */
#undef USE_IPV6
/* if libpsl is in use */
#undef USE_LIBPSL
/* if librtmp is in use */
#undef USE_LIBRTMP
/* if libssh is in use */
#undef USE_LIBSSH
/* if libssh2 is in use */
#undef USE_LIBSSH2
/* if libuv is in use */
#undef USE_LIBUV
/* if mbedTLS is enabled */
#undef USE_MBEDTLS
/* if nghttp2 is in use */
#undef USE_NGHTTP2
/* if nghttp3 is in use */
#undef USE_NGHTTP3
/* if ngtcp2 is in use */
#undef USE_NGTCP2
/* Use OpenLDAP-specific code */
#undef USE_OPENLDAP
/* if OpenSSL is in use */
#undef USE_OPENSSL
/* if openssl QUIC is in use */
#undef USE_OPENSSL_QUIC
/* if quiche is in use */
#undef USE_QUICHE
/* if Rustls is enabled */
#undef USE_RUSTLS
/* to enable Windows native SSL/TLS support */
#undef USE_SCHANNEL
/* if SSL session export support is available */
#undef USE_SSLS_EXPORT
/* if you want POSIX threaded DNS lookup */
#undef USE_THREADS_POSIX
/* if you want Win32 threaded DNS lookup */
#undef USE_THREADS_WIN32
/* Use TLS-SRP authentication */
#undef USE_TLS_SRP
/* Use Unix domain sockets */
#undef USE_UNIX_SOCKETS
/* if Watt-32 is in use */
#undef USE_WATT32
/* Define to 1 if you are building a Windows target with crypto API support.
*/
#undef USE_WIN32_CRYPTO
/* Define to 1 if you have the `normaliz' (WinIDN) library (-lnormaliz). */
#undef USE_WIN32_IDN
/* Define to 1 if you are building a Windows target with large file support.
*/
#undef USE_WIN32_LARGE_FILES
/* Use Windows LDAP implementation */
#undef USE_WIN32_LDAP
/* to enable SSPI support */
#undef USE_WINDOWS_SSPI
/* if wolfSSL is enabled */
#undef USE_WOLFSSL
/* Version number of package */
#undef VERSION
/* Define to 1 if OS is AIX. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
/* Define for large files, on AIX-style hosts. */
#undef _LARGE_FILES
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
/* Define to `unsigned int' if <sys/types.h> does not define. */
#undef size_t
/* the signed version of size_t */
#undef ssize_t
+52
View File
@@ -0,0 +1,52 @@
#ifndef HEADER_CURL_CTYPE_H
#define HEADER_CURL_CTYPE_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#define ISLOWHEXALHA(x) (((x) >= 'a') && ((x) <= 'f'))
#define ISUPHEXALHA(x) (((x) >= 'A') && ((x) <= 'F'))
#define ISLOWCNTRL(x) ((unsigned char)(x) <= 0x1f)
#define IS7F(x) ((x) == 0x7f)
#define ISLOWPRINT(x) (((x) >= 9) && ((x) <= 0x0d))
#define ISPRINT(x) (ISLOWPRINT(x) || (((x) >= ' ') && ((x) <= 0x7e)))
#define ISGRAPH(x) (ISLOWPRINT(x) || (((x) > ' ') && ((x) <= 0x7e)))
#define ISCNTRL(x) (ISLOWCNTRL(x) || IS7F(x))
#define ISALPHA(x) (ISLOWER(x) || ISUPPER(x))
#define ISXDIGIT(x) (ISDIGIT(x) || ISLOWHEXALHA(x) || ISUPHEXALHA(x))
#define ISODIGIT(x) (((x) >= '0') && ((x) <= '7'))
#define ISALNUM(x) (ISDIGIT(x) || ISLOWER(x) || ISUPPER(x))
#define ISUPPER(x) (((x) >= 'A') && ((x) <= 'Z'))
#define ISLOWER(x) (((x) >= 'a') && ((x) <= 'z'))
#define ISDIGIT(x) (((x) >= '0') && ((x) <= '9'))
#define ISBLANK(x) (((x) == ' ') || ((x) == '\t'))
#define ISSPACE(x) (ISBLANK(x) || (((x) >= 0xa) && ((x) <= 0x0d)))
#define ISURLPUNTCS(x) (((x) == '-') || ((x) == '.') || ((x) == '_') || \
((x) == '~'))
#define ISUNRESERVED(x) (ISALNUM(x) || ISURLPUNTCS(x))
#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r')
#endif /* HEADER_CURL_CTYPE_H */
+84
View File
@@ -0,0 +1,84 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include "curl_endian.h"
/*
* Curl_read16_le()
*
* This function converts a 16-bit integer from the little endian format, as
* used in the incoming package to whatever endian format we are using
* natively.
*
* Parameters:
*
* buf [in] - A pointer to a 2 byte buffer.
*
* Returns the integer.
*/
unsigned short Curl_read16_le(const unsigned char *buf)
{
return (unsigned short)(((unsigned short)buf[0]) |
((unsigned short)buf[1] << 8));
}
/*
* Curl_read32_le()
*
* This function converts a 32-bit integer from the little endian format, as
* used in the incoming package to whatever endian format we are using
* natively.
*
* Parameters:
*
* buf [in] - A pointer to a 4 byte buffer.
*
* Returns the integer.
*/
unsigned int Curl_read32_le(const unsigned char *buf)
{
return ((unsigned int)buf[0]) | ((unsigned int)buf[1] << 8) |
((unsigned int)buf[2] << 16) | ((unsigned int)buf[3] << 24);
}
/*
* Curl_read16_be()
*
* This function converts a 16-bit integer from the big endian format, as
* used in the incoming package to whatever endian format we are using
* natively.
*
* Parameters:
*
* buf [in] - A pointer to a 2 byte buffer.
*
* Returns the integer.
*/
unsigned short Curl_read16_be(const unsigned char *buf)
{
return (unsigned short)(((unsigned short)buf[0] << 8) |
((unsigned short)buf[1]));
}
+36
View File
@@ -0,0 +1,36 @@
#ifndef HEADER_CURL_ENDIAN_H
#define HEADER_CURL_ENDIAN_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
/* Converts a 16-bit integer from little endian */
unsigned short Curl_read16_le(const unsigned char *buf);
/* Converts a 32-bit integer from little endian */
unsigned int Curl_read32_le(const unsigned char *buf);
/* Converts a 16-bit integer from big endian */
unsigned short Curl_read16_be(const unsigned char *buf);
#endif /* HEADER_CURL_ENDIAN_H */
+390
View File
@@ -0,0 +1,390 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifndef CURL_DISABLE_FTP
#include <curl/curl.h>
#include "curl_fnmatch.h"
#include "curl_memory.h"
/* The last #include file should be: */
#include "memdebug.h"
#ifndef HAVE_FNMATCH
#define CURLFNM_CHARSET_LEN (sizeof(char) * 256)
#define CURLFNM_CHSET_SIZE (CURLFNM_CHARSET_LEN + 15)
#define CURLFNM_NEGATE CURLFNM_CHARSET_LEN
#define CURLFNM_ALNUM (CURLFNM_CHARSET_LEN + 1)
#define CURLFNM_DIGIT (CURLFNM_CHARSET_LEN + 2)
#define CURLFNM_XDIGIT (CURLFNM_CHARSET_LEN + 3)
#define CURLFNM_ALPHA (CURLFNM_CHARSET_LEN + 4)
#define CURLFNM_PRINT (CURLFNM_CHARSET_LEN + 5)
#define CURLFNM_BLANK (CURLFNM_CHARSET_LEN + 6)
#define CURLFNM_LOWER (CURLFNM_CHARSET_LEN + 7)
#define CURLFNM_GRAPH (CURLFNM_CHARSET_LEN + 8)
#define CURLFNM_SPACE (CURLFNM_CHARSET_LEN + 9)
#define CURLFNM_UPPER (CURLFNM_CHARSET_LEN + 10)
typedef enum {
CURLFNM_SCHS_DEFAULT = 0,
CURLFNM_SCHS_RIGHTBR,
CURLFNM_SCHS_RIGHTBRLEFTBR
} setcharset_state;
typedef enum {
CURLFNM_PKW_INIT = 0,
CURLFNM_PKW_DDOT
} parsekey_state;
typedef enum {
CCLASS_OTHER = 0,
CCLASS_DIGIT,
CCLASS_UPPER,
CCLASS_LOWER
} char_class;
#define SETCHARSET_OK 1
#define SETCHARSET_FAIL 0
static int parsekeyword(const unsigned char **pattern, unsigned char *charset)
{
parsekey_state state = CURLFNM_PKW_INIT;
char keyword[10] = { 0 };
size_t i;
const unsigned char *p = *pattern;
bool found = FALSE;
for(i = 0; !found; i++) {
char c = (char)*p++;
if(i >= sizeof(keyword))
return SETCHARSET_FAIL;
switch(state) {
case CURLFNM_PKW_INIT:
if(ISLOWER(c))
keyword[i] = c;
else if(c == ':')
state = CURLFNM_PKW_DDOT;
else
return SETCHARSET_FAIL;
break;
case CURLFNM_PKW_DDOT:
if(c == ']')
found = TRUE;
else
return SETCHARSET_FAIL;
}
}
#undef KEYLEN
*pattern = p; /* move caller's pattern pointer */
if(strcmp(keyword, "digit") == 0)
charset[CURLFNM_DIGIT] = 1;
else if(strcmp(keyword, "alnum") == 0)
charset[CURLFNM_ALNUM] = 1;
else if(strcmp(keyword, "alpha") == 0)
charset[CURLFNM_ALPHA] = 1;
else if(strcmp(keyword, "xdigit") == 0)
charset[CURLFNM_XDIGIT] = 1;
else if(strcmp(keyword, "print") == 0)
charset[CURLFNM_PRINT] = 1;
else if(strcmp(keyword, "graph") == 0)
charset[CURLFNM_GRAPH] = 1;
else if(strcmp(keyword, "space") == 0)
charset[CURLFNM_SPACE] = 1;
else if(strcmp(keyword, "blank") == 0)
charset[CURLFNM_BLANK] = 1;
else if(strcmp(keyword, "upper") == 0)
charset[CURLFNM_UPPER] = 1;
else if(strcmp(keyword, "lower") == 0)
charset[CURLFNM_LOWER] = 1;
else
return SETCHARSET_FAIL;
return SETCHARSET_OK;
}
/* Return the character class. */
static char_class charclass(unsigned char c)
{
if(ISUPPER(c))
return CCLASS_UPPER;
if(ISLOWER(c))
return CCLASS_LOWER;
if(ISDIGIT(c))
return CCLASS_DIGIT;
return CCLASS_OTHER;
}
/* Include a character or a range in set. */
static void setcharorrange(const unsigned char **pp, unsigned char *charset)
{
const unsigned char *p = (*pp)++;
unsigned char c = *p++;
charset[c] = 1;
if(ISALNUM(c) && *p++ == '-') {
char_class cc = charclass(c);
unsigned char endrange = *p++;
if(endrange == '\\')
endrange = *p++;
if(endrange >= c && charclass(endrange) == cc) {
while(c++ != endrange)
if(charclass(c) == cc) /* Chars in class may be not consecutive. */
charset[c] = 1;
*pp = p;
}
}
}
/* returns 1 (TRUE) if pattern is OK, 0 if is bad ("p" is pattern pointer) */
static int setcharset(const unsigned char **p, unsigned char *charset)
{
setcharset_state state = CURLFNM_SCHS_DEFAULT;
bool something_found = FALSE;
unsigned char c;
memset(charset, 0, CURLFNM_CHSET_SIZE);
for(;;) {
c = **p;
if(!c)
return SETCHARSET_FAIL;
switch(state) {
case CURLFNM_SCHS_DEFAULT:
if(c == ']') {
if(something_found)
return SETCHARSET_OK;
something_found = TRUE;
state = CURLFNM_SCHS_RIGHTBR;
charset[c] = 1;
(*p)++;
}
else if(c == '[') {
const unsigned char *pp = *p + 1;
if(*pp++ == ':' && parsekeyword(&pp, charset))
*p = pp;
else {
charset[c] = 1;
(*p)++;
}
something_found = TRUE;
}
else if(c == '^' || c == '!') {
if(!something_found) {
if(charset[CURLFNM_NEGATE]) {
charset[c] = 1;
something_found = TRUE;
}
else
charset[CURLFNM_NEGATE] = 1; /* negate charset */
}
else
charset[c] = 1;
(*p)++;
}
else if(c == '\\') {
c = *(++(*p));
if(c)
setcharorrange(p, charset);
else
charset['\\'] = 1;
something_found = TRUE;
}
else {
setcharorrange(p, charset);
something_found = TRUE;
}
break;
case CURLFNM_SCHS_RIGHTBR:
if(c == '[') {
state = CURLFNM_SCHS_RIGHTBRLEFTBR;
charset[c] = 1;
(*p)++;
}
else if(c == ']') {
return SETCHARSET_OK;
}
else if(ISPRINT(c)) {
charset[c] = 1;
(*p)++;
state = CURLFNM_SCHS_DEFAULT;
}
else
/* used 'goto fail' instead of 'return SETCHARSET_FAIL' to avoid a
* nonsense warning 'statement not reached' at end of the fnc when
* compiling on Solaris */
goto fail;
break;
case CURLFNM_SCHS_RIGHTBRLEFTBR:
if(c == ']')
return SETCHARSET_OK;
state = CURLFNM_SCHS_DEFAULT;
charset[c] = 1;
(*p)++;
break;
}
}
fail:
return SETCHARSET_FAIL;
}
static int loop(const unsigned char *pattern, const unsigned char *string,
int maxstars)
{
const unsigned char *p = (const unsigned char *)pattern;
const unsigned char *s = (const unsigned char *)string;
unsigned char charset[CURLFNM_CHSET_SIZE] = { 0 };
for(;;) {
const unsigned char *pp;
switch(*p) {
case '*':
if(!maxstars)
return CURL_FNMATCH_NOMATCH;
/* Regroup consecutive stars and question marks. This can be done because
'*?*?*' can be expressed as '??*'. */
for(;;) {
if(*++p == '\0')
return CURL_FNMATCH_MATCH;
if(*p == '?') {
if(!*s++)
return CURL_FNMATCH_NOMATCH;
}
else if(*p != '*')
break;
}
/* Skip string characters until we find a match with pattern suffix. */
for(maxstars--; *s; s++) {
if(loop(p, s, maxstars) == CURL_FNMATCH_MATCH)
return CURL_FNMATCH_MATCH;
}
return CURL_FNMATCH_NOMATCH;
case '?':
if(!*s)
return CURL_FNMATCH_NOMATCH;
s++;
p++;
break;
case '\0':
return *s ? CURL_FNMATCH_NOMATCH : CURL_FNMATCH_MATCH;
case '\\':
if(p[1])
p++;
if(*s++ != *p++)
return CURL_FNMATCH_NOMATCH;
break;
case '[':
pp = p + 1; /* Copy in case of syntax error in set. */
if(setcharset(&pp, charset)) {
bool found = FALSE;
if(!*s)
return CURL_FNMATCH_NOMATCH;
if(charset[(unsigned int)*s])
found = TRUE;
else if(charset[CURLFNM_ALNUM])
found = ISALNUM(*s);
else if(charset[CURLFNM_ALPHA])
found = ISALPHA(*s);
else if(charset[CURLFNM_DIGIT])
found = ISDIGIT(*s);
else if(charset[CURLFNM_XDIGIT])
found = ISXDIGIT(*s);
else if(charset[CURLFNM_PRINT])
found = ISPRINT(*s);
else if(charset[CURLFNM_SPACE])
found = ISBLANK(*s);
else if(charset[CURLFNM_UPPER])
found = ISUPPER(*s);
else if(charset[CURLFNM_LOWER])
found = ISLOWER(*s);
else if(charset[CURLFNM_BLANK])
found = ISBLANK(*s);
else if(charset[CURLFNM_GRAPH])
found = ISGRAPH(*s);
if(charset[CURLFNM_NEGATE])
found = !found;
if(!found)
return CURL_FNMATCH_NOMATCH;
p = pp + 1;
s++;
break;
}
/* Syntax error in set; mismatch! */
return CURL_FNMATCH_NOMATCH;
default:
if(*p++ != *s++)
return CURL_FNMATCH_NOMATCH;
break;
}
}
}
/*
* @unittest: 1307
*/
int Curl_fnmatch(void *ptr, const char *pattern, const char *string)
{
(void)ptr; /* the argument is specified by the curl_fnmatch_callback
prototype, but not used by Curl_fnmatch() */
if(!pattern || !string) {
return CURL_FNMATCH_FAIL;
}
return loop((const unsigned char *)pattern,
(const unsigned char *)string, 2);
}
#else
#include <fnmatch.h>
/*
* @unittest: 1307
*/
int Curl_fnmatch(void *ptr, const char *pattern, const char *string)
{
(void)ptr; /* the argument is specified by the curl_fnmatch_callback
prototype, but not used by Curl_fnmatch() */
if(!pattern || !string) {
return CURL_FNMATCH_FAIL;
}
switch(fnmatch(pattern, string, 0)) {
case 0:
return CURL_FNMATCH_MATCH;
case FNM_NOMATCH:
return CURL_FNMATCH_NOMATCH;
default:
return CURL_FNMATCH_FAIL;
}
/* not reached */
}
#endif
#endif /* if FTP is disabled */
+46
View File
@@ -0,0 +1,46 @@
#ifndef HEADER_CURL_FNMATCH_H
#define HEADER_CURL_FNMATCH_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#define CURL_FNMATCH_MATCH 0
#define CURL_FNMATCH_NOMATCH 1
#define CURL_FNMATCH_FAIL 2
/* default pattern matching function
* =================================
* Implemented with recursive backtracking, if you want to use Curl_fnmatch,
* please note that there is not implemented UTF/Unicode support.
*
* Implemented features:
* '?' notation, does not match UTF characters
* '*' can also work with UTF string
* [a-zA-Z0-9] enumeration support
*
* keywords: alnum, digit, xdigit, alpha, print, blank, lower, graph, space
* and upper (use as "[[:alnum:]]")
*/
int Curl_fnmatch(void *ptr, const char *pattern, const char *string);
#endif /* HEADER_CURL_FNMATCH_H */
+163
View File
@@ -0,0 +1,163 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \
!defined(CURL_DISABLE_HSTS)
#include "urldata.h"
#include "rand.h"
#include "curl_fopen.h"
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
/*
The dirslash() function breaks a null-terminated pathname string into
directory and filename components then returns the directory component up
to, *AND INCLUDING*, a final '/'. If there is no directory in the path,
this instead returns a "" string.
This function returns a pointer to malloc'ed memory.
The input path to this function is expected to have a filename part.
*/
#ifdef _WIN32
#define PATHSEP "\\"
#define IS_SEP(x) (((x) == '/') || ((x) == '\\'))
#elif defined(MSDOS) || defined(OS2)
#define PATHSEP "\\"
#define IS_SEP(x) ((x) == '\\')
#else
#define PATHSEP "/"
#define IS_SEP(x) ((x) == '/')
#endif
static char *dirslash(const char *path)
{
size_t n;
struct dynbuf out;
DEBUGASSERT(path);
curlx_dyn_init(&out, CURL_MAX_INPUT_LENGTH);
n = strlen(path);
if(n) {
/* find the rightmost path separator, if any */
while(n && !IS_SEP(path[n-1]))
--n;
/* skip over all the path separators, if any */
while(n && IS_SEP(path[n-1]))
--n;
}
if(curlx_dyn_addn(&out, path, n))
return NULL;
/* if there was a directory, append a single trailing slash */
if(n && curlx_dyn_addn(&out, PATHSEP, 1))
return NULL;
return curlx_dyn_ptr(&out);
}
/*
* Curl_fopen() opens a file for writing with a temp name, to be renamed
* to the final name when completed. If there is an existing file using this
* name at the time of the open, this function will clone the mode from that
* file. if 'tempname' is non-NULL, it needs a rename after the file is
* written.
*/
CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
FILE **fh, char **tempname)
{
CURLcode result = CURLE_WRITE_ERROR;
unsigned char randbuf[41];
char *tempstore = NULL;
struct_stat sb;
int fd = -1;
char *dir = NULL;
*tempname = NULL;
*fh = curlx_fopen(filename, FOPEN_WRITETEXT);
if(!*fh)
goto fail;
if(
#ifdef UNDER_CE
/* !checksrc! disable BANNEDFUNC 1 */
stat(filename, &sb) == -1
#else
fstat(fileno(*fh), &sb) == -1
#endif
|| !S_ISREG(sb.st_mode)) {
return CURLE_OK;
}
curlx_fclose(*fh);
*fh = NULL;
result = Curl_rand_alnum(data, randbuf, sizeof(randbuf));
if(result)
goto fail;
dir = dirslash(filename);
if(dir) {
/* The temp filename should not end up too long for the target file
system */
tempstore = curl_maprintf("%s%s.tmp", dir, randbuf);
free(dir);
}
if(!tempstore) {
result = CURLE_OUT_OF_MEMORY;
goto fail;
}
result = CURLE_WRITE_ERROR;
#if (defined(ANDROID) || defined(__ANDROID__)) && \
(defined(__i386__) || defined(__arm__))
fd = curlx_open(tempstore, O_WRONLY | O_CREAT | O_EXCL,
(mode_t)(0600 | sb.st_mode));
#else
fd = curlx_open(tempstore, O_WRONLY | O_CREAT | O_EXCL,
0600 | sb.st_mode);
#endif
if(fd == -1)
goto fail;
*fh = curlx_fdopen(fd, FOPEN_WRITETEXT);
if(!*fh)
goto fail;
*tempname = tempstore;
return CURLE_OK;
fail:
if(fd != -1) {
close(fd);
unlink(tempstore);
}
free(tempstore);
return result;
}
#endif /* ! disabled */
+32
View File
@@ -0,0 +1,32 @@
#ifndef HEADER_CURL_FOPEN_H
#define HEADER_CURL_FOPEN_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curlx/fopen.h"
CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
FILE **fh, char **tempname);
#endif
+74
View File
@@ -0,0 +1,74 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#if !defined(CURL_DISABLE_COOKIES) || !defined(CURL_DISABLE_ALTSVC) || \
!defined(CURL_DISABLE_HSTS) || !defined(CURL_DISABLE_NETRC)
#include "curl_get_line.h"
#include "curl_memory.h"
/* The last #include file should be: */
#include "memdebug.h"
#define appendnl(b) \
curlx_dyn_addn(buf, "\n", 1)
/*
* Curl_get_line() returns only complete whole lines that end with newline.
* When 'eof' is set TRUE, the last line has been read.
*/
CURLcode Curl_get_line(struct dynbuf *buf, FILE *input, bool *eof)
{
CURLcode result;
char buffer[128];
curlx_dyn_reset(buf);
while(1) {
size_t rlen;
char *b = fgets(buffer, sizeof(buffer), input);
*eof = feof(input);
rlen = b ? strlen(b) : 0;
if(rlen) {
result = curlx_dyn_addn(buf, b, rlen);
if(result)
/* too long line or out of memory */
return result;
}
/* now check the full line */
rlen = curlx_dyn_len(buf);
b = curlx_dyn_ptr(buf);
if(rlen && (b[rlen-1] == '\n'))
/* LF at end of the line */
return CURLE_OK; /* all good */
if(*eof)
/* append a newline */
return appendnl(buf);
/* otherwise get next line to append */
}
/* UNREACHABLE */
}
#endif /* if not disabled */
+32
View File
@@ -0,0 +1,32 @@
#ifndef HEADER_CURL_GET_LINE_H
#define HEADER_CURL_GET_LINE_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curlx/dynbuf.h"
/* Curl_get_line() returns complete lines that end with a newline. */
CURLcode Curl_get_line(struct dynbuf *buf, FILE *input, bool *eof);
#endif /* HEADER_CURL_GET_LINE_H */
+97
View File
@@ -0,0 +1,97 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include "curl_gethostname.h"
/*
* Curl_gethostname() is a wrapper around gethostname() which allows
* overriding the hostname that the function would normally return.
* This capability is used by the test suite to verify exact matching
* of NTLM authentication, which exercises libcurl's MD4 and DES code
* as well as by the SMTP module when a hostname is not provided.
*
* For libcurl debug enabled builds hostname overriding takes place
* when environment variable CURL_GETHOSTNAME is set, using the value
* held by the variable to override returned hostname.
*
* Note: The function always returns the un-qualified hostname rather
* than being provider dependent.
*/
int Curl_gethostname(char * const name, GETHOSTNAME_TYPE_ARG2 namelen)
{
#ifndef HAVE_GETHOSTNAME
/* Allow compilation and return failure when unavailable */
(void)name;
(void)namelen;
return -1;
#else
int err;
char *dot;
#ifdef DEBUGBUILD
/* Override hostname when environment variable CURL_GETHOSTNAME is set */
const char *force_hostname = getenv("CURL_GETHOSTNAME");
if(force_hostname) {
if(strlen(force_hostname) < (size_t)namelen)
strcpy(name, force_hostname);
else
return 1; /* can't do it */
err = 0;
}
else {
name[0] = '\0';
err = gethostname(name, namelen);
}
#else /* DEBUGBUILD */
name[0] = '\0';
#ifdef __AMIGA__
err = gethostname((unsigned char *)name, namelen);
#else
err = gethostname(name, namelen);
#endif
#endif
name[namelen - 1] = '\0';
if(err)
return err;
/* Truncate domain, leave only machine name */
dot = strchr(name, '.');
if(dot)
*dot = '\0';
return 0;
#endif
}
+33
View File
@@ -0,0 +1,33 @@
#ifndef HEADER_CURL_GETHOSTNAME_H
#define HEADER_CURL_GETHOSTNAME_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
/* Hostname buffer size */
#define HOSTNAME_MAX 1024
/* This returns the local machine's un-qualified hostname */
int Curl_gethostname(char * const name, GETHOSTNAME_TYPE_ARG2 namelen);
#endif /* HEADER_CURL_GETHOSTNAME_H */
+455
View File
@@ -0,0 +1,455 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifdef HAVE_GSSAPI
#include "curl_gssapi.h"
#include "sendf.h"
#ifdef DEBUGBUILD
#if defined(HAVE_GSSGNU) || !defined(_WIN32)
/* 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(). */
#define Curl_gss_alloc (malloc)
#define Curl_gss_free (free)
#define CURL_GSS_STUB
/* For correctness this would be required for all platforms, not only Windows,
but, as of v1.22.1, MIT Kerberos uses a special allocator only for Windows,
and the availability of 'gssapi/gssapi_alloc.h' is difficult to detect,
because GSS headers are not versioned, and there is also no other macro to
indicate 1.18+ vs. previous versions. On Windows we can use 'GSS_S_BAD_MIC'.
*/
#elif defined(_WIN32) && defined(GSS_S_BAD_MIC) /* MIT Kerberos 1.15+ */
/* MIT Kerberos 1.10+ (Windows), 1.18+ (all platforms), missing from GNU GSS */
#include <gssapi/gssapi_alloc.h>
#define Curl_gss_alloc gssalloc_malloc
#define Curl_gss_free gssalloc_free
#define CURL_GSS_STUB
#endif
#endif /* DEBUGBUILD */
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
#ifdef __GNUC__
#define CURL_ALIGN8 __attribute__((aligned(8)))
#else
#define CURL_ALIGN8
#endif
#if defined(__GNUC__) && defined(__APPLE__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif
gss_OID_desc Curl_spnego_mech_oid CURL_ALIGN8 = {
6, CURL_UNCONST("\x2b\x06\x01\x05\x05\x02")
};
gss_OID_desc Curl_krb5_mech_oid CURL_ALIGN8 = {
9, CURL_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")
};
#ifdef CURL_GSS_STUB
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;
}
token = Curl_gss_alloc(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)) {
Curl_gss_free(token);
free(ctx);
*min = STUB_GSS_NO_MEMORY;
return GSS_S_FAILURE;
}
if(strlen(creds) + target_desc.length + 5 >= sizeof(ctx->creds)) {
Curl_gss_free(token);
free(ctx);
*min = STUB_GSS_NO_MEMORY;
return GSS_S_FAILURE;
}
/* Token format: creds:target:type:padding */
used = curl_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) {
Curl_gss_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 /* CURL_GSS_STUB */
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;
if(mutual_auth)
req_flags |= GSS_C_MUTUAL_FLAG;
if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_POLICY_FLAG) {
#ifdef GSS_C_DELEG_POLICY_FLAG /* MIT Kerberos 1.8+, missing from GNU GSS */
req_flags |= GSS_C_DELEG_POLICY_FLAG;
#else
infof(data, "WARNING: support for CURLGSSAPI_DELEGATION_POLICY_FLAG not "
"compiled in");
#endif
}
if(data->set.gssapi_delegation & CURLGSSAPI_DELEGATION_FLAG)
req_flags |= GSS_C_DELEG_FLAG;
#ifdef CURL_GSS_STUB
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 /* CURL_GSS_STUB */
return gss_init_sec_context(minor_status,
GSS_C_NO_CREDENTIAL, /* cred_handle */
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 */);
}
OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min,
gss_ctx_id_t *context,
gss_buffer_t output_token)
{
#ifdef CURL_GSS_STUB
if(getenv("CURL_STUB_GSS_CREDS"))
return stub_gss_delete_sec_context(min,
(struct stub_gss_ctx_id_t_desc **)context,
output_token);
#endif /* CURL_GSS_STUB */
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) {
OM_uint32 maj_stat;
OM_uint32 min_stat;
OM_uint32 msg_ctx = 0;
gss_buffer_desc status_string = GSS_C_EMPTY_BUFFER;
do {
maj_stat = gss_display_status(&min_stat,
status,
type,
GSS_C_NO_OID,
&msg_ctx,
&status_string);
if(maj_stat == GSS_S_COMPLETE && status_string.length > 0) {
if(GSS_LOG_BUFFER_LEN > len + status_string.length + 3) {
len += curl_msnprintf(buf + len, GSS_LOG_BUFFER_LEN - len,
"%.*s. ", (int)status_string.length,
(char *)status_string.value);
}
}
gss_release_buffer(&min_stat, &status_string);
} while(!GSS_ERROR(maj_stat) && msg_ctx);
return len;
}
/*
* Curl_gss_log_error()
*
* This is used to log a GSS-API error status.
*
* Parameters:
*
* data [in] - The session handle.
* prefix [in] - The prefix of the log message.
* major [in] - The major status code.
* minor [in] - The minor status code.
*/
void Curl_gss_log_error(struct Curl_easy *data, const char *prefix,
OM_uint32 major, OM_uint32 minor)
{
char buf[GSS_LOG_BUFFER_LEN];
size_t len = 0;
if(major != GSS_S_FAILURE)
len = display_gss_error(major, GSS_C_GSS_CODE, buf, len);
display_gss_error(minor, GSS_C_MECH_CODE, buf, len);
infof(data, "%s%s", prefix, buf);
#ifdef CURL_DISABLE_VERBOSE_STRINGS
(void)data;
(void)prefix;
#endif
}
#if defined(__GNUC__) && defined(__APPLE__)
#pragma GCC diagnostic pop
#endif
#endif /* HAVE_GSSAPI */
+65
View File
@@ -0,0 +1,65 @@
#ifndef HEADER_CURL_GSSAPI_H
#define HEADER_CURL_GSSAPI_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include "urldata.h"
#ifdef HAVE_GSSAPI
#ifdef GSS_C_CHANNEL_BOUND_FLAG /* MIT Kerberos 1.19+, missing from GNU GSS */
#define CURL_GSSAPI_HAS_CHANNEL_BINDING
#endif
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_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,
OM_uint32 major, OM_uint32 minor);
/* Define our privacy and integrity protection values */
#define GSSAUTH_P_NONE 1
#define GSSAUTH_P_INTEGRITY 2
#define GSSAUTH_P_PRIVACY 4
#endif /* HAVE_GSSAPI */
#endif /* HEADER_CURL_GSSAPI_H */
+76
View File
@@ -0,0 +1,76 @@
#ifndef HEADER_CURL_HMAC_H
#define HEADER_CURL_HMAC_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \
!defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \
defined(USE_LIBSSH2) || defined(USE_SSL)
#include <curl/curl.h>
#define HMAC_MD5_LENGTH 16
typedef CURLcode (*HMAC_hinit)(void *context);
typedef void (*HMAC_hupdate)(void *context,
const unsigned char *data,
unsigned int len);
typedef void (*HMAC_hfinal)(unsigned char *result, void *context);
/* Per-hash function HMAC parameters. */
struct HMAC_params {
HMAC_hinit hinit; /* Initialize context procedure. */
HMAC_hupdate hupdate; /* Update context with data. */
HMAC_hfinal hfinal; /* Get final result procedure. */
unsigned int ctxtsize; /* Context structure size. */
unsigned int maxkeylen; /* Maximum key length (bytes). */
unsigned int resultlen; /* Result length (bytes). */
};
/* HMAC computation context. */
struct HMAC_context {
const struct HMAC_params *hash; /* Hash function definition. */
void *hashctxt1; /* Hash function context 1. */
void *hashctxt2; /* Hash function context 2. */
};
/* Prototypes. */
struct HMAC_context *Curl_HMAC_init(const struct HMAC_params *hashparams,
const unsigned char *key,
unsigned int keylen);
int Curl_HMAC_update(struct HMAC_context *context,
const unsigned char *data,
unsigned int len);
int Curl_HMAC_final(struct HMAC_context *context, unsigned char *result);
CURLcode Curl_hmacit(const struct HMAC_params *hashparams,
const unsigned char *key, const size_t keylen,
const unsigned char *data, const size_t datalen,
unsigned char *output);
#endif
#endif /* HEADER_CURL_HMAC_H */
+36
View File
@@ -0,0 +1,36 @@
#ifndef HEADER_CURL_LDAP_H
#define HEADER_CURL_LDAP_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#ifndef CURL_DISABLE_LDAP
extern const struct Curl_handler Curl_handler_ldap;
#if !defined(CURL_DISABLE_LDAPS) && \
((defined(USE_OPENLDAP) && defined(USE_SSL)) || \
(!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL)))
extern const struct Curl_handler Curl_handler_ldaps;
#endif
#endif
#endif /* HEADER_CURL_LDAP_H */
+39
View File
@@ -0,0 +1,39 @@
#ifndef HEADER_CURL_MD4_H
#define HEADER_CURL_MD4_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include <curl/curl.h>
#ifdef USE_CURL_NTLM_CORE
#define MD4_DIGEST_LENGTH 16
CURLcode Curl_md4it(unsigned char *output, const unsigned char *input,
const size_t len);
#endif /* USE_CURL_NTLM_CORE */
#endif /* HEADER_CURL_MD4_H */
+67
View File
@@ -0,0 +1,67 @@
#ifndef HEADER_CURL_MD5_H
#define HEADER_CURL_MD5_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \
!defined(CURL_DISABLE_DIGEST_AUTH)
#include "curl_hmac.h"
#define MD5_DIGEST_LEN 16
typedef CURLcode (*Curl_MD5_init_func)(void *context);
typedef void (*Curl_MD5_update_func)(void *context,
const unsigned char *data,
unsigned int len);
typedef void (*Curl_MD5_final_func)(unsigned char *result, void *context);
struct MD5_params {
Curl_MD5_init_func md5_init_func; /* Initialize context procedure */
Curl_MD5_update_func md5_update_func; /* Update context with data */
Curl_MD5_final_func md5_final_func; /* Get final result procedure */
unsigned int md5_ctxtsize; /* Context structure size */
unsigned int md5_resultlen; /* Result length (bytes) */
};
struct MD5_context {
const struct MD5_params *md5_hash; /* Hash function definition */
void *md5_hashctx; /* Hash function context */
};
extern const struct MD5_params Curl_DIGEST_MD5;
extern const struct HMAC_params Curl_HMAC_MD5;
CURLcode Curl_md5it(unsigned char *output, const unsigned char *input,
const size_t len);
struct MD5_context *Curl_MD5_init(const struct MD5_params *md5params);
CURLcode Curl_MD5_update(struct MD5_context *context,
const unsigned char *data,
unsigned int len);
CURLcode Curl_MD5_final(struct MD5_context *context, unsigned char *result);
#endif
#endif /* HEADER_CURL_MD5_H */
+37
View File
@@ -0,0 +1,37 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
/* Unset redefined system symbols. */
#undef strdup
#undef malloc
#undef calloc
#undef realloc
#undef free
#ifdef _WIN32
#undef Curl_tcsdup
#endif
#undef HEADER_CURL_MEMORY_H
#undef HEADER_CURL_MEMDEBUG_H
+89
View File
@@ -0,0 +1,89 @@
#ifndef HEADER_CURL_MEMORY_H
#define HEADER_CURL_MEMORY_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
/*
* Nasty internal details ahead...
*
* File curl_memory.h must be included by _all_ *.c source files
* that use memory related functions strdup, malloc, calloc, realloc
* or free, and given source file is used to build libcurl library.
* It should be included immediately before memdebug.h as the last files
* included to avoid undesired interaction with other memory function
* headers in dependent libraries.
*
* There is nearly no exception to above rule. All libcurl source
* files in 'lib' subdirectory as well as those living deep inside
* 'packages' subdirectories and linked together in order to build
* libcurl library shall follow it.
*
* File lib/strdup.c is an exception, given that it provides a strdup
* clone implementation while using malloc. Extra care needed inside
* this one.
*
* The need for curl_memory.h inclusion is due to libcurl's feature
* of allowing library user to provide memory replacement functions,
* memory callbacks, at runtime with curl_global_init_mem()
*
* Any *.c source file used to build libcurl library that does not
* include curl_memory.h and uses any memory function of the five
* mentioned above will compile without any indication, but it will
* trigger weird memory related issues at runtime.
*
*/
#ifndef CURLDEBUG
/*
* libcurl's 'memory tracking' system defines strdup, malloc, calloc,
* realloc and free, along with others, in memdebug.h in a different
* way although still using memory callbacks forward declared above.
* When using the 'memory tracking' system (CURLDEBUG defined) we do
* not define here the five memory functions given that definitions
* from memdebug.h are the ones that shall be used.
*/
#undef strdup
#define strdup(ptr) Curl_cstrdup(ptr)
#undef malloc
#define malloc(size) Curl_cmalloc(size)
#undef calloc
#define calloc(nbelem,size) Curl_ccalloc(nbelem, size)
#undef realloc
#define realloc(ptr,size) Curl_crealloc(ptr, size)
#undef free
#define free(ptr) Curl_cfree(ptr)
#ifdef _WIN32
#undef Curl_tcsdup
#ifdef UNICODE
#define Curl_tcsdup(ptr) Curl_wcsdup(ptr)
#else
#define Curl_tcsdup(ptr) Curl_cstrdup(ptr)
#endif
#endif /* _WIN32 */
#endif /* CURLDEBUG */
#endif /* HEADER_CURL_MEMORY_H */
+62
View File
@@ -0,0 +1,62 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include <curl/curl.h>
#include "curl_memrchr.h"
#include "curl_memory.h"
/* The last #include file should be: */
#include "memdebug.h"
#ifndef HAVE_MEMRCHR
/*
* Curl_memrchr()
*
* Our memrchr() function clone for systems which lack this function. The
* memrchr() function is like the memchr() function, except that it searches
* backwards from the end of the n bytes pointed to by s instead of forward
* from the beginning.
*/
void *
Curl_memrchr(const void *s, int c, size_t n)
{
if(n > 0) {
const unsigned char *p = s;
const unsigned char *q = s;
p += n - 1;
while(p >= q) {
if(*p == (unsigned char)c)
return CURL_UNCONST(p);
p--;
}
}
return NULL;
}
#endif /* HAVE_MEMRCHR */
+42
View File
@@ -0,0 +1,42 @@
#ifndef HEADER_CURL_MEMRCHR_H
#define HEADER_CURL_MEMRCHR_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifdef HAVE_MEMRCHR
#include <string.h>
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#else /* HAVE_MEMRCHR */
void *Curl_memrchr(const void *s, int c, size_t n);
#define memrchr(x,y,z) Curl_memrchr((x),(y),(z))
#endif /* HAVE_MEMRCHR */
#endif /* HEADER_CURL_MEMRCHR_H */
+679
View File
@@ -0,0 +1,679 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifdef USE_CURL_NTLM_CORE
/*
* NTLM details:
*
* https://davenport.sourceforge.net/ntlm.html
* https://www.innovation.ch/java/ntlm.html
*/
/* Please keep the SSL backend-specific #if branches in this order:
1. USE_OPENSSL
2. USE_WOLFSSL
3. USE_GNUTLS
4. USE_MBEDTLS
5. USE_OS400CRYPTO
6. USE_WIN32_CRYPTO
This ensures that:
- the same SSL branch gets activated throughout this source
file even if multiple backends are enabled at the same time.
- OpenSSL has higher priority than Windows Crypt, due
to issues with the latter supporting NTLM2Session responses
in NTLM type-3 messages.
*/
#ifdef USE_MBEDTLS
#include <mbedtls/version.h>
#if MBEDTLS_VERSION_NUMBER < 0x03020000
#error "mbedTLS 3.2.0 or later required"
#endif
#endif
#if defined(USE_OPENSSL) && defined(HAVE_DES_ECB_ENCRYPT)
#define USE_OPENSSL_DES
#elif defined(USE_WOLFSSL) && defined(HAVE_WOLFSSL_DES_ECB_ENCRYPT)
#define USE_OPENSSL_DES
#elif defined(USE_MBEDTLS) && defined(HAVE_MBEDTLS_DES_CRYPT_ECB)
#define USE_MBEDTLS_DES
#endif
#ifdef USE_OPENSSL_DES
#ifdef USE_OPENSSL
# include <openssl/des.h>
# include <openssl/ssl.h>
# include <openssl/rand.h>
# ifdef OPENSSL_IS_AWSLC /* for versions 1.2.0 to 1.30.1 */
# define DES_set_key_unchecked (void)DES_set_key
# endif
# define DESKEY(x) &x
#else
# include <wolfssl/options.h>
# include <wolfssl/openssl/des.h>
# include <wolfssl/openssl/ssl.h>
# include <wolfssl/openssl/rand.h>
# ifdef OPENSSL_COEXIST
# define DES_key_schedule WOLFSSL_DES_key_schedule
# define DES_cblock WOLFSSL_DES_cblock
# define DES_set_odd_parity wolfSSL_DES_set_odd_parity
# define DES_set_key wolfSSL_DES_set_key
# define DES_set_key_unchecked wolfSSL_DES_set_key_unchecked
# define DES_ecb_encrypt wolfSSL_DES_ecb_encrypt
# define DESKEY(x) ((WOLFSSL_DES_key_schedule *)(x))
# else
# define DESKEY(x) &x
# endif
#endif
#define DESKEYARG(x) *x
#elif defined(USE_GNUTLS)
# include <nettle/des.h>
# define USE_CURL_DES_SET_ODD_PARITY
#elif defined(USE_MBEDTLS_DES)
# include <mbedtls/des.h>
#elif defined(USE_OS400CRYPTO)
# include "cipher.mih" /* mih/cipher */
# define USE_CURL_DES_SET_ODD_PARITY
#elif defined(USE_WIN32_CRYPTO)
# include <wincrypt.h>
# define USE_CURL_DES_SET_ODD_PARITY
#else
# error "cannot compile NTLM support without a crypto library with DES."
#endif
#include "urldata.h"
#include "strcase.h"
#include "curl_ntlm_core.h"
#include "curl_md5.h"
#include "curl_hmac.h"
#include "curlx/warnless.h"
#include "curl_endian.h"
#include "curl_md4.h"
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
#ifdef USE_CURL_DES_SET_ODD_PARITY
/*
* curl_des_set_odd_parity()
*
* Copyright (C) Steve Holme, <steve_holme@hotmail.com>
*
* This is used to apply odd parity to the given byte array. It is typically
* used by when a cryptography engine does not have its own version.
*
* The function is a port of the Java based oddParity() function over at:
*
* https://davenport.sourceforge.net/ntlm.html
*
* Parameters:
*
* bytes [in/out] - The data whose parity bits are to be adjusted for
* odd parity.
* len [out] - The length of the data.
*/
static void curl_des_set_odd_parity(unsigned char *bytes, size_t len)
{
size_t i;
for(i = 0; i < len; i++) {
unsigned char b = bytes[i];
bool needs_parity = (((b >> 7) ^ (b >> 6) ^ (b >> 5) ^
(b >> 4) ^ (b >> 3) ^ (b >> 2) ^
(b >> 1)) & 0x01) == 0;
if(needs_parity)
bytes[i] |= 0x01;
else
bytes[i] &= 0xfe;
}
}
#endif /* USE_CURL_DES_SET_ODD_PARITY */
/*
* Turns a 56-bit key into being 64-bit wide.
*/
static void extend_key_56_to_64(const unsigned char *key_56, char *key)
{
key[0] = (char)key_56[0];
key[1] = (char)(((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1));
key[2] = (char)(((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2));
key[3] = (char)(((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3));
key[4] = (char)(((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4));
key[5] = (char)(((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5));
key[6] = (char)(((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6));
key[7] = (char) ((key_56[6] << 1) & 0xFF);
}
#ifdef USE_OPENSSL_DES
/*
* Turns a 56-bit key into a 64-bit, odd parity key and sets the key. The
* key schedule ks is also set.
*/
static void setup_des_key(const unsigned char *key_56,
DES_key_schedule DESKEYARG(ks))
{
DES_cblock key;
/* Expand the 56-bit key to 64 bits */
extend_key_56_to_64(key_56, (char *) &key);
/* Set the key parity to odd */
DES_set_odd_parity(&key);
/* Set the key */
DES_set_key_unchecked(&key, ks);
}
#elif defined(USE_GNUTLS)
static void setup_des_key(const unsigned char *key_56,
struct des_ctx *des)
{
char key[8];
/* 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));
/* Set the key */
des_set_key(des, (const uint8_t *) key);
}
#elif defined(USE_MBEDTLS_DES)
static bool encrypt_des(const unsigned char *in, unsigned char *out,
const unsigned char *key_56)
{
mbedtls_des_context ctx;
char key[8];
/* Expand the 56-bit key to 64 bits */
extend_key_56_to_64(key_56, key);
/* Set the key parity to odd */
mbedtls_des_key_set_parity((unsigned char *) key);
/* Perform the encryption */
mbedtls_des_init(&ctx);
mbedtls_des_setkey_enc(&ctx, (unsigned char *) key);
return mbedtls_des_crypt_ecb(&ctx, in, out) == 0;
}
#elif defined(USE_OS400CRYPTO)
static bool encrypt_des(const unsigned char *in, unsigned char *out,
const unsigned char *key_56)
{
char key[8];
_CIPHER_Control_T ctl;
/* Setup the cipher control structure */
ctl.Func_ID = ENCRYPT_ONLY;
ctl.Data_Len = sizeof(key);
/* Expand the 56-bit key to 64 bits */
extend_key_56_to_64(key_56, ctl.Crypto_Key);
/* Set the key parity to odd */
curl_des_set_odd_parity((unsigned char *) ctl.Crypto_Key, ctl.Data_Len);
/* Perform the encryption */
_CIPHER((_SPCPTR *) &out, &ctl, (_SPCPTR *) &in);
return TRUE;
}
#elif defined(USE_WIN32_CRYPTO)
static bool encrypt_des(const unsigned char *in, unsigned char *out,
const unsigned char *key_56)
{
HCRYPTPROV hprov;
HCRYPTKEY hkey;
struct {
BLOBHEADER hdr;
unsigned int len;
char key[8];
} blob;
DWORD len = 8;
/* Acquire the crypto provider */
if(!CryptAcquireContext(&hprov, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
return FALSE;
/* Setup the key blob structure */
memset(&blob, 0, sizeof(blob));
blob.hdr.bType = PLAINTEXTKEYBLOB;
blob.hdr.bVersion = 2;
blob.hdr.aiKeyAlg = CALG_DES;
blob.len = sizeof(blob.key);
/* Expand the 56-bit key to 64 bits */
extend_key_56_to_64(key_56, blob.key);
/* Set the key parity to odd */
curl_des_set_odd_parity((unsigned char *) blob.key, sizeof(blob.key));
/* Import the key */
if(!CryptImportKey(hprov, (BYTE *) &blob, sizeof(blob), 0, 0, &hkey)) {
CryptReleaseContext(hprov, 0);
return FALSE;
}
memcpy(out, in, 8);
/* Perform the encryption */
CryptEncrypt(hkey, 0, FALSE, 0, out, &len, len);
CryptDestroyKey(hkey);
CryptReleaseContext(hprov, 0);
return TRUE;
}
#endif /* USE_WIN32_CRYPTO */
/*
* takes a 21 byte array and treats it as 3 56-bit DES keys. The
* 8 byte plaintext is encrypted with each key and the resulting 24
* bytes are stored in the results array.
*/
void Curl_ntlm_core_lm_resp(const unsigned char *keys,
const unsigned char *plaintext,
unsigned char *results)
{
#ifdef USE_OPENSSL_DES
DES_key_schedule ks;
setup_des_key(keys, DESKEY(ks));
DES_ecb_encrypt((DES_cblock*)CURL_UNCONST(plaintext),
(DES_cblock*)results, DESKEY(ks), DES_ENCRYPT);
setup_des_key(keys + 7, DESKEY(ks));
DES_ecb_encrypt((DES_cblock*)CURL_UNCONST(plaintext),
(DES_cblock*)(results + 8), DESKEY(ks), DES_ENCRYPT);
setup_des_key(keys + 14, DESKEY(ks));
DES_ecb_encrypt((DES_cblock*)CURL_UNCONST(plaintext),
(DES_cblock*)(results + 16), DESKEY(ks), DES_ENCRYPT);
#elif defined(USE_GNUTLS)
struct des_ctx des;
setup_des_key(keys, &des);
des_encrypt(&des, 8, results, plaintext);
setup_des_key(keys + 7, &des);
des_encrypt(&des, 8, results + 8, plaintext);
setup_des_key(keys + 14, &des);
des_encrypt(&des, 8, results + 16, plaintext);
#elif defined(USE_MBEDTLS_DES) || 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);
#else
(void)keys;
(void)plaintext;
(void)results;
#endif
}
/*
* Set up lanmanager hashed password
*/
CURLcode Curl_ntlm_core_mk_lm_hash(const char *password,
unsigned char *lmbuffer /* 21 bytes */)
{
unsigned char pw[14];
static const unsigned char magic[] = {
0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 /* i.e. KGS!@#$% */
};
size_t len = CURLMIN(strlen(password), 14);
Curl_strntoupper((char *)pw, password, len);
memset(&pw[len], 0, 14 - len);
{
/* Create LanManager hashed password. */
#ifdef USE_OPENSSL_DES
DES_key_schedule ks;
setup_des_key(pw, DESKEY(ks));
DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(magic),
(DES_cblock *)lmbuffer, DESKEY(ks), DES_ENCRYPT);
setup_des_key(pw + 7, DESKEY(ks));
DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(magic),
(DES_cblock *)(lmbuffer + 8), DESKEY(ks), DES_ENCRYPT);
#elif defined(USE_GNUTLS)
struct des_ctx des;
setup_des_key(pw, &des);
des_encrypt(&des, 8, lmbuffer, magic);
setup_des_key(pw + 7, &des);
des_encrypt(&des, 8, lmbuffer + 8, magic);
#elif defined(USE_MBEDTLS_DES) || defined(USE_OS400CRYPTO) || \
defined(USE_WIN32_CRYPTO)
encrypt_des(magic, lmbuffer, pw);
encrypt_des(magic, lmbuffer + 8, pw + 7);
#endif
memset(lmbuffer + 16, 0, 21 - 16);
}
return CURLE_OK;
}
static void ascii_to_unicode_le(unsigned char *dest, const char *src,
size_t srclen)
{
size_t i;
for(i = 0; i < srclen; i++) {
dest[2 * i] = (unsigned char)src[i];
dest[2 * i + 1] = '\0';
}
}
#ifndef USE_WINDOWS_SSPI
static void ascii_uppercase_to_unicode_le(unsigned char *dest,
const char *src, size_t srclen)
{
size_t i;
for(i = 0; i < srclen; i++) {
dest[2 * i] = (unsigned char)(Curl_raw_toupper(src[i]));
dest[2 * i + 1] = '\0';
}
}
#endif /* !USE_WINDOWS_SSPI */
/*
* Set up nt hashed passwords
* @unittest: 1600
*/
CURLcode Curl_ntlm_core_mk_nt_hash(const char *password,
unsigned char *ntbuffer /* 21 bytes */)
{
size_t len = strlen(password);
unsigned char *pw;
CURLcode result;
if(len > SIZE_MAX/2) /* avoid integer overflow */
return CURLE_OUT_OF_MEMORY;
pw = len ? malloc(len * 2) : (unsigned char *)strdup("");
if(!pw)
return CURLE_OUT_OF_MEMORY;
ascii_to_unicode_le(pw, password, len);
/* Create NT hashed password. */
result = Curl_md4it(ntbuffer, pw, 2 * len);
if(!result)
memset(ntbuffer + 16, 0, 21 - 16);
free(pw);
return result;
}
#ifndef USE_WINDOWS_SSPI
#define NTLMv2_BLOB_SIGNATURE "\x01\x01\x00\x00"
#define NTLMv2_BLOB_LEN (44 -16 + ntlm->target_info_len + 4)
/* Timestamp in tenths of a microsecond since January 1, 1601 00:00:00 UTC. */
struct ms_filetime {
unsigned int dwLowDateTime;
unsigned int dwHighDateTime;
};
/* Convert a time_t to an MS FILETIME (MS-DTYP section 2.3.3). */
static void time2filetime(struct ms_filetime *ft, time_t t)
{
#if SIZEOF_TIME_T > 4
t = (t + (curl_off_t)11644473600) * 10000000;
ft->dwLowDateTime = (unsigned int) (t & 0xFFFFFFFF);
ft->dwHighDateTime = (unsigned int) (t >> 32);
#else
unsigned int r, s;
unsigned int i;
ft->dwLowDateTime = (unsigned int)t & 0xFFFFFFFF;
ft->dwHighDateTime = 0;
# ifndef HAVE_TIME_T_UNSIGNED
/* Extend sign if needed. */
if(ft->dwLowDateTime & 0x80000000)
ft->dwHighDateTime = ~(unsigned int)0;
# endif
/* Bias seconds to Jan 1, 1601.
134774 days = 11644473600 seconds = 0x2B6109100 */
r = ft->dwLowDateTime;
ft->dwLowDateTime = (ft->dwLowDateTime + 0xB6109100U) & 0xFFFFFFFF;
ft->dwHighDateTime += ft->dwLowDateTime < r ? 0x03 : 0x02;
/* Convert to tenths of microseconds. */
ft->dwHighDateTime *= 10000000;
i = 32;
do {
i -= 8;
s = ((ft->dwLowDateTime >> i) & 0xFF) * (10000000 - 1);
r = (s << i) & 0xFFFFFFFF;
s >>= 1; /* Split shift to avoid width overflow. */
s >>= 31 - i;
ft->dwLowDateTime = (ft->dwLowDateTime + r) & 0xFFFFFFFF;
if(ft->dwLowDateTime < r)
s++;
ft->dwHighDateTime += s;
} while(i);
ft->dwHighDateTime &= 0xFFFFFFFF;
#endif
}
/* This creates the NTLMv2 hash by using NTLM hash as the key and Unicode
* (uppercase UserName + Domain) as the data
*/
CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, size_t userlen,
const char *domain, size_t domlen,
unsigned char *ntlmhash,
unsigned char *ntlmv2hash)
{
/* Unicode representation */
size_t identity_len;
unsigned char *identity;
CURLcode result = CURLE_OK;
if((userlen > CURL_MAX_INPUT_LENGTH) || (domlen > CURL_MAX_INPUT_LENGTH))
return CURLE_OUT_OF_MEMORY;
identity_len = (userlen + domlen) * 2;
identity = malloc(identity_len + 1);
if(!identity)
return CURLE_OUT_OF_MEMORY;
ascii_uppercase_to_unicode_le(identity, user, userlen);
ascii_to_unicode_le(identity + (userlen << 1), domain, domlen);
result = Curl_hmacit(&Curl_HMAC_MD5, ntlmhash, 16, identity, identity_len,
ntlmv2hash);
free(identity);
return result;
}
/*
* Curl_ntlm_core_mk_ntlmv2_resp()
*
* This creates the NTLMv2 response as set in the NTLM type-3 message.
*
* Parameters:
*
* ntlmv2hash [in] - The NTLMv2 hash (16 bytes)
* challenge_client [in] - The client nonce (8 bytes)
* ntlm [in] - The NTLM data struct being used to read TargetInfo
and Server challenge received in the type-2 message
* ntresp [out] - The address where a pointer to newly allocated
* memory holding the NTLMv2 response.
* ntresp_len [out] - The length of the output message.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash,
unsigned char *challenge_client,
struct ntlmdata *ntlm,
unsigned char **ntresp,
unsigned int *ntresp_len)
{
/* NTLMv2 response structure :
------------------------------------------------------------------------------
0 HMAC MD5 16 bytes
------BLOB--------------------------------------------------------------------
16 Signature 0x01010000
20 Reserved long (0x00000000)
24 Timestamp LE, 64-bit signed value representing the number of
tenths of a microsecond since January 1, 1601.
32 Client Nonce 8 bytes
40 Unknown 4 bytes
44 Target Info N bytes (from the type-2 message)
44+N Unknown 4 bytes
------------------------------------------------------------------------------
*/
unsigned int len = 0;
unsigned char *ptr = NULL;
unsigned char hmac_output[HMAC_MD5_LENGTH];
struct ms_filetime tw;
CURLcode result = CURLE_OK;
/* Calculate the timestamp */
#ifdef DEBUGBUILD
char *force_timestamp = getenv("CURL_FORCETIME");
if(force_timestamp)
time2filetime(&tw, (time_t) 0);
else
#endif
time2filetime(&tw, time(NULL));
/* Calculate the response len */
len = HMAC_MD5_LENGTH + NTLMv2_BLOB_LEN;
/* Allocate the response */
ptr = calloc(1, len);
if(!ptr)
return CURLE_OUT_OF_MEMORY;
/* Create the BLOB structure */
curl_msnprintf((char *)ptr + HMAC_MD5_LENGTH, NTLMv2_BLOB_LEN,
"%c%c%c%c" /* NTLMv2_BLOB_SIGNATURE */
"%c%c%c%c" /* Reserved = 0 */
"%c%c%c%c%c%c%c%c", /* Timestamp */
NTLMv2_BLOB_SIGNATURE[0], NTLMv2_BLOB_SIGNATURE[1],
NTLMv2_BLOB_SIGNATURE[2], NTLMv2_BLOB_SIGNATURE[3],
0, 0, 0, 0,
LONGQUARTET(tw.dwLowDateTime),
LONGQUARTET(tw.dwHighDateTime));
memcpy(ptr + 32, challenge_client, 8);
if(ntlm->target_info_len)
memcpy(ptr + 44, ntlm->target_info, ntlm->target_info_len);
/* Concatenate the Type 2 challenge with the BLOB and do HMAC MD5 */
memcpy(ptr + 8, &ntlm->nonce[0], 8);
result = Curl_hmacit(&Curl_HMAC_MD5, ntlmv2hash, HMAC_MD5_LENGTH, ptr + 8,
NTLMv2_BLOB_LEN + 8, hmac_output);
if(result) {
free(ptr);
return result;
}
/* Concatenate the HMAC MD5 output with the BLOB */
memcpy(ptr, hmac_output, HMAC_MD5_LENGTH);
/* Return the response */
*ntresp = ptr;
*ntresp_len = len;
return result;
}
/*
* Curl_ntlm_core_mk_lmv2_resp()
*
* This creates the LMv2 response as used in the NTLM type-3 message.
*
* Parameters:
*
* ntlmv2hash [in] - The NTLMv2 hash (16 bytes)
* challenge_client [in] - The client nonce (8 bytes)
* challenge_client [in] - The server challenge (8 bytes)
* lmresp [out] - The LMv2 response (24 bytes)
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash,
unsigned char *challenge_client,
unsigned char *challenge_server,
unsigned char *lmresp)
{
unsigned char data[16];
unsigned char hmac_output[16];
CURLcode result = CURLE_OK;
memcpy(&data[0], challenge_server, 8);
memcpy(&data[8], challenge_client, 8);
result = Curl_hmacit(&Curl_HMAC_MD5, ntlmv2hash, 16, &data[0], 16,
hmac_output);
if(result)
return result;
/* Concatenate the HMAC MD5 output with the client nonce */
memcpy(lmresp, hmac_output, 16);
memcpy(lmresp + 16, challenge_client, 8);
return result;
}
#endif /* !USE_WINDOWS_SSPI */
#endif /* USE_CURL_NTLM_CORE */
+76
View File
@@ -0,0 +1,76 @@
#ifndef HEADER_CURL_NTLM_CORE_H
#define HEADER_CURL_NTLM_CORE_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifdef 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)), \
((int)(((x) >> 16) & 0xff)), ((int)(((x) >> 24) & 0xff))
void Curl_ntlm_core_lm_resp(const unsigned char *keys,
const unsigned char *plaintext,
unsigned char *results);
CURLcode Curl_ntlm_core_mk_lm_hash(const char *password,
unsigned char *lmbuffer /* 21 bytes */);
CURLcode Curl_ntlm_core_mk_nt_hash(const char *password,
unsigned char *ntbuffer /* 21 bytes */);
#ifndef USE_WINDOWS_SSPI
CURLcode Curl_hmac_md5(const unsigned char *key, unsigned int keylen,
const unsigned char *data, unsigned int datalen,
unsigned char *output);
CURLcode Curl_ntlm_core_mk_ntlmv2_hash(const char *user, size_t userlen,
const char *domain, size_t domlen,
unsigned char *ntlmhash,
unsigned char *ntlmv2hash);
CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash,
unsigned char *challenge_client,
struct ntlmdata *ntlm,
unsigned char **ntresp,
unsigned int *ntresp_len);
CURLcode Curl_ntlm_core_mk_lmv2_resp(unsigned char *ntlmv2hash,
unsigned char *challenge_client,
unsigned char *challenge_server,
unsigned char *lmresp);
#endif /* !USE_WINDOWS_SSPI */
#endif /* USE_CURL_NTLM_CORE */
#endif /* HEADER_CURL_NTLM_CORE_H */
+37
View File
@@ -0,0 +1,37 @@
#ifndef HEADER_CURL_PRINTF_H
#define HEADER_CURL_PRINTF_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#define MERR_OK 0
#define MERR_MEM 1
#define MERR_TOO_LARGE 2
/* Lower-case digits. */
extern const unsigned char Curl_ldigits[];
/* Upper-case digits. */
extern const unsigned char Curl_udigits[];
#endif /* HEADER_CURL_PRINTF_H */
+94
View File
@@ -0,0 +1,94 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include <curl/curl.h>
#include "curl_range.h"
#include "sendf.h"
#include "curlx/strparse.h"
/* Only include this function if one or more of FTP, FILE are enabled. */
#if !defined(CURL_DISABLE_FTP) || !defined(CURL_DISABLE_FILE)
/*
Check if this is a range download, and if so, set the internal variables
properly.
*/
CURLcode Curl_range(struct Curl_easy *data)
{
if(data->state.use_range && data->state.range) {
curl_off_t from, to;
bool first_num = TRUE;
const char *p = data->state.range;
if(curlx_str_number(&p, &from, CURL_OFF_T_MAX))
first_num = FALSE;
if(curlx_str_single(&p, '-'))
/* no leading dash or after the first number is an error */
return CURLE_RANGE_ERROR;
if(curlx_str_number(&p, &to, CURL_OFF_T_MAX)) {
/* no second number */
/* X - */
data->state.resume_from = from;
DEBUGF(infof(data, "RANGE %" FMT_OFF_T " to end of file", from));
}
else if(!first_num) {
/* -Y */
if(!to)
/* "-0" is just wrong */
return CURLE_RANGE_ERROR;
data->req.maxdownload = to;
data->state.resume_from = -to;
DEBUGF(infof(data, "RANGE the last %" FMT_OFF_T " bytes", to));
}
else {
/* X-Y */
curl_off_t totalsize;
/* Ensure the range is sensible - to should follow from. */
if(from > to)
return CURLE_RANGE_ERROR;
totalsize = to - from;
if(totalsize == CURL_OFF_T_MAX)
return CURLE_RANGE_ERROR;
data->req.maxdownload = totalsize + 1; /* include last byte */
data->state.resume_from = from;
DEBUGF(infof(data, "RANGE from %" FMT_OFF_T
" getting %" FMT_OFF_T " bytes",
from, data->req.maxdownload));
}
DEBUGF(infof(data, "range-download from %" FMT_OFF_T
" to %" FMT_OFF_T ", totally %" FMT_OFF_T " bytes",
from, to, data->req.maxdownload));
}
else
data->req.maxdownload = -1;
return CURLE_OK;
}
#endif
+31
View File
@@ -0,0 +1,31 @@
#ifndef HEADER_CURL_RANGE_H
#define HEADER_CURL_RANGE_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include "urldata.h"
CURLcode Curl_range(struct Curl_easy *data);
#endif /* HEADER_CURL_RANGE_H */
+393
View File
@@ -0,0 +1,393 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
* Copyright (C) Howard Chu, <hyc@highlandsun.com>
*
* 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_LIBRTMP
#include "curl_rtmp.h"
#include "urldata.h"
#include "url.h"
#include "curlx/nonblock.h" /* for curlx_nonblock */
#include "progress.h" /* for Curl_pgrsSetUploadSize */
#include "transfer.h"
#include "curlx/warnless.h"
#include <curl/curl.h>
#include <librtmp/rtmp.h>
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
#if defined(_WIN32) && !defined(USE_LWIPSOCK)
#define setsockopt(a,b,c,d,e) (setsockopt)(a,b,c,(const char *)d,(int)e)
#define SET_RCVTIMEO(tv,s) int tv = s*1000
#elif defined(LWIP_SO_SNDRCVTIMEO_NONSTANDARD)
#define SET_RCVTIMEO(tv,s) int tv = s*1000
#else
#define SET_RCVTIMEO(tv,s) struct timeval tv = {s,0}
#endif
#define DEF_BUFTIME (2*60*60*1000) /* 2 hours */
/* meta key for storing RTMP* at connection */
#define CURL_META_RTMP_CONN "meta:proto:rtmp:conn"
static CURLcode rtmp_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
static CURLcode rtmp_do(struct Curl_easy *data, bool *done);
static CURLcode rtmp_done(struct Curl_easy *data, CURLcode, bool premature);
static CURLcode rtmp_connect(struct Curl_easy *data, bool *done);
static CURLcode rtmp_disconnect(struct Curl_easy *data,
struct connectdata *conn, bool dead);
static Curl_recv rtmp_recv;
static Curl_send rtmp_send;
/*
* RTMP protocol handler.h, based on https://rtmpdump.mplayerhq.hu
*/
const struct Curl_handler Curl_handler_rtmp = {
"rtmp", /* scheme */
rtmp_setup_connection, /* setup_connection */
rtmp_do, /* do_it */
rtmp_done, /* done */
ZERO_NULL, /* do_more */
rtmp_connect, /* connect_it */
ZERO_NULL, /* connecting */
ZERO_NULL, /* doing */
ZERO_NULL, /* proto_pollset */
ZERO_NULL, /* doing_pollset */
ZERO_NULL, /* domore_pollset */
ZERO_NULL, /* perform_pollset */
rtmp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
ZERO_NULL, /* follow */
PORT_RTMP, /* defport */
CURLPROTO_RTMP, /* protocol */
CURLPROTO_RTMP, /* family */
PROTOPT_NONE /* flags */
};
const struct Curl_handler Curl_handler_rtmpt = {
"rtmpt", /* scheme */
rtmp_setup_connection, /* setup_connection */
rtmp_do, /* do_it */
rtmp_done, /* done */
ZERO_NULL, /* do_more */
rtmp_connect, /* connect_it */
ZERO_NULL, /* connecting */
ZERO_NULL, /* doing */
ZERO_NULL, /* proto_pollset */
ZERO_NULL, /* doing_pollset */
ZERO_NULL, /* domore_pollset */
ZERO_NULL, /* perform_pollset */
rtmp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
ZERO_NULL, /* follow */
PORT_RTMPT, /* defport */
CURLPROTO_RTMPT, /* protocol */
CURLPROTO_RTMPT, /* family */
PROTOPT_NONE /* flags */
};
const struct Curl_handler Curl_handler_rtmpe = {
"rtmpe", /* scheme */
rtmp_setup_connection, /* setup_connection */
rtmp_do, /* do_it */
rtmp_done, /* done */
ZERO_NULL, /* do_more */
rtmp_connect, /* connect_it */
ZERO_NULL, /* connecting */
ZERO_NULL, /* doing */
ZERO_NULL, /* proto_pollset */
ZERO_NULL, /* doing_pollset */
ZERO_NULL, /* domore_pollset */
ZERO_NULL, /* perform_pollset */
rtmp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
ZERO_NULL, /* follow */
PORT_RTMP, /* defport */
CURLPROTO_RTMPE, /* protocol */
CURLPROTO_RTMPE, /* family */
PROTOPT_NONE /* flags */
};
const struct Curl_handler Curl_handler_rtmpte = {
"rtmpte", /* scheme */
rtmp_setup_connection, /* setup_connection */
rtmp_do, /* do_it */
rtmp_done, /* done */
ZERO_NULL, /* do_more */
rtmp_connect, /* connect_it */
ZERO_NULL, /* connecting */
ZERO_NULL, /* doing */
ZERO_NULL, /* proto_pollset */
ZERO_NULL, /* doing_pollset */
ZERO_NULL, /* domore_pollset */
ZERO_NULL, /* perform_pollset */
rtmp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
ZERO_NULL, /* follow */
PORT_RTMPT, /* defport */
CURLPROTO_RTMPTE, /* protocol */
CURLPROTO_RTMPTE, /* family */
PROTOPT_NONE /* flags */
};
const struct Curl_handler Curl_handler_rtmps = {
"rtmps", /* scheme */
rtmp_setup_connection, /* setup_connection */
rtmp_do, /* do_it */
rtmp_done, /* done */
ZERO_NULL, /* do_more */
rtmp_connect, /* connect_it */
ZERO_NULL, /* connecting */
ZERO_NULL, /* doing */
ZERO_NULL, /* proto_pollset */
ZERO_NULL, /* doing_pollset */
ZERO_NULL, /* domore_pollset */
ZERO_NULL, /* perform_pollset */
rtmp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
ZERO_NULL, /* follow */
PORT_RTMPS, /* defport */
CURLPROTO_RTMPS, /* protocol */
CURLPROTO_RTMP, /* family */
PROTOPT_NONE /* flags */
};
const struct Curl_handler Curl_handler_rtmpts = {
"rtmpts", /* scheme */
rtmp_setup_connection, /* setup_connection */
rtmp_do, /* do_it */
rtmp_done, /* done */
ZERO_NULL, /* do_more */
rtmp_connect, /* connect_it */
ZERO_NULL, /* connecting */
ZERO_NULL, /* doing */
ZERO_NULL, /* proto_pollset */
ZERO_NULL, /* doing_pollset */
ZERO_NULL, /* domore_pollset */
ZERO_NULL, /* perform_pollset */
rtmp_disconnect, /* disconnect */
ZERO_NULL, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
ZERO_NULL, /* follow */
PORT_RTMPS, /* defport */
CURLPROTO_RTMPTS, /* protocol */
CURLPROTO_RTMPT, /* family */
PROTOPT_NONE /* flags */
};
static void rtmp_conn_dtor(void *key, size_t klen, void *entry)
{
RTMP *r = entry;
(void)key;
(void)klen;
RTMP_Close(r);
RTMP_Free(r);
}
static CURLcode rtmp_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
RTMP *r = RTMP_Alloc();
if(!r ||
Curl_conn_meta_set(conn, CURL_META_RTMP_CONN, r, rtmp_conn_dtor))
return CURLE_OUT_OF_MEMORY;
RTMP_Init(r);
RTMP_SetBufferMS(r, DEF_BUFTIME);
if(!RTMP_SetupURL(r, data->state.url)) {
RTMP_Free(r);
return CURLE_URL_MALFORMAT;
}
return CURLE_OK;
}
static CURLcode rtmp_connect(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
SET_RCVTIMEO(tv, 10);
if(!r)
return CURLE_FAILED_INIT;
r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET];
/* We have to know if it is a write before we send the
* connect request packet
*/
if(data->state.upload)
r->Link.protocol |= RTMP_FEATURE_WRITE;
/* For plain streams, use the buffer toggle trick to keep data flowing */
if(!(r->Link.lFlags & RTMP_LF_LIVE) &&
!(r->Link.protocol & RTMP_FEATURE_HTTP))
r->Link.lFlags |= RTMP_LF_BUFX;
(void)curlx_nonblock(r->m_sb.sb_socket, FALSE);
setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO,
(char *)&tv, sizeof(tv));
if(!RTMP_Connect1(r, NULL))
return CURLE_FAILED_INIT;
/* Clients must send a periodic BytesReceived report to the server */
r->m_bSendCounter = TRUE;
*done = TRUE;
conn->recv[FIRSTSOCKET] = rtmp_recv;
conn->send[FIRSTSOCKET] = rtmp_send;
return CURLE_OK;
}
static CURLcode rtmp_do(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
if(!r || !RTMP_ConnectStream(r, 0))
return CURLE_FAILED_INIT;
if(data->state.upload) {
Curl_pgrsSetUploadSize(data, data->state.infilesize);
Curl_xfer_setup_send(data, FIRSTSOCKET);
}
else
Curl_xfer_setup_recv(data, FIRSTSOCKET, -1);
*done = TRUE;
return CURLE_OK;
}
static CURLcode rtmp_done(struct Curl_easy *data, CURLcode status,
bool premature)
{
(void)data;
(void)status;
(void)premature;
return CURLE_OK;
}
static CURLcode rtmp_disconnect(struct Curl_easy *data,
struct connectdata *conn,
bool dead_connection)
{
RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
(void)data;
(void)dead_connection;
if(r)
Curl_conn_meta_remove(conn, CURL_META_RTMP_CONN);
return CURLE_OK;
}
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;
*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;
}
else
result = CURLE_RECV_ERROR;
}
else
*pnread = (size_t)nread;
return result;
}
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 nwritten;
(void)sockindex;
(void)eos;
*pnwritten = 0;
if(!r)
return CURLE_FAILED_INIT;
nwritten = RTMP_Write(r, (const char *)buf, curlx_uztosi(len));
if(nwritten < 0)
return CURLE_SEND_ERROR;
*pnwritten = (size_t)nwritten;
return CURLE_OK;
}
void Curl_rtmp_version(char *version, size_t len)
{
char suff[2];
if(RTMP_LIB_VERSION & 0xff) {
suff[0] = (RTMP_LIB_VERSION & 0xff) + 'a' - 1;
suff[1] = '\0';
}
else
suff[0] = '\0';
curl_msnprintf(version, len, "librtmp/%d.%d%s",
RTMP_LIB_VERSION >> 16, (RTMP_LIB_VERSION >> 8) & 0xff,
suff);
}
#endif /* USE_LIBRTMP */
+37
View File
@@ -0,0 +1,37 @@
#ifndef HEADER_CURL_RTMP_H
#define HEADER_CURL_RTMP_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Howard Chu, <hyc@highlandsun.com>
*
* 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
*
***************************************************************************/
#ifdef USE_LIBRTMP
extern const struct Curl_handler Curl_handler_rtmp;
extern const struct Curl_handler Curl_handler_rtmpt;
extern const struct Curl_handler Curl_handler_rtmpe;
extern const struct Curl_handler Curl_handler_rtmpte;
extern const struct Curl_handler Curl_handler_rtmps;
extern const struct Curl_handler Curl_handler_rtmpts;
void Curl_rtmp_version(char *version, size_t len);
#endif
#endif /* HEADER_CURL_RTMP_H */
+948
View File
@@ -0,0 +1,948 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
* RFC2195 CRAM-MD5 authentication
* RFC2617 Basic and Digest Access Authentication
* RFC2831 DIGEST-MD5 authentication
* RFC4422 Simple Authentication and Security Layer (SASL)
* RFC4616 PLAIN authentication
* RFC5802 SCRAM-SHA-1 authentication
* RFC7677 SCRAM-SHA-256 authentication
* RFC6749 OAuth 2.0 Authorization Framework
* RFC7628 A Set of SASL Mechanisms for OAuth
* Draft LOGIN SASL Mechanism <draft-murchison-sasl-login-00.txt>
*
***************************************************************************/
#include "curl_setup.h"
#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
!defined(CURL_DISABLE_POP3) || \
(!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP))
#include <curl/curl.h>
#include "urldata.h"
#include "curlx/base64.h"
#include "vauth/vauth.h"
#include "cfilters.h"
#include "vtls/vtls.h"
#include "curl_hmac.h"
#include "curl_sasl.h"
#include "curlx/warnless.h"
#include "sendf.h"
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
/* Supported mechanisms */
static const struct {
const char *name; /* Name */
size_t len; /* Name length */
unsigned short bit; /* Flag bit */
} mechtable[] = {
{ "LOGIN", 5, SASL_MECH_LOGIN },
{ "PLAIN", 5, SASL_MECH_PLAIN },
{ "CRAM-MD5", 8, SASL_MECH_CRAM_MD5 },
{ "DIGEST-MD5", 10, SASL_MECH_DIGEST_MD5 },
{ "GSSAPI", 6, SASL_MECH_GSSAPI },
{ "EXTERNAL", 8, SASL_MECH_EXTERNAL },
{ "NTLM", 4, SASL_MECH_NTLM },
{ "XOAUTH2", 7, SASL_MECH_XOAUTH2 },
{ "OAUTHBEARER", 11, SASL_MECH_OAUTHBEARER },
{ "SCRAM-SHA-1", 11, SASL_MECH_SCRAM_SHA_1 },
{ "SCRAM-SHA-256",13, SASL_MECH_SCRAM_SHA_256 },
{ ZERO_NULL, 0, 0 }
};
/*
* Curl_sasl_decode_mech()
*
* Convert a SASL mechanism name into a token.
*
* Parameters:
*
* ptr [in] - The mechanism string.
* maxlen [in] - Maximum mechanism string length.
* len [out] - If not NULL, effective name length.
*
* Returns the SASL mechanism token or 0 if no match.
*/
unsigned short Curl_sasl_decode_mech(const char *ptr, size_t maxlen,
size_t *len)
{
unsigned int i;
char c;
for(i = 0; mechtable[i].name; i++) {
if(maxlen >= mechtable[i].len &&
!memcmp(ptr, mechtable[i].name, mechtable[i].len)) {
if(len)
*len = mechtable[i].len;
if(maxlen == mechtable[i].len)
return mechtable[i].bit;
c = ptr[mechtable[i].len];
if(!ISUPPER(c) && !ISDIGIT(c) && c != '-' && c != '_')
return mechtable[i].bit;
}
}
return 0;
}
/*
* Curl_sasl_parse_url_auth_option()
*
* Parse the URL login options.
*/
CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
const char *value, size_t len)
{
CURLcode result = CURLE_OK;
size_t mechlen;
if(!len)
return CURLE_URL_MALFORMAT;
if(sasl->resetprefs) {
sasl->resetprefs = FALSE;
sasl->prefmech = SASL_AUTH_NONE;
}
if(!strncmp(value, "*", len))
sasl->prefmech = SASL_AUTH_DEFAULT;
else {
unsigned short mechbit = Curl_sasl_decode_mech(value, len, &mechlen);
if(mechbit && mechlen == len)
sasl->prefmech |= mechbit;
else
result = CURLE_URL_MALFORMAT;
}
return result;
}
/*
* Curl_sasl_init()
*
* Initializes the SASL structure.
*/
void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data,
const struct SASLproto *params)
{
unsigned long auth = data->set.httpauth;
sasl->params = params; /* Set protocol dependent parameters */
sasl->state = SASL_STOP; /* Not yet running */
sasl->curmech = NULL; /* No mechanism yet. */
sasl->authmechs = SASL_AUTH_NONE; /* No known authentication mechanism yet */
sasl->prefmech = params->defmechs; /* Default preferred mechanisms */
sasl->authused = SASL_AUTH_NONE; /* The authentication mechanism used */
sasl->resetprefs = TRUE; /* Reset prefmech upon AUTH parsing. */
sasl->mutual_auth = FALSE; /* No mutual authentication (GSSAPI only) */
sasl->force_ir = FALSE; /* Respect external option */
if(auth != CURLAUTH_BASIC) {
unsigned short mechs = SASL_AUTH_NONE;
/* If some usable http authentication options have been set, determine
new defaults from them. */
if(auth & CURLAUTH_BASIC)
mechs |= SASL_MECH_PLAIN | SASL_MECH_LOGIN;
if(auth & CURLAUTH_DIGEST)
mechs |= SASL_MECH_DIGEST_MD5;
if(auth & CURLAUTH_NTLM)
mechs |= SASL_MECH_NTLM;
if(auth & CURLAUTH_BEARER)
mechs |= SASL_MECH_OAUTHBEARER | SASL_MECH_XOAUTH2;
if(auth & CURLAUTH_GSSAPI)
mechs |= SASL_MECH_GSSAPI;
if(mechs != SASL_AUTH_NONE)
sasl->prefmech = mechs;
}
}
/*
* sasl_state()
*
* This is the ONLY way to change SASL state!
*/
static void sasl_state(struct SASL *sasl, struct Curl_easy *data,
saslstate newstate)
{
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
/* for debug purposes */
static const char * const names[]={
"STOP",
"PLAIN",
"LOGIN",
"LOGIN_PASSWD",
"EXTERNAL",
"CRAMMD5",
"DIGESTMD5",
"DIGESTMD5_RESP",
"NTLM",
"NTLM_TYPE2MSG",
"GSSAPI",
"GSSAPI_TOKEN",
"GSSAPI_NO_DATA",
"OAUTH2",
"OAUTH2_RESP",
"GSASL",
"CANCEL",
"FINAL",
/* LAST */
};
if(sasl->state != newstate)
infof(data, "SASL %p state change from %s to %s",
(void *)sasl, names[sasl->state], names[newstate]);
#else
(void)data;
#endif
sasl->state = newstate;
}
#if defined(USE_NTLM) || defined(USE_GSASL) || defined(USE_KERBEROS5) || \
!defined(CURL_DISABLE_DIGEST_AUTH)
/* Get the SASL server message and convert it to binary. */
static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data,
struct bufref *out)
{
CURLcode result = CURLE_OK;
result = sasl->params->getmessage(data, out);
if(!result && (sasl->params->flags & SASL_FLAG_BASE64)) {
unsigned char *msg;
size_t msglen;
const char *serverdata = (const char *) Curl_bufref_ptr(out);
if(!*serverdata || *serverdata == '=')
Curl_bufref_set(out, NULL, 0, NULL);
else {
result = curlx_base64_decode(serverdata, &msg, &msglen);
if(!result)
Curl_bufref_set(out, msg, msglen, curl_free);
}
}
return result;
}
#endif
/* Encode the outgoing SASL message. */
static CURLcode build_message(struct SASL *sasl, struct bufref *msg)
{
CURLcode result = CURLE_OK;
if(sasl->params->flags & SASL_FLAG_BASE64) {
if(!Curl_bufref_ptr(msg)) /* Empty message. */
Curl_bufref_set(msg, "", 0, NULL);
else if(!Curl_bufref_len(msg)) /* Explicit empty response. */
Curl_bufref_set(msg, "=", 1, NULL);
else {
char *base64;
size_t base64len;
result = curlx_base64_encode((const char *) Curl_bufref_ptr(msg),
Curl_bufref_len(msg), &base64, &base64len);
if(!result)
Curl_bufref_set(msg, base64, base64len, curl_free);
}
}
return result;
}
/*
* Curl_sasl_can_authenticate()
*
* Check if we have enough auth data and capabilities to authenticate.
*/
bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data)
{
/* Have credentials been provided? */
if(data->state.aptr.user)
return TRUE;
/* EXTERNAL can authenticate without a username and/or password */
if(sasl->authmechs & sasl->prefmech & SASL_MECH_EXTERNAL)
return TRUE;
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()
*
* Calculate the required login details for SASL authentication.
*/
CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
bool force_ir, saslprogress *progress)
{
struct sasl_ctx sctx;
sasl->force_ir = force_ir; /* Latch for future use */
sasl->authused = 0; /* No mechanism used yet */
*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(sasl_choose_external(data, &sctx) ||
#ifdef USE_KERBEROS5
sasl_choose_krb5(data, &sctx) ||
#endif
#ifdef USE_GSASL
sasl_choose_gsasl(data, &sctx) ||
#endif
#ifndef CURL_DISABLE_DIGEST_AUTH
sasl_choose_digest(data, &sctx) ||
#endif
#ifdef USE_NTLM
sasl_choose_ntlm(data, &sctx) ||
#endif
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(!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(sctx.mech) + Curl_bufref_len(&sctx.resp) >
sasl->params->maxirlen)
Curl_bufref_free(&sctx.resp);
if(!sctx.result)
sctx.result = sasl->params->sendauth(data, sctx.mech, &sctx.resp);
if(!sctx.result) {
*progress = SASL_INPROGRESS;
sasl_state(sasl, data, Curl_bufref_ptr(&sctx.resp) ?
sctx.state2 : sctx.state1);
}
}
Curl_bufref_free(&sctx.resp);
return sctx.result;
}
/*
* Curl_sasl_continue()
*
* Continue the authentication.
*/
CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
int code, saslprogress *progress)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
saslstate newstate = SASL_FINAL;
struct bufref resp;
const char *hostname;
int port;
#if defined(USE_KERBEROS5) || defined(USE_NTLM) || \
!defined(CURL_DISABLE_DIGEST_AUTH)
const char *service = data->set.str[STRING_SERVICE_NAME] ?
data->set.str[STRING_SERVICE_NAME] :
sasl->params->service;
#endif
const char *oauth_bearer = data->set.str[STRING_BEARER];
struct bufref serverdata;
Curl_conn_get_current_host(data, FIRSTSOCKET, &hostname, &port);
Curl_bufref_init(&serverdata);
Curl_bufref_init(&resp);
*progress = SASL_INPROGRESS;
if(sasl->state == SASL_FINAL) {
if(code != sasl->params->finalcode)
result = CURLE_LOGIN_DENIED;
*progress = SASL_DONE;
sasl_state(sasl, data, SASL_STOP);
return result;
}
if(sasl->state != SASL_CANCEL && sasl->state != SASL_OAUTH2_RESP &&
code != sasl->params->contcode) {
*progress = SASL_DONE;
sasl_state(sasl, data, SASL_STOP);
return CURLE_LOGIN_DENIED;
}
switch(sasl->state) {
case SASL_STOP:
*progress = SASL_DONE;
return result;
case SASL_PLAIN:
result = Curl_auth_create_plain_message(conn->sasl_authzid,
conn->user, conn->passwd, &resp);
break;
case SASL_LOGIN:
Curl_auth_create_login_message(conn->user, &resp);
newstate = SASL_LOGIN_PASSWD;
break;
case SASL_LOGIN_PASSWD:
Curl_auth_create_login_message(conn->passwd, &resp);
break;
case SASL_EXTERNAL:
Curl_auth_create_external_message(conn->user, &resp);
break;
#ifdef USE_GSASL
case SASL_GSASL:
result = get_server_message(sasl, data, &serverdata);
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;
#endif
#ifndef CURL_DISABLE_DIGEST_AUTH
case SASL_CRAMMD5:
result = get_server_message(sasl, data, &serverdata);
if(!result)
result = Curl_auth_create_cram_md5_message(&serverdata, conn->user,
conn->passwd, &resp);
break;
case SASL_DIGESTMD5:
result = get_server_message(sasl, data, &serverdata);
if(!result)
result = Curl_auth_create_digest_md5_message(data, &serverdata,
conn->user, conn->passwd,
service, &resp);
if(!result && (sasl->params->flags & SASL_FLAG_BASE64))
newstate = SASL_DIGESTMD5_RESP;
break;
case SASL_DIGESTMD5_RESP:
/* Keep response NULL to output an empty line. */
break;
#endif
#ifdef USE_NTLM
case SASL_NTLM: {
/* Create the type-1 message */
struct ntlmdata *ntlm = Curl_auth_ntlm_get(conn, FALSE);
result = !ntlm ? CURLE_OUT_OF_MEMORY :
Curl_auth_create_ntlm_type1_message(data,
conn->user, conn->passwd,
service, hostname,
ntlm, &resp);
newstate = SASL_NTLM_TYPE2MSG;
break;
}
case SASL_NTLM_TYPE2MSG: {
/* Decode the type-2 message */
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, ntlm);
if(!result)
result = Curl_auth_create_ntlm_type3_message(data, conn->user,
conn->passwd, ntlm,
&resp);
break;
}
#endif
#ifdef USE_KERBEROS5
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,
krb5, &resp);
newstate = SASL_GSSAPI_TOKEN;
break;
}
case SASL_GSSAPI_TOKEN:
result = get_server_message(sasl, data, &serverdata);
if(!result) {
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,
krb5, &resp);
newstate = SASL_GSSAPI_NO_DATA;
}
else
/* Decode the security challenge and create the response message */
result = Curl_auth_create_gssapi_security_message(data,
conn->sasl_authzid,
&serverdata,
krb5, &resp);
}
break;
case SASL_GSSAPI_NO_DATA:
/* Decode the security challenge and create the response message */
result = get_server_message(sasl, data, &serverdata);
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
case SASL_OAUTH2:
/* Create the authorization message */
if(sasl->authused == SASL_MECH_OAUTHBEARER) {
result = Curl_auth_create_oauth_bearer_message(conn->user,
hostname,
port,
oauth_bearer,
&resp);
/* Failures maybe sent by the server as continuations for OAUTHBEARER */
newstate = SASL_OAUTH2_RESP;
}
else
result = Curl_auth_create_xoauth_bearer_message(conn->user,
oauth_bearer,
&resp);
break;
case SASL_OAUTH2_RESP:
/* The continuation is optional so check the response code */
if(code == sasl->params->finalcode) {
/* Final response was received so we are done */
*progress = SASL_DONE;
sasl_state(sasl, data, SASL_STOP);
return result;
}
else if(code == sasl->params->contcode) {
/* Acknowledge the continuation by sending a 0x01 response. */
Curl_bufref_set(&resp, "\x01", 1, NULL);
break;
}
else {
*progress = SASL_DONE;
sasl_state(sasl, data, SASL_STOP);
return CURLE_LOGIN_DENIED;
}
case SASL_CANCEL:
/* Remove the offending mechanism from the supported list */
sasl->authmechs &= (unsigned short)~sasl->authused;
sasl->authused = SASL_AUTH_NONE;
sasl->curmech = NULL;
/* Start an alternative SASL authentication */
return Curl_sasl_start(sasl, data, sasl->force_ir, progress);
default:
failf(data, "Unsupported SASL authentication mechanism");
result = CURLE_UNSUPPORTED_PROTOCOL; /* Should not happen */
break;
}
Curl_bufref_free(&serverdata);
switch(result) {
case CURLE_BAD_CONTENT_ENCODING:
/* Cancel dialog */
result = sasl->params->cancelauth(data, sasl->curmech);
newstate = SASL_CANCEL;
break;
case CURLE_OK:
result = build_message(sasl, &resp);
if(!result)
result = sasl->params->contauth(data, sasl->curmech, &resp);
break;
default:
newstate = SASL_STOP; /* Stop on error */
*progress = SASL_DONE;
break;
}
Curl_bufref_free(&resp);
sasl_state(sasl, data, newstate);
return result;
}
#ifndef CURL_DISABLE_VERBOSE_STRINGS
static void sasl_unchosen(struct Curl_easy *data, unsigned short mech,
unsigned short enabledmechs,
bool built_in, bool platform,
const char *param_missing)
{
const char *mname = NULL;
size_t i;
if(!(enabledmechs & mech))
return;
for(i = 0; mechtable[i].name; ++i) {
if(mechtable[i].bit == mech) {
mname = mechtable[i].name;
break;
}
}
if(!mname) /* should not happen */
return;
if(!built_in)
infof(data, "SASL: %s not builtin", mname);
else if(!platform)
infof(data, "SASL: %s not supported by the platform/libraries", mname);
else {
if(param_missing)
infof(data, "SASL: %s is missing %s", mname, param_missing);
if(!data->state.aptr.user)
infof(data, "SASL: %s is missing username", mname);
}
}
#endif /* CURL_DISABLE_VERBOSE_STRINGS */
CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data)
{
#ifndef CURL_DISABLE_VERBOSE_STRINGS
#ifdef USE_KERBEROS5
#define CURL_SASL_KERBEROS5 TRUE
#else
#define CURL_SASL_KERBEROS5 FALSE
#endif
#ifdef USE_GSASL
#define CURL_SASL_GASL TRUE
#else
#define CURL_SASL_GASL FALSE
#endif
#ifdef CURL_DISABLE_DIGEST_AUTH
#define CURL_SASL_DIGEST TRUE
#else
#define CURL_SASL_DIGEST FALSE
#endif
#ifndef USE_NTLM
#define CURL_SASL_NTLM TRUE
#else
#define CURL_SASL_NTLM FALSE
#endif
/* Failing SASL authentication is a pain. Give a helping hand if
* we were unable to select an AUTH mechanism.
* `sasl->authmechs` are mechanisms offered by the peer
* `sasl->prefmech` are mechanisms preferred by us */
unsigned short enabledmechs = sasl->authmechs & sasl->prefmech;
if(!sasl->authmechs)
infof(data, "SASL: no auth mechanism was offered or recognized");
else if(!enabledmechs)
infof(data, "SASL: no overlap between offered and configured "
"auth mechanisms");
else {
infof(data, "SASL: no auth mechanism offered could be selected");
if((enabledmechs & SASL_MECH_EXTERNAL) && data->conn->passwd[0])
infof(data, "SASL: auth EXTERNAL not chosen with password");
sasl_unchosen(data, SASL_MECH_GSSAPI, enabledmechs,
CURL_SASL_KERBEROS5, Curl_auth_is_gssapi_supported(), NULL);
sasl_unchosen(data, SASL_MECH_SCRAM_SHA_256, enabledmechs,
CURL_SASL_GASL, FALSE, NULL);
sasl_unchosen(data, SASL_MECH_SCRAM_SHA_1, enabledmechs,
CURL_SASL_GASL, FALSE, NULL);
sasl_unchosen(data, SASL_MECH_DIGEST_MD5, enabledmechs,
CURL_SASL_DIGEST, Curl_auth_is_digest_supported(), NULL);
sasl_unchosen(data, SASL_MECH_CRAM_MD5, enabledmechs,
CURL_SASL_DIGEST, TRUE, NULL);
sasl_unchosen(data, SASL_MECH_NTLM, enabledmechs,
CURL_SASL_NTLM, Curl_auth_is_ntlm_supported(), NULL);
sasl_unchosen(data, SASL_MECH_OAUTHBEARER, enabledmechs, TRUE, TRUE,
data->set.str[STRING_BEARER] ?
NULL : "CURLOPT_XOAUTH2_BEARER");
sasl_unchosen(data, SASL_MECH_XOAUTH2, enabledmechs, TRUE, TRUE,
data->set.str[STRING_BEARER] ?
NULL : "CURLOPT_XOAUTH2_BEARER");
}
#endif /* CURL_DISABLE_VERBOSE_STRINGS */
(void)sasl;
(void)data;
return CURLE_LOGIN_DENIED;
}
#endif /* protocols are enabled that use SASL */
+163
View File
@@ -0,0 +1,163 @@
#ifndef HEADER_CURL_SASL_H
#define HEADER_CURL_SASL_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include <curl/curl.h>
#include "bufref.h"
struct Curl_easy;
struct connectdata;
/* Authentication mechanism flags */
#define SASL_MECH_LOGIN (1 << 0)
#define SASL_MECH_PLAIN (1 << 1)
#define SASL_MECH_CRAM_MD5 (1 << 2)
#define SASL_MECH_DIGEST_MD5 (1 << 3)
#define SASL_MECH_GSSAPI (1 << 4)
#define SASL_MECH_EXTERNAL (1 << 5)
#define SASL_MECH_NTLM (1 << 6)
#define SASL_MECH_XOAUTH2 (1 << 7)
#define SASL_MECH_OAUTHBEARER (1 << 8)
#define SASL_MECH_SCRAM_SHA_1 (1 << 9)
#define SASL_MECH_SCRAM_SHA_256 (1 << 10)
/* Authentication mechanism values */
#define SASL_AUTH_NONE 0
#define SASL_AUTH_ANY 0xffff
#define SASL_AUTH_DEFAULT (SASL_AUTH_ANY & ~SASL_MECH_EXTERNAL)
/* Authentication mechanism strings */
#define SASL_MECH_STRING_LOGIN "LOGIN"
#define SASL_MECH_STRING_PLAIN "PLAIN"
#define SASL_MECH_STRING_CRAM_MD5 "CRAM-MD5"
#define SASL_MECH_STRING_DIGEST_MD5 "DIGEST-MD5"
#define SASL_MECH_STRING_GSSAPI "GSSAPI"
#define SASL_MECH_STRING_EXTERNAL "EXTERNAL"
#define SASL_MECH_STRING_NTLM "NTLM"
#define SASL_MECH_STRING_XOAUTH2 "XOAUTH2"
#define SASL_MECH_STRING_OAUTHBEARER "OAUTHBEARER"
#define SASL_MECH_STRING_SCRAM_SHA_1 "SCRAM-SHA-1"
#define SASL_MECH_STRING_SCRAM_SHA_256 "SCRAM-SHA-256"
/* SASL flags */
#define SASL_FLAG_BASE64 0x0001 /* Messages are base64-encoded */
/* SASL machine states */
typedef enum {
SASL_STOP,
SASL_PLAIN,
SASL_LOGIN,
SASL_LOGIN_PASSWD,
SASL_EXTERNAL,
SASL_CRAMMD5,
SASL_DIGESTMD5,
SASL_DIGESTMD5_RESP,
SASL_NTLM,
SASL_NTLM_TYPE2MSG,
SASL_GSSAPI,
SASL_GSSAPI_TOKEN,
SASL_GSSAPI_NO_DATA,
SASL_OAUTH2,
SASL_OAUTH2_RESP,
SASL_GSASL,
SASL_CANCEL,
SASL_FINAL
} saslstate;
/* Progress indicator */
typedef enum {
SASL_IDLE,
SASL_INPROGRESS,
SASL_DONE
} saslprogress;
/* Protocol dependent SASL parameters */
struct SASLproto {
const char *service; /* The service name */
CURLcode (*sendauth)(struct Curl_easy *data, const char *mech,
const struct bufref *ir);
/* Send authentication command */
CURLcode (*contauth)(struct Curl_easy *data, const char *mech,
const struct bufref *contauth);
/* Send authentication continuation */
CURLcode (*cancelauth)(struct Curl_easy *data, const char *mech);
/* Cancel authentication. */
CURLcode (*getmessage)(struct Curl_easy *data, struct bufref *out);
/* Get SASL response message */
size_t maxirlen; /* Maximum initial response + mechanism length,
or zero if no max. This is normally the max
command length - other characters count.
This has to be zero for non-base64 protocols. */
int contcode; /* Code to receive when continuation is expected */
int finalcode; /* Code to receive upon authentication success */
unsigned short defmechs; /* Mechanisms enabled by default */
unsigned short flags; /* Configuration flags. */
};
/* Per-connection parameters */
struct SASL {
const struct SASLproto *params; /* Protocol dependent parameters */
saslstate state; /* Current machine state */
const char *curmech; /* Current mechanism id. */
unsigned short authmechs; /* Accepted authentication mechanisms */
unsigned short prefmech; /* Preferred authentication mechanism */
unsigned short authused; /* Auth mechanism used for the connection */
BIT(resetprefs); /* For URL auth option parsing. */
BIT(mutual_auth); /* Mutual authentication enabled (GSSAPI only) */
BIT(force_ir); /* Protocol always supports initial response */
};
/* This is used to test whether the line starts with the given mechanism */
#define sasl_mech_equal(line, wordlen, mech) \
(wordlen == (sizeof(mech) - 1) / sizeof(char) && \
!memcmp(line, mech, wordlen))
/* Convert a mechanism name to a token */
unsigned short Curl_sasl_decode_mech(const char *ptr,
size_t maxlen, size_t *len);
/* Parse the URL login options */
CURLcode Curl_sasl_parse_url_auth_option(struct SASL *sasl,
const char *value, size_t len);
/* Initializes an SASL structure */
void Curl_sasl_init(struct SASL *sasl, struct Curl_easy *data,
const struct SASLproto *params);
/* Check if we have enough auth data and capabilities to authenticate */
bool Curl_sasl_can_authenticate(struct SASL *sasl, struct Curl_easy *data);
/* Calculate the required login details for SASL authentication */
CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
bool force_ir, saslprogress *progress);
/* Continue an SASL authentication */
CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
int code, saslprogress *progress);
CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data);
#endif /* HEADER_CURL_SASL_H */
File diff suppressed because it is too large Load Diff
+374
View File
@@ -0,0 +1,374 @@
#ifndef HEADER_CURL_SETUP_ONCE_H
#define HEADER_CURL_SETUP_ONCE_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
/*
* Inclusion of common header files.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#ifndef UNDER_CE
#include <errno.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#include <sys/stat.h>
#if !defined(_WIN32) || defined(__MINGW32__)
#include <sys/time.h>
#endif
#ifdef HAVE_IO_H
#include <io.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#if defined(HAVE_STDBOOL_H) && defined(HAVE_BOOL_T)
#include <stdbool.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
/* Macro to strip 'const' without triggering a compiler warning.
Use it for APIs that do not or cannot support the const qualifier. */
#ifdef HAVE_STDINT_H
# define CURL_UNCONST(p) ((void *)(uintptr_t)(const void *)(p))
#elif defined(_WIN32) /* for VS2008 */
# define CURL_UNCONST(p) ((void *)(ULONG_PTR)(const void *)(p))
#else
# define CURL_UNCONST(p) ((void *)(p)) /* Fall back to simple cast */
#endif
#ifdef USE_SCHANNEL
/* Must set this before <schannel.h> is included directly or indirectly by
another Windows header. */
# define SCHANNEL_USE_BLACKLISTS 1
#endif
#ifdef __hpux
# if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL)
# ifdef _APP32_64BIT_OFF_T
# define OLD_APP32_64BIT_OFF_T _APP32_64BIT_OFF_T
# undef _APP32_64BIT_OFF_T
# else
# undef OLD_APP32_64BIT_OFF_T
# endif
# endif
#endif
#ifndef _WIN32
#include <sys/socket.h>
#endif
#include "functypes.h"
#ifdef __hpux
# if !defined(_XOPEN_SOURCE_EXTENDED) || defined(_KERNEL)
# ifdef OLD_APP32_64BIT_OFF_T
# define _APP32_64BIT_OFF_T OLD_APP32_64BIT_OFF_T
# undef OLD_APP32_64BIT_OFF_T
# endif
# endif
#endif
/*
* Definition of timeval struct for platforms that do not have it.
*/
#ifndef HAVE_STRUCT_TIMEVAL
struct timeval {
long tv_sec;
long tv_usec;
};
#endif
/*
* If we have the MSG_NOSIGNAL define, make sure we use
* it as the fourth argument of function send()
*/
#ifdef HAVE_MSG_NOSIGNAL
#define SEND_4TH_ARG MSG_NOSIGNAL
#else
#define SEND_4TH_ARG 0
#endif
#ifdef __minix
/* Minix does not support recv on TCP sockets */
#define sread(x,y,z) (ssize_t)read((RECV_TYPE_ARG1)(x), \
(RECV_TYPE_ARG2)(y), \
(RECV_TYPE_ARG3)(z))
#elif defined(HAVE_RECV)
/*
* The definitions for the return type and arguments types
* of functions recv() and send() belong and come from the
* configuration file. Do not define them in any other place.
*
* HAVE_RECV is defined if you have a function named recv()
* which is used to read incoming data from sockets. If your
* function has another name then do not define HAVE_RECV.
*
* If HAVE_RECV is defined then RECV_TYPE_ARG1, RECV_TYPE_ARG2,
* RECV_TYPE_ARG3, RECV_TYPE_ARG4 and RECV_TYPE_RETV must also
* be defined.
*
* HAVE_SEND is defined if you have a function named send()
* which is used to write outgoing data on a connected socket.
* If yours has another name then do not define HAVE_SEND.
*
* If HAVE_SEND is defined then SEND_TYPE_ARG1, SEND_QUAL_ARG2,
* SEND_TYPE_ARG2, SEND_TYPE_ARG3, SEND_TYPE_ARG4 and
* SEND_TYPE_RETV must also be defined.
*/
#define sread(x,y,z) (ssize_t)recv((RECV_TYPE_ARG1)(x), \
(RECV_TYPE_ARG2)(y), \
(RECV_TYPE_ARG3)(z), \
(RECV_TYPE_ARG4)(0))
#else /* HAVE_RECV */
#ifndef sread
#error "Missing definition of macro sread!"
#endif
#endif /* HAVE_RECV */
#ifdef __minix
/* Minix does not support send on TCP sockets */
#define swrite(x,y,z) (ssize_t)write((SEND_TYPE_ARG1)(x), \
(SEND_TYPE_ARG2)CURL_UNCONST(y), \
(SEND_TYPE_ARG3)(z))
#elif defined(HAVE_SEND)
#define swrite(x,y,z) (ssize_t)send((SEND_TYPE_ARG1)(x), \
(SEND_QUAL_ARG2 SEND_TYPE_ARG2)CURL_UNCONST(y), \
(SEND_TYPE_ARG3)(z), \
(SEND_TYPE_ARG4)(SEND_4TH_ARG))
#else /* HAVE_SEND */
#ifndef swrite
#error "Missing definition of macro swrite!"
#endif
#endif /* HAVE_SEND */
/*
* Function-like macro definition used to close a socket.
*/
#ifdef HAVE_CLOSESOCKET
# define CURL_SCLOSE(x) closesocket((x))
#elif defined(HAVE_CLOSESOCKET_CAMEL)
# define CURL_SCLOSE(x) CloseSocket((x))
#elif defined(MSDOS) /* Watt-32 */
# define CURL_SCLOSE(x) close_s((x))
#elif defined(USE_LWIPSOCK)
# define CURL_SCLOSE(x) lwip_close((x))
#else
# define CURL_SCLOSE(x) close((x))
#endif
/*
* Stack-independent version of fcntl() on sockets:
*/
#ifdef USE_LWIPSOCK
# define sfcntl lwip_fcntl
#else
# define sfcntl fcntl
#endif
/*
* 'bool' stuff compatible with HP-UX headers.
*/
#if defined(__hpux) && !defined(HAVE_BOOL_T)
typedef int bool;
# define false 0
# define true 1
# define HAVE_BOOL_T
#endif
/*
* 'bool' exists on platforms with <stdbool.h>, i.e. C99 platforms.
* On non-C99 platforms there is no bool, so define an enum for that.
* On C99 platforms 'false' and 'true' also exist. Enum uses a
* global namespace though, so use bool_false and bool_true.
*/
#ifndef HAVE_BOOL_T
typedef enum {
bool_false = 0,
bool_true = 1
} bool;
/*
* Use a define to let 'true' and 'false' use those enums. There
* are currently no use of true and false in libcurl proper, but
* there are some in the examples. This will cater for any later
* code happening to use true and false.
*/
# define false bool_false
# define true bool_true
# define HAVE_BOOL_T
#endif
/* the type we use for storing a single boolean bit */
#ifdef _MSC_VER
typedef bool bit;
#define BIT(x) bool x
#else
typedef unsigned int bit;
#define BIT(x) bit x:1
#endif
/*
* Redefine TRUE and FALSE too, to catch current use. With this
* change, 'bool found = 1' will give a warning on MIPSPro, but
* 'bool found = TRUE' will not. Change tested on IRIX/MIPSPro,
* AIX 5.1/Xlc, Tru64 5.1/cc, w/make test too.
*/
#ifndef TRUE
#define TRUE true
#endif
#ifndef FALSE
#define FALSE false
#endif
#include "curl_ctype.h"
/*
* Macro used to include code only in debug builds.
*/
#ifdef DEBUGBUILD
#define DEBUGF(x) x
#else
#define DEBUGF(x) do { } while(0)
#endif
/*
* Macro used to include assertion code only in debug builds.
*/
#undef DEBUGASSERT
#ifdef DEBUGBUILD
#define DEBUGASSERT(x) assert(x)
#else
#define DEBUGASSERT(x) do { } while(0)
#endif
/*
* Macro SOCKERRNO / SET_SOCKERRNO() returns / sets the *socket-related* errno
* (or equivalent) on this platform to hide platform details to code using it.
*/
#ifdef USE_WINSOCK
#define SOCKERRNO ((int)WSAGetLastError())
#define SET_SOCKERRNO(x) (WSASetLastError((int)(x)))
#else
#define SOCKERRNO (errno)
#define SET_SOCKERRNO(x) (errno = (x))
#endif
/*
* Portable error number symbolic names defined to Winsock error codes.
*/
#ifdef USE_WINSOCK
#define SOCKEACCES WSAEACCES
#define SOCKEADDRINUSE WSAEADDRINUSE
#define SOCKEADDRNOTAVAIL WSAEADDRNOTAVAIL
#define SOCKEAFNOSUPPORT WSAEAFNOSUPPORT
#define SOCKEBADF WSAEBADF
#define SOCKECONNREFUSED WSAECONNREFUSED
#define SOCKECONNRESET WSAECONNRESET
#define SOCKEINPROGRESS WSAEINPROGRESS
#define SOCKEINTR WSAEINTR
#define SOCKEINVAL WSAEINVAL
#define SOCKEISCONN WSAEISCONN
#define SOCKEMSGSIZE WSAEMSGSIZE
#define SOCKENOMEM WSA_NOT_ENOUGH_MEMORY
#define SOCKETIMEDOUT WSAETIMEDOUT
#define SOCKEWOULDBLOCK WSAEWOULDBLOCK
#else
#define SOCKEACCES EACCES
#define SOCKEADDRINUSE EADDRINUSE
#define SOCKEADDRNOTAVAIL EADDRNOTAVAIL
#define SOCKEAFNOSUPPORT EAFNOSUPPORT
#define SOCKEBADF EBADF
#define SOCKECONNREFUSED ECONNREFUSED
#define SOCKECONNRESET ECONNRESET
#define SOCKEINPROGRESS EINPROGRESS
#define SOCKEINTR EINTR
#define SOCKEINVAL EINVAL
#define SOCKEISCONN EISCONN
#define SOCKEMSGSIZE EMSGSIZE
#define SOCKENOMEM ENOMEM
#ifdef ETIMEDOUT
#define SOCKETIMEDOUT ETIMEDOUT
#endif
#define SOCKEWOULDBLOCK EWOULDBLOCK
#endif
/*
* Macro argv_item_t hides platform details to code using it.
*/
#ifdef __VMS
#define argv_item_t __char_ptr32
#elif defined(_UNICODE) && !defined(UNDER_CE)
#define argv_item_t wchar_t *
#else
#define argv_item_t char *
#endif
/*
* We use this ZERO_NULL to avoid picky compiler warnings,
* when assigning a NULL pointer to a function pointer var.
*/
#define ZERO_NULL 0
#endif /* HEADER_CURL_SETUP_ONCE_H */
+45
View File
@@ -0,0 +1,45 @@
#ifndef HEADER_CURL_SHA256_H
#define HEADER_CURL_SHA256_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Florin Petriuc, <petriuc.florin@gmail.com>
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \
defined(USE_LIBSSH2) || defined(USE_SSL)
#include <curl/curl.h>
#include "curl_hmac.h"
extern const struct HMAC_params Curl_HMAC_SHA256;
#ifndef CURL_SHA256_DIGEST_LENGTH
#define CURL_SHA256_DIGEST_LENGTH 32 /* fixed size */
#endif
CURLcode Curl_sha256it(unsigned char *outbuffer, const unsigned char *input,
const size_t len);
#endif
#endif /* HEADER_CURL_SHA256_H */
+826
View File
@@ -0,0 +1,826 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Evgeny Grin (Karlson2k), <k2k@narod.ru>.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#if !defined(CURL_DISABLE_DIGEST_AUTH) && !defined(CURL_DISABLE_SHA512_256)
#include "curl_sha512_256.h"
#include "curlx/warnless.h"
/* The recommended order of the TLS backends:
* * OpenSSL
* * GnuTLS
* * wolfSSL
* * Schannel SSPI
* * mbedTLS
* * Rustls
* Skip the backend if it does not support the required algorithm */
#ifdef USE_OPENSSL
# include <openssl/opensslv.h>
# if (!defined(LIBRESSL_VERSION_NUMBER) && \
OPENSSL_VERSION_NUMBER >= 0x10101000L) || \
(defined(LIBRESSL_VERSION_NUMBER) && \
LIBRESSL_VERSION_NUMBER >= 0x3080000fL)
# include <openssl/opensslconf.h>
# if !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_SHA512)
# include <openssl/evp.h>
# define USE_OPENSSL_SHA512_256 1
# define HAS_SHA512_256_IMPLEMENTATION 1
# ifdef __NetBSD__
/* Some NetBSD versions has a bug in SHA-512/256.
* See https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039
* The problematic versions:
* - NetBSD before 9.4
* - NetBSD 9 all development versions (9.99.x)
* - NetBSD 10 development versions (10.99.x) before 10.99.11
* The bug was fixed in NetBSD 9.4 release, NetBSD 10.0 release,
* NetBSD 10.99.11 development.
* It is safe to apply the workaround even if the bug is not present, as
* the workaround just reduces performance slightly. */
# include <sys/param.h>
# if __NetBSD_Version__ < 904000000 || \
(__NetBSD_Version__ >= 999000000 && \
__NetBSD_Version__ < 1000000000) || \
(__NetBSD_Version__ >= 1099000000 && \
__NetBSD_Version__ < 1099001100)
# define NEED_NETBSD_SHA512_256_WORKAROUND 1
# include <string.h>
# endif
# endif
# endif
# endif
#endif /* USE_OPENSSL */
#if !defined(HAS_SHA512_256_IMPLEMENTATION) && defined(USE_GNUTLS)
# include <nettle/sha.h>
# ifdef SHA512_256_DIGEST_SIZE
# define USE_GNUTLS_SHA512_256 1
# endif
#endif /* ! HAS_SHA512_256_IMPLEMENTATION && USE_GNUTLS */
#ifdef USE_OPENSSL_SHA512_256
/* OpenSSL does not provide macros for SHA-512/256 sizes */
/**
* Size of the SHA-512/256 single processing block in bytes.
*/
#define CURL_SHA512_256_BLOCK_SIZE 128
/**
* Size of the SHA-512/256 resulting digest in bytes.
* This is the final digest size, not intermediate hash.
*/
#define CURL_SHA512_256_DIGEST_SIZE CURL_SHA512_256_DIGEST_LENGTH
/**
* Context type used for SHA-512/256 calculations
*/
typedef EVP_MD_CTX *Curl_sha512_256_ctx;
/**
* Initialise structure for SHA-512/256 calculation.
*
* @param context the calculation context
* @return CURLE_OK if succeed,
* error code otherwise
*/
static CURLcode Curl_sha512_256_init(void *context)
{
Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
*ctx = EVP_MD_CTX_create();
if(!*ctx)
return CURLE_OUT_OF_MEMORY;
if(EVP_DigestInit_ex(*ctx, EVP_sha512_256(), NULL)) {
/* Check whether the header and this file use the same numbers */
DEBUGASSERT(EVP_MD_CTX_size(*ctx) == CURL_SHA512_256_DIGEST_SIZE);
/* Check whether the block size is correct */
DEBUGASSERT(EVP_MD_CTX_block_size(*ctx) == CURL_SHA512_256_BLOCK_SIZE);
return CURLE_OK; /* Success */
}
/* Cleanup */
EVP_MD_CTX_destroy(*ctx);
return CURLE_FAILED_INIT;
}
/**
* Process portion of bytes.
*
* @param context the calculation context
* @param data bytes to add to hash
* @return CURLE_OK if succeed,
* error code otherwise
*/
static CURLcode Curl_sha512_256_update(void *context,
const unsigned char *data,
size_t length)
{
Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
if(!EVP_DigestUpdate(*ctx, data, length))
return CURLE_SSL_CIPHER;
return CURLE_OK;
}
/**
* Finalise SHA-512/256 calculation, return digest.
*
* @param context the calculation context
* @param[out] digest set to the hash, must be #CURL_SHA512_256_DIGEST_SIZE
# bytes
* @return CURLE_OK if succeed,
* error code otherwise
*/
static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context)
{
CURLcode ret;
Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
#ifdef NEED_NETBSD_SHA512_256_WORKAROUND
/* Use a larger buffer to work around a bug in NetBSD:
https://gnats.netbsd.org/cgi-bin/query-pr-single.pl?number=58039 */
unsigned char tmp_digest[CURL_SHA512_256_DIGEST_SIZE * 2];
ret = EVP_DigestFinal_ex(*ctx,
tmp_digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER;
if(ret == CURLE_OK)
memcpy(digest, tmp_digest, CURL_SHA512_256_DIGEST_SIZE);
explicit_memset(tmp_digest, 0, sizeof(tmp_digest));
#else /* ! NEED_NETBSD_SHA512_256_WORKAROUND */
ret = EVP_DigestFinal_ex(*ctx, digest, NULL) ? CURLE_OK : CURLE_SSL_CIPHER;
#endif /* ! NEED_NETBSD_SHA512_256_WORKAROUND */
EVP_MD_CTX_destroy(*ctx);
*ctx = NULL;
return ret;
}
#elif defined(USE_GNUTLS_SHA512_256)
#define CURL_SHA512_256_BLOCK_SIZE SHA512_256_BLOCK_SIZE
#define CURL_SHA512_256_DIGEST_SIZE SHA512_256_DIGEST_SIZE
/**
* Context type used for SHA-512/256 calculations
*/
typedef struct sha512_256_ctx Curl_sha512_256_ctx;
/**
* Initialise structure for SHA-512/256 calculation.
*
* @param context the calculation context
* @return always CURLE_OK
*/
static CURLcode Curl_sha512_256_init(void *context)
{
Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
/* Check whether the header and this file use the same numbers */
DEBUGASSERT(CURL_SHA512_256_DIGEST_LENGTH == CURL_SHA512_256_DIGEST_SIZE);
sha512_256_init(ctx);
return CURLE_OK;
}
/**
* Process portion of bytes.
*
* @param context the calculation context
* @param data bytes to add to hash
* @param length number of bytes in @a data
* @return always CURLE_OK
*/
static CURLcode Curl_sha512_256_update(void *context,
const unsigned char *data,
size_t length)
{
Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
DEBUGASSERT((data != NULL) || (length == 0));
sha512_256_update(ctx, length, (const uint8_t *)data);
return CURLE_OK;
}
/**
* Finalise SHA-512/256 calculation, return digest.
*
* @param context the calculation context
* @param[out] digest set to the hash, must be #CURL_SHA512_256_DIGEST_SIZE
# bytes
* @return always CURLE_OK
*/
static CURLcode Curl_sha512_256_finish(unsigned char *digest,
void *context)
{
Curl_sha512_256_ctx *const ctx = (Curl_sha512_256_ctx *)context;
sha512_256_digest(ctx,
(size_t)CURL_SHA512_256_DIGEST_SIZE, (uint8_t *)digest);
return CURLE_OK;
}
#else /* No system or TLS backend SHA-512/256 implementation available */
/* ** This implementation of SHA-512/256 hash calculation was originally ** *
* ** written by Evgeny Grin (Karlson2k) for GNU libmicrohttpd. ** *
* ** The author ported the code to libcurl. The ported code is provided ** *
* ** under curl license. ** *
* ** This is a minimal version with minimal optimizations. Performance ** *
* ** can be significantly improved. Big-endian store and load macros ** *
* ** are obvious targets for optimization. ** */
#ifdef __GNUC__
# if defined(__has_attribute) && defined(__STDC_VERSION__)
# if __has_attribute(always_inline) && __STDC_VERSION__ >= 199901
# define CURL_FORCEINLINE CURL_INLINE __attribute__((always_inline))
# endif
# endif
#endif
#if !defined(CURL_FORCEINLINE) && \
defined(_MSC_VER) && !defined(__GNUC__) && !defined(__clang__)
#define CURL_FORCEINLINE __forceinline
#endif
/* Assume that 'CURL_INLINE' keyword works or the
* macro was already defined correctly. */
#ifndef CURL_FORCEINLINE
#define CURL_FORCEINLINE CURL_INLINE
#endif
/* Bits manipulation macros and functions.
Can be moved to other headers to reuse. */
#define CURL_GET_64BIT_BE(ptr) \
( ((curl_uint64_t)(((const unsigned char*)(ptr))[0]) << 56) | \
((curl_uint64_t)(((const unsigned char*)(ptr))[1]) << 48) | \
((curl_uint64_t)(((const unsigned char*)(ptr))[2]) << 40) | \
((curl_uint64_t)(((const unsigned char*)(ptr))[3]) << 32) | \
((curl_uint64_t)(((const unsigned char*)(ptr))[4]) << 24) | \
((curl_uint64_t)(((const unsigned char*)(ptr))[5]) << 16) | \
((curl_uint64_t)(((const unsigned char*)(ptr))[6]) << 8) | \
(curl_uint64_t)(((const unsigned char*)(ptr))[7]) )
#define CURL_PUT_64BIT_BE(ptr,val) do { \
((unsigned char*)(ptr))[7]=(unsigned char)((curl_uint64_t)(val)); \
((unsigned char*)(ptr))[6]=(unsigned char)(((curl_uint64_t)(val)) >> 8); \
((unsigned char*)(ptr))[5]=(unsigned char)(((curl_uint64_t)(val)) >> 16); \
((unsigned char*)(ptr))[4]=(unsigned char)(((curl_uint64_t)(val)) >> 24); \
((unsigned char*)(ptr))[3]=(unsigned char)(((curl_uint64_t)(val)) >> 32); \
((unsigned char*)(ptr))[2]=(unsigned char)(((curl_uint64_t)(val)) >> 40); \
((unsigned char*)(ptr))[1]=(unsigned char)(((curl_uint64_t)(val)) >> 48); \
((unsigned char*)(ptr))[0]=(unsigned char)(((curl_uint64_t)(val)) >> 56); \
} while(0)
/* Defined as a function. The macro version may duplicate the binary code
* size as each argument is used twice, so if any calculation is used
* as an argument, the calculation could be done twice. */
static CURL_FORCEINLINE curl_uint64_t Curl_rotr64(curl_uint64_t value,
unsigned int bits)
{
bits %= 64;
if(bits == 0)
return value;
/* Defined in a form which modern compiler could optimize. */
return (value >> bits) | (value << (64 - bits));
}
/* SHA-512/256 specific data */
/**
* Number of bits in a single SHA-512/256 word.
*/
#define SHA512_256_WORD_SIZE_BITS 64
/**
* Number of bytes in a single SHA-512/256 word.
*/
#define SHA512_256_BYTES_IN_WORD (SHA512_256_WORD_SIZE_BITS / 8)
/**
* Hash is kept internally as 8 64-bit words.
* This is the intermediate hash size, used during computing the final digest.
*/
#define SHA512_256_HASH_SIZE_WORDS 8
/**
* Size of the SHA-512/256 resulting digest in words.
* This is the final digest size, not intermediate hash.
*/
#define SHA512_256_DIGEST_SIZE_WORDS (SHA512_256_HASH_SIZE_WORDS / 2)
/**
* Size of the SHA-512/256 resulting digest in bytes
* This is the final digest size, not intermediate hash.
*/
#define CURL_SHA512_256_DIGEST_SIZE \
(SHA512_256_DIGEST_SIZE_WORDS * SHA512_256_BYTES_IN_WORD)
/**
* Size of the SHA-512/256 single processing block in bits.
*/
#define SHA512_256_BLOCK_SIZE_BITS 1024
/**
* Size of the SHA-512/256 single processing block in bytes.
*/
#define CURL_SHA512_256_BLOCK_SIZE (SHA512_256_BLOCK_SIZE_BITS / 8)
/**
* Size of the SHA-512/256 single processing block in words.
*/
#define SHA512_256_BLOCK_SIZE_WORDS \
(SHA512_256_BLOCK_SIZE_BITS / SHA512_256_WORD_SIZE_BITS)
/**
* SHA-512/256 calculation context
*/
struct Curl_sha512_256ctx {
/**
* Intermediate hash value. The variable is properly aligned. Smart
* compilers may automatically use fast load/store instruction for big
* endian data on little endian machine.
*/
curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS];
/**
* SHA-512/256 input data buffer. The buffer is properly aligned. Smart
* compilers may automatically use fast load/store instruction for big
* endian data on little endian machine.
*/
curl_uint64_t buffer[SHA512_256_BLOCK_SIZE_WORDS];
/**
* The number of bytes, lower part
*/
curl_uint64_t count;
/**
* The number of bits, high part. Unlike lower part, this counts the number
* of bits, not bytes.
*/
curl_uint64_t count_bits_hi;
};
/**
* Context type used for SHA-512/256 calculations
*/
typedef struct Curl_sha512_256ctx Curl_sha512_256_ctx;
/**
* Initialise structure for SHA-512/256 calculation.
*
* @param context the calculation context
* @return always CURLE_OK
*/
static CURLcode Curl_sha512_256_init(void *context)
{
struct Curl_sha512_256ctx *const ctx = (struct Curl_sha512_256ctx *)context;
/* Check whether the header and this file use the same numbers */
DEBUGASSERT(CURL_SHA512_256_DIGEST_LENGTH == CURL_SHA512_256_DIGEST_SIZE);
DEBUGASSERT(sizeof(curl_uint64_t) == 8);
/* Initial hash values, see FIPS PUB 180-4 section 5.3.6.2 */
/* Values generated by "IV Generation Function" as described in
* section 5.3.6 */
ctx->H[0] = CURL_UINT64_C(0x22312194FC2BF72C);
ctx->H[1] = CURL_UINT64_C(0x9F555FA3C84C64C2);
ctx->H[2] = CURL_UINT64_C(0x2393B86B6F53B151);
ctx->H[3] = CURL_UINT64_C(0x963877195940EABD);
ctx->H[4] = CURL_UINT64_C(0x96283EE2A88EFFE3);
ctx->H[5] = CURL_UINT64_C(0xBE5E1E2553863992);
ctx->H[6] = CURL_UINT64_C(0x2B0199FC2C85B8AA);
ctx->H[7] = CURL_UINT64_C(0x0EB72DDC81C52CA2);
/* Initialise number of bytes and high part of number of bits. */
ctx->count = CURL_UINT64_C(0);
ctx->count_bits_hi = CURL_UINT64_C(0);
return CURLE_OK;
}
/**
* Base of the SHA-512/256 transformation.
* Gets a full 128 bytes block of data and updates hash values;
* @param H hash values
* @param data the data buffer with #CURL_SHA512_256_BLOCK_SIZE bytes block
*/
static
void Curl_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS],
const void *data)
{
/* Working variables,
see FIPS PUB 180-4 section 6.7, 6.4. */
curl_uint64_t a = H[0];
curl_uint64_t b = H[1];
curl_uint64_t c = H[2];
curl_uint64_t d = H[3];
curl_uint64_t e = H[4];
curl_uint64_t f = H[5];
curl_uint64_t g = H[6];
curl_uint64_t h = H[7];
/* Data buffer, used as a cyclic buffer.
See FIPS PUB 180-4 section 5.2.2, 6.7, 6.4. */
curl_uint64_t W[16];
/* 'Ch' and 'Maj' macro functions are defined with widely-used optimization.
See FIPS PUB 180-4 formulae 4.8, 4.9. */
#define Sha512_Ch(x,y,z) ( (z) ^ ((x) & ((y) ^ (z))) )
#define Sha512_Maj(x,y,z) ( ((x) & (y)) ^ ((z) & ((x) ^ (y))) )
/* Four 'Sigma' macro functions.
See FIPS PUB 180-4 formulae 4.10, 4.11, 4.12, 4.13. */
#define SIG0(x) \
( Curl_rotr64((x), 28) ^ Curl_rotr64((x), 34) ^ Curl_rotr64((x), 39) )
#define SIG1(x) \
( Curl_rotr64((x), 14) ^ Curl_rotr64((x), 18) ^ Curl_rotr64((x), 41) )
#define sig0(x) \
( Curl_rotr64((x), 1) ^ Curl_rotr64((x), 8) ^ ((x) >> 7) )
#define sig1(x) \
( Curl_rotr64((x), 19) ^ Curl_rotr64((x), 61) ^ ((x) >> 6) )
if(1) {
unsigned int t;
/* K constants array.
See FIPS PUB 180-4 section 4.2.3 for K values. */
static const curl_uint64_t K[80] = {
CURL_UINT64_C(0x428a2f98d728ae22), CURL_UINT64_C(0x7137449123ef65cd),
CURL_UINT64_C(0xb5c0fbcfec4d3b2f), CURL_UINT64_C(0xe9b5dba58189dbbc),
CURL_UINT64_C(0x3956c25bf348b538), CURL_UINT64_C(0x59f111f1b605d019),
CURL_UINT64_C(0x923f82a4af194f9b), CURL_UINT64_C(0xab1c5ed5da6d8118),
CURL_UINT64_C(0xd807aa98a3030242), CURL_UINT64_C(0x12835b0145706fbe),
CURL_UINT64_C(0x243185be4ee4b28c), CURL_UINT64_C(0x550c7dc3d5ffb4e2),
CURL_UINT64_C(0x72be5d74f27b896f), CURL_UINT64_C(0x80deb1fe3b1696b1),
CURL_UINT64_C(0x9bdc06a725c71235), CURL_UINT64_C(0xc19bf174cf692694),
CURL_UINT64_C(0xe49b69c19ef14ad2), CURL_UINT64_C(0xefbe4786384f25e3),
CURL_UINT64_C(0x0fc19dc68b8cd5b5), CURL_UINT64_C(0x240ca1cc77ac9c65),
CURL_UINT64_C(0x2de92c6f592b0275), CURL_UINT64_C(0x4a7484aa6ea6e483),
CURL_UINT64_C(0x5cb0a9dcbd41fbd4), CURL_UINT64_C(0x76f988da831153b5),
CURL_UINT64_C(0x983e5152ee66dfab), CURL_UINT64_C(0xa831c66d2db43210),
CURL_UINT64_C(0xb00327c898fb213f), CURL_UINT64_C(0xbf597fc7beef0ee4),
CURL_UINT64_C(0xc6e00bf33da88fc2), CURL_UINT64_C(0xd5a79147930aa725),
CURL_UINT64_C(0x06ca6351e003826f), CURL_UINT64_C(0x142929670a0e6e70),
CURL_UINT64_C(0x27b70a8546d22ffc), CURL_UINT64_C(0x2e1b21385c26c926),
CURL_UINT64_C(0x4d2c6dfc5ac42aed), CURL_UINT64_C(0x53380d139d95b3df),
CURL_UINT64_C(0x650a73548baf63de), CURL_UINT64_C(0x766a0abb3c77b2a8),
CURL_UINT64_C(0x81c2c92e47edaee6), CURL_UINT64_C(0x92722c851482353b),
CURL_UINT64_C(0xa2bfe8a14cf10364), CURL_UINT64_C(0xa81a664bbc423001),
CURL_UINT64_C(0xc24b8b70d0f89791), CURL_UINT64_C(0xc76c51a30654be30),
CURL_UINT64_C(0xd192e819d6ef5218), CURL_UINT64_C(0xd69906245565a910),
CURL_UINT64_C(0xf40e35855771202a), CURL_UINT64_C(0x106aa07032bbd1b8),
CURL_UINT64_C(0x19a4c116b8d2d0c8), CURL_UINT64_C(0x1e376c085141ab53),
CURL_UINT64_C(0x2748774cdf8eeb99), CURL_UINT64_C(0x34b0bcb5e19b48a8),
CURL_UINT64_C(0x391c0cb3c5c95a63), CURL_UINT64_C(0x4ed8aa4ae3418acb),
CURL_UINT64_C(0x5b9cca4f7763e373), CURL_UINT64_C(0x682e6ff3d6b2b8a3),
CURL_UINT64_C(0x748f82ee5defb2fc), CURL_UINT64_C(0x78a5636f43172f60),
CURL_UINT64_C(0x84c87814a1f0ab72), CURL_UINT64_C(0x8cc702081a6439ec),
CURL_UINT64_C(0x90befffa23631e28), CURL_UINT64_C(0xa4506cebde82bde9),
CURL_UINT64_C(0xbef9a3f7b2c67915), CURL_UINT64_C(0xc67178f2e372532b),
CURL_UINT64_C(0xca273eceea26619c), CURL_UINT64_C(0xd186b8c721c0c207),
CURL_UINT64_C(0xeada7dd6cde0eb1e), CURL_UINT64_C(0xf57d4f7fee6ed178),
CURL_UINT64_C(0x06f067aa72176fba), CURL_UINT64_C(0x0a637dc5a2c898a6),
CURL_UINT64_C(0x113f9804bef90dae), CURL_UINT64_C(0x1b710b35131c471b),
CURL_UINT64_C(0x28db77f523047d84), CURL_UINT64_C(0x32caab7b40c72493),
CURL_UINT64_C(0x3c9ebe0a15c9bebc), CURL_UINT64_C(0x431d67c49c100d4c),
CURL_UINT64_C(0x4cc5d4becb3e42b6), CURL_UINT64_C(0x597f299cfc657e2a),
CURL_UINT64_C(0x5fcb6fab3ad6faec), CURL_UINT64_C(0x6c44198c4a475817)
};
/* One step of SHA-512/256 computation,
see FIPS PUB 180-4 section 6.4.2 step 3.
* Note: this macro updates working variables in-place, without rotation.
* Note: the first (vH += SIG1(vE) + Ch(vE,vF,vG) + kt + wt) equals T1 in
FIPS PUB 180-4 section 6.4.2 step 3.
the second (vH += SIG0(vA) + Maj(vE,vF,vC) equals T1 + T2 in
FIPS PUB 180-4 section 6.4.2 step 3.
* Note: 'wt' must be used exactly one time in this macro as macro for
'wt' calculation may change other data as well every time when
used. */
#define SHA2STEP64(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \
(vD) += ((vH) += SIG1((vE)) + Sha512_Ch((vE),(vF),(vG)) + (kt) + (wt)); \
(vH) += SIG0((vA)) + Sha512_Maj((vA),(vB),(vC)); } while (0)
/* One step of SHA-512/256 computation with working variables rotation,
see FIPS PUB 180-4 section 6.4.2 step 3. This macro version reassigns
all working variables on each step. */
#define SHA2STEP64RV(vA,vB,vC,vD,vE,vF,vG,vH,kt,wt) do { \
curl_uint64_t tmp_h_ = (vH); \
SHA2STEP64((vA),(vB),(vC),(vD),(vE),(vF),(vG),tmp_h_,(kt),(wt)); \
(vH) = (vG); \
(vG) = (vF); \
(vF) = (vE); \
(vE) = (vD); \
(vD) = (vC); \
(vC) = (vB); \
(vB) = (vA); \
(vA) = tmp_h_; } while(0)
/* Get value of W(t) from input data buffer for 0 <= t <= 15,
See FIPS PUB 180-4 section 6.2.
Input data must be read in big-endian bytes order,
see FIPS PUB 180-4 section 3.1.2. */
#define SHA512_GET_W_FROM_DATA(buf,t) \
CURL_GET_64BIT_BE( \
((const unsigned char*) (buf)) + (t) * SHA512_256_BYTES_IN_WORD)
/* During first 16 steps, before making any calculation on each step, the
W element is read from the input data buffer as a big-endian value and
stored in the array of W elements. */
for(t = 0; t < 16; ++t) {
SHA2STEP64RV(a, b, c, d, e, f, g, h, K[t], \
W[t] = SHA512_GET_W_FROM_DATA(data, t));
}
/* 'W' generation and assignment for 16 <= t <= 79.
See FIPS PUB 180-4 section 6.4.2.
As only the last 16 'W' are used in calculations, it is possible to
use 16 elements array of W as a cyclic buffer.
Note: ((t-16) & 15) have same value as (t & 15) */
#define Wgen(w,t) \
(curl_uint64_t)( (w)[(t - 16) & 15] + sig1((w)[((t) - 2) & 15]) \
+ (w)[((t) - 7) & 15] + sig0((w)[((t) - 15) & 15]) )
/* During the last 64 steps, before making any calculation on each step,
current W element is generated from other W elements of the cyclic
buffer and the generated value is stored back in the cyclic buffer. */
for(t = 16; t < 80; ++t) {
SHA2STEP64RV(a, b, c, d, e, f, g, h, K[t], \
W[t & 15] = Wgen(W, t));
}
}
/* Compute and store the intermediate hash.
See FIPS PUB 180-4 section 6.4.2 step 4. */
H[0] += a;
H[1] += b;
H[2] += c;
H[3] += d;
H[4] += e;
H[5] += f;
H[6] += g;
H[7] += h;
}
/**
* Process portion of bytes.
*
* @param context the calculation context
* @param data bytes to add to hash
* @param length number of bytes in @a data
* @return always CURLE_OK
*/
static CURLcode Curl_sha512_256_update(void *context,
const unsigned char *data,
size_t length)
{
unsigned int bytes_have; /**< Number of bytes in the context buffer */
struct Curl_sha512_256ctx *const ctx = (struct Curl_sha512_256ctx *)context;
/* the void pointer here is required to mute Intel compiler warning */
void *const ctx_buf = ctx->buffer;
DEBUGASSERT((data != NULL) || (length == 0));
if(length == 0)
return CURLE_OK; /* Shortcut, do nothing */
/* Note: (count & (CURL_SHA512_256_BLOCK_SIZE-1))
equals (count % CURL_SHA512_256_BLOCK_SIZE) for this block size. */
bytes_have = (unsigned int) (ctx->count & (CURL_SHA512_256_BLOCK_SIZE - 1));
ctx->count += length;
if(length > ctx->count)
ctx->count_bits_hi += 1U << 3; /* Value wrap */
ctx->count_bits_hi += ctx->count >> 61;
ctx->count &= CURL_UINT64_C(0x1FFFFFFFFFFFFFFF);
if(bytes_have) {
unsigned int bytes_left = CURL_SHA512_256_BLOCK_SIZE - bytes_have;
if(length >= bytes_left) {
/* Combine new data with data in the buffer and process the full
block. */
memcpy(((unsigned char *) ctx_buf) + bytes_have,
data,
bytes_left);
data += bytes_left;
length -= bytes_left;
Curl_sha512_256_transform(ctx->H, ctx->buffer);
bytes_have = 0;
}
}
while(CURL_SHA512_256_BLOCK_SIZE <= length) {
/* Process any full blocks of new data directly,
without copying to the buffer. */
Curl_sha512_256_transform(ctx->H, data);
data += CURL_SHA512_256_BLOCK_SIZE;
length -= CURL_SHA512_256_BLOCK_SIZE;
}
if(length) {
/* Copy incomplete block of new data (if any)
to the buffer. */
memcpy(((unsigned char *) ctx_buf) + bytes_have, data, length);
}
return CURLE_OK;
}
/**
* Size of "length" insertion in bits.
* See FIPS PUB 180-4 section 5.1.2.
*/
#define SHA512_256_SIZE_OF_LEN_ADD_BITS 128
/**
* Size of "length" insertion in bytes.
*/
#define SHA512_256_SIZE_OF_LEN_ADD (SHA512_256_SIZE_OF_LEN_ADD_BITS / 8)
/**
* Finalise SHA-512/256 calculation, return digest.
*
* @param context the calculation context
* @param[out] digest set to the hash, must be #CURL_SHA512_256_DIGEST_SIZE
# bytes
* @return always CURLE_OK
*/
static CURLcode Curl_sha512_256_finish(unsigned char *digest, void *context)
{
struct Curl_sha512_256ctx *const ctx = (struct Curl_sha512_256ctx *)context;
curl_uint64_t num_bits; /**< Number of processed bits */
unsigned int bytes_have; /**< Number of bytes in the context buffer */
/* the void pointer here is required to mute Intel compiler warning */
void *const ctx_buf = ctx->buffer;
/* Memorise the number of processed bits.
The padding and other data added here during the postprocessing must
not change the amount of hashed data. */
num_bits = ctx->count << 3;
/* Note: (count & (CURL_SHA512_256_BLOCK_SIZE-1))
equals (count % CURL_SHA512_256_BLOCK_SIZE) for this block size. */
bytes_have = (unsigned int) (ctx->count & (CURL_SHA512_256_BLOCK_SIZE - 1));
/* Input data must be padded with a single bit "1", then with zeros and
the finally the length of data in bits must be added as the final bytes
of the last block.
See FIPS PUB 180-4 section 5.1.2. */
/* Data is always processed in form of bytes (not by individual bits),
therefore position of the first padding bit in byte is always
predefined (0x80). */
/* Buffer always have space at least for one byte (as full buffers are
processed when formed). */
((unsigned char *) ctx_buf)[bytes_have++] = 0x80U;
if(CURL_SHA512_256_BLOCK_SIZE - bytes_have < SHA512_256_SIZE_OF_LEN_ADD) {
/* No space in the current block to put the total length of message.
Pad the current block with zeros and process it. */
if(bytes_have < CURL_SHA512_256_BLOCK_SIZE)
memset(((unsigned char *) ctx_buf) + bytes_have, 0,
CURL_SHA512_256_BLOCK_SIZE - bytes_have);
/* Process the full block. */
Curl_sha512_256_transform(ctx->H, ctx->buffer);
/* Start the new block. */
bytes_have = 0;
}
/* Pad the rest of the buffer with zeros. */
memset(((unsigned char *) ctx_buf) + bytes_have, 0,
CURL_SHA512_256_BLOCK_SIZE - SHA512_256_SIZE_OF_LEN_ADD - bytes_have);
/* Put high part of number of bits in processed message and then lower
part of number of bits as big-endian values.
See FIPS PUB 180-4 section 5.1.2. */
/* Note: the target location is predefined and buffer is always aligned */
CURL_PUT_64BIT_BE(((unsigned char *) ctx_buf) \
+ CURL_SHA512_256_BLOCK_SIZE \
- SHA512_256_SIZE_OF_LEN_ADD, \
ctx->count_bits_hi);
CURL_PUT_64BIT_BE(((unsigned char *) ctx_buf) \
+ CURL_SHA512_256_BLOCK_SIZE \
- SHA512_256_SIZE_OF_LEN_ADD \
+ SHA512_256_BYTES_IN_WORD, \
num_bits);
/* Process the full final block. */
Curl_sha512_256_transform(ctx->H, ctx->buffer);
/* Put in BE mode the leftmost part of the hash as the final digest.
See FIPS PUB 180-4 section 6.7. */
CURL_PUT_64BIT_BE((digest + 0 * SHA512_256_BYTES_IN_WORD), ctx->H[0]);
CURL_PUT_64BIT_BE((digest + 1 * SHA512_256_BYTES_IN_WORD), ctx->H[1]);
CURL_PUT_64BIT_BE((digest + 2 * SHA512_256_BYTES_IN_WORD), ctx->H[2]);
CURL_PUT_64BIT_BE((digest + 3 * SHA512_256_BYTES_IN_WORD), ctx->H[3]);
/* Erase potentially sensitive data. */
memset(ctx, 0, sizeof(struct Curl_sha512_256ctx));
return CURLE_OK;
}
#endif /* Local SHA-512/256 code */
/**
* Compute SHA-512/256 hash for the given data in one function call
* @param[out] output the pointer to put the hash
* @param[in] input the pointer to the data to process
* @param input_size the size of the data pointed by @a input
* @return always #CURLE_OK
*/
CURLcode Curl_sha512_256it(unsigned char *output, const unsigned char *input,
size_t input_size)
{
Curl_sha512_256_ctx ctx;
CURLcode res;
res = Curl_sha512_256_init(&ctx);
if(res != CURLE_OK)
return res;
res = Curl_sha512_256_update(&ctx, (const void *) input, input_size);
if(res != CURLE_OK) {
(void)Curl_sha512_256_finish(output, &ctx);
return res;
}
return Curl_sha512_256_finish(output, &ctx);
}
/* Wrapper function, takes 'unsigned int' as length type, returns void */
static void Curl_sha512_256_update_i(void *context,
const unsigned char *data,
unsigned int length)
{
/* Hypothetically the function may fail, but assume it does not */
(void)Curl_sha512_256_update(context, data, length);
}
/* Wrapper function, returns void */
static void Curl_sha512_256_finish_v(unsigned char *result, void *context)
{
/* Hypothetically the function may fail, but assume it does not */
(void)Curl_sha512_256_finish(result, context);
}
/* Wrapper function, takes 'unsigned int' as length type, returns void */
const struct HMAC_params Curl_HMAC_SHA512_256[] = {
{
/* Initialize context procedure. */
Curl_sha512_256_init,
/* Update context with data. */
Curl_sha512_256_update_i,
/* Get final result procedure. */
Curl_sha512_256_finish_v,
/* Context structure size. */
sizeof(Curl_sha512_256_ctx),
/* Maximum key length (bytes). */
CURL_SHA512_256_BLOCK_SIZE,
/* Result length (bytes). */
CURL_SHA512_256_DIGEST_SIZE
}
};
#endif /* !CURL_DISABLE_DIGEST_AUTH && !CURL_DISABLE_SHA512_256 */
+44
View File
@@ -0,0 +1,44 @@
#ifndef HEADER_CURL_SHA512_256_H
#define HEADER_CURL_SHA512_256_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Evgeny Grin (Karlson2k), <k2k@narod.ru>.
*
* 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
*
***************************************************************************/
#if !defined(CURL_DISABLE_DIGEST_AUTH) && !defined(CURL_DISABLE_SHA512_256)
#include <curl/curl.h>
#include "curl_hmac.h"
#define CURL_HAVE_SHA512_256
extern const struct HMAC_params Curl_HMAC_SHA512_256[1];
#define CURL_SHA512_256_DIGEST_LENGTH 32
CURLcode
Curl_sha512_256it(unsigned char *output, const unsigned char *input,
size_t input_size);
#endif /* !CURL_DISABLE_DIGEST_AUTH && !CURL_DISABLE_SHA512_256 */
#endif /* HEADER_CURL_SHA256_H */
+208
View File
@@ -0,0 +1,208 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifdef USE_WINDOWS_SSPI
#include <curl/curl.h>
#include "curl_sspi.h"
#include "strdup.h"
#include "curlx/multibyte.h"
#include "system_win32.h"
#include "curlx/warnless.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
/* Pointer to SSPI dispatch table */
PSecurityFunctionTable Curl_pSecFn = NULL;
/*
* Curl_sspi_global_init()
*
* This is used to load the Security Service Provider Interface (SSPI)
* dynamic link library portably across all Windows versions, without
* the need to directly link libcurl, nor the application using it, at
* build time.
*
* Once this function has been executed, Windows SSPI functions can be
* called through the Security Service Provider Interface dispatch table.
*
* Parameters:
*
* None.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_sspi_global_init(void)
{
/* If security interface is not yet initialized try to do this */
if(!Curl_pSecFn) {
/* Get pointer to Security Service Provider Interface dispatch table */
#ifdef __MINGW32CE__
Curl_pSecFn = InitSecurityInterfaceW();
#else
Curl_pSecFn = InitSecurityInterface();
#endif
if(!Curl_pSecFn)
return CURLE_FAILED_INIT;
}
return CURLE_OK;
}
/*
* Curl_sspi_global_cleanup()
*
* This deinitializes the Security Service Provider Interface from libcurl.
*
* Parameters:
*
* None.
*/
void Curl_sspi_global_cleanup(void)
{
if(Curl_pSecFn) {
Curl_pSecFn = NULL;
}
}
/*
* Curl_create_sspi_identity()
*
* This is used to populate an SSPI identity structure based on the supplied
* username and password.
*
* Parameters:
*
* userp [in] - The username in the format User or Domain\User.
* passwdp [in] - The user's password.
* identity [in/out] - The identity structure.
*
* Returns CURLE_OK on success.
*/
CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
SEC_WINNT_AUTH_IDENTITY *identity)
{
xcharp_u useranddomain;
xcharp_u user, dup_user;
xcharp_u domain, dup_domain;
xcharp_u passwd, dup_passwd;
size_t domlen = 0;
domain.const_tchar_ptr = TEXT("");
/* Initialize the identity */
memset(identity, 0, sizeof(*identity));
useranddomain.tchar_ptr = curlx_convert_UTF8_to_tchar(userp);
if(!useranddomain.tchar_ptr)
return CURLE_OUT_OF_MEMORY;
user.const_tchar_ptr = _tcschr(useranddomain.const_tchar_ptr, TEXT('\\'));
if(!user.const_tchar_ptr)
user.const_tchar_ptr = _tcschr(useranddomain.const_tchar_ptr, TEXT('/'));
if(user.tchar_ptr) {
domain.tchar_ptr = useranddomain.tchar_ptr;
domlen = user.tchar_ptr - useranddomain.tchar_ptr;
user.tchar_ptr++;
}
else {
user.tchar_ptr = useranddomain.tchar_ptr;
domain.const_tchar_ptr = TEXT("");
domlen = 0;
}
/* Setup the identity's user and length */
dup_user.tchar_ptr = Curl_tcsdup(user.tchar_ptr);
if(!dup_user.tchar_ptr) {
curlx_unicodefree(useranddomain.tchar_ptr);
return CURLE_OUT_OF_MEMORY;
}
identity->User = dup_user.tbyte_ptr;
identity->UserLength = curlx_uztoul(_tcslen(dup_user.tchar_ptr));
dup_user.tchar_ptr = NULL;
/* Setup the identity's domain and length */
dup_domain.tchar_ptr = malloc(sizeof(TCHAR) * (domlen + 1));
if(!dup_domain.tchar_ptr) {
curlx_unicodefree(useranddomain.tchar_ptr);
return CURLE_OUT_OF_MEMORY;
}
_tcsncpy(dup_domain.tchar_ptr, domain.tchar_ptr, domlen);
*(dup_domain.tchar_ptr + domlen) = TEXT('\0');
identity->Domain = dup_domain.tbyte_ptr;
identity->DomainLength = curlx_uztoul(domlen);
dup_domain.tchar_ptr = NULL;
curlx_unicodefree(useranddomain.tchar_ptr);
/* Setup the identity's password and length */
passwd.tchar_ptr = curlx_convert_UTF8_to_tchar(passwdp);
if(!passwd.tchar_ptr)
return CURLE_OUT_OF_MEMORY;
dup_passwd.tchar_ptr = Curl_tcsdup(passwd.tchar_ptr);
if(!dup_passwd.tchar_ptr) {
curlx_unicodefree(passwd.tchar_ptr);
return CURLE_OUT_OF_MEMORY;
}
identity->Password = dup_passwd.tbyte_ptr;
identity->PasswordLength = curlx_uztoul(_tcslen(dup_passwd.tchar_ptr));
dup_passwd.tchar_ptr = NULL;
curlx_unicodefree(passwd.tchar_ptr);
/* Setup the identity's flags */
identity->Flags = (unsigned long)
#ifdef UNICODE
SEC_WINNT_AUTH_IDENTITY_UNICODE;
#else
SEC_WINNT_AUTH_IDENTITY_ANSI;
#endif
return CURLE_OK;
}
/*
* Curl_sspi_free_identity()
*
* This is used to free the contents of an SSPI identifier structure.
*
* Parameters:
*
* identity [in/out] - The identity structure.
*/
void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity)
{
if(identity) {
Curl_safefree(identity->User);
Curl_safefree(identity->Password);
Curl_safefree(identity->Domain);
}
}
#endif /* USE_WINDOWS_SSPI */
+351
View File
@@ -0,0 +1,351 @@
#ifndef HEADER_CURL_SSPI_H
#define HEADER_CURL_SSPI_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifdef USE_WINDOWS_SSPI
#include <curl/curl.h>
/*
* When including the following three headers, it is mandatory to define either
* SECURITY_WIN32 or SECURITY_KERNEL, indicating who is compiling the code.
*/
#undef SECURITY_WIN32
#undef SECURITY_KERNEL
#define SECURITY_WIN32 1
#include <security.h>
#include <sspi.h>
#include <rpc.h>
CURLcode Curl_sspi_global_init(void);
void Curl_sspi_global_cleanup(void);
/* This is used to populate the domain in an SSPI identity structure */
CURLcode Curl_override_sspi_http_realm(const char *chlg,
SEC_WINNT_AUTH_IDENTITY *identity);
/* This is used to generate an SSPI identity structure */
CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
SEC_WINNT_AUTH_IDENTITY *identity);
/* This is used to free an SSPI identity structure */
void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity);
/* Forward-declaration of global variables defined in curl_sspi.c */
extern PSecurityFunctionTable Curl_pSecFn;
/* Provide some definitions missing in old headers */
#define SP_NAME_DIGEST "WDigest"
#define SP_NAME_NTLM "NTLM"
#define SP_NAME_NEGOTIATE "Negotiate"
#define SP_NAME_KERBEROS "Kerberos"
/* Offered by mingw-w64 v9+. MS SDK 7.0A+. */
#ifndef ISC_REQ_USE_HTTP_STYLE
#define ISC_REQ_USE_HTTP_STYLE 0x01000000
#endif
#ifdef __MINGW32CE__
#ifndef ISC_RET_REPLAY_DETECT
#define ISC_RET_REPLAY_DETECT 0x00000004
#endif
#ifndef ISC_RET_SEQUENCE_DETECT
#define ISC_RET_SEQUENCE_DETECT 0x00000008
#endif
#ifndef ISC_RET_CONFIDENTIALITY
#define ISC_RET_CONFIDENTIALITY 0x00000010
#endif
#ifndef ISC_RET_ALLOCATED_MEMORY
#define ISC_RET_ALLOCATED_MEMORY 0x00000100
#endif
#ifndef ISC_RET_STREAM
#define ISC_RET_STREAM 0x00008000
#endif
#ifndef SEC_E_INSUFFICIENT_MEMORY
#define SEC_E_INSUFFICIENT_MEMORY ((HRESULT)0x80090300L)
#endif
#ifndef SEC_E_INVALID_HANDLE
#define SEC_E_INVALID_HANDLE ((HRESULT)0x80090301L)
#endif
#ifndef SEC_E_UNSUPPORTED_FUNCTION
#define SEC_E_UNSUPPORTED_FUNCTION ((HRESULT)0x80090302L)
#endif
#ifndef SEC_E_TARGET_UNKNOWN
#define SEC_E_TARGET_UNKNOWN ((HRESULT)0x80090303L)
#endif
#ifndef SEC_E_INTERNAL_ERROR
#define SEC_E_INTERNAL_ERROR ((HRESULT)0x80090304L)
#endif
#ifndef SEC_E_SECPKG_NOT_FOUND
#define SEC_E_SECPKG_NOT_FOUND ((HRESULT)0x80090305L)
#endif
#ifndef SEC_E_NOT_OWNER
#define SEC_E_NOT_OWNER ((HRESULT)0x80090306L)
#endif
#ifndef SEC_E_CANNOT_INSTALL
#define SEC_E_CANNOT_INSTALL ((HRESULT)0x80090307L)
#endif
#ifndef SEC_E_INVALID_TOKEN
#define SEC_E_INVALID_TOKEN ((HRESULT)0x80090308L)
#endif
#ifndef SEC_E_CANNOT_PACK
#define SEC_E_CANNOT_PACK ((HRESULT)0x80090309L)
#endif
#ifndef SEC_E_QOP_NOT_SUPPORTED
#define SEC_E_QOP_NOT_SUPPORTED ((HRESULT)0x8009030AL)
#endif
#ifndef SEC_E_NO_IMPERSONATION
#define SEC_E_NO_IMPERSONATION ((HRESULT)0x8009030BL)
#endif
#ifndef SEC_E_LOGON_DENIED
#define SEC_E_LOGON_DENIED ((HRESULT)0x8009030CL)
#endif
#ifndef SEC_E_UNKNOWN_CREDENTIALS
#define SEC_E_UNKNOWN_CREDENTIALS ((HRESULT)0x8009030DL)
#endif
#ifndef SEC_E_NO_CREDENTIALS
#define SEC_E_NO_CREDENTIALS ((HRESULT)0x8009030EL)
#endif
#ifndef SEC_E_MESSAGE_ALTERED
#define SEC_E_MESSAGE_ALTERED ((HRESULT)0x8009030FL)
#endif
#ifndef SEC_E_OUT_OF_SEQUENCE
#define SEC_E_OUT_OF_SEQUENCE ((HRESULT)0x80090310L)
#endif
#ifndef SEC_E_NO_AUTHENTICATING_AUTHORITY
#define SEC_E_NO_AUTHENTICATING_AUTHORITY ((HRESULT)0x80090311L)
#endif
#ifndef SEC_E_BAD_PKGID
#define SEC_E_BAD_PKGID ((HRESULT)0x80090316L)
#endif
#ifndef SEC_E_CONTEXT_EXPIRED
#define SEC_E_CONTEXT_EXPIRED ((HRESULT)0x80090317L)
#endif
#ifndef SEC_E_INCOMPLETE_MESSAGE
#define SEC_E_INCOMPLETE_MESSAGE ((HRESULT)0x80090318L)
#endif
#ifndef SEC_E_INCOMPLETE_CREDENTIALS
#define SEC_E_INCOMPLETE_CREDENTIALS ((HRESULT)0x80090320L)
#endif
#ifndef SEC_E_BUFFER_TOO_SMALL
#define SEC_E_BUFFER_TOO_SMALL ((HRESULT)0x80090321L)
#endif
#ifndef SEC_E_WRONG_PRINCIPAL
#define SEC_E_WRONG_PRINCIPAL ((HRESULT)0x80090322L)
#endif
#ifndef SEC_E_TIME_SKEW
#define SEC_E_TIME_SKEW ((HRESULT)0x80090324L)
#endif
#ifndef SEC_E_UNTRUSTED_ROOT
#define SEC_E_UNTRUSTED_ROOT ((HRESULT)0x80090325L)
#endif
#ifndef SEC_E_ILLEGAL_MESSAGE
#define SEC_E_ILLEGAL_MESSAGE ((HRESULT)0x80090326L)
#endif
#ifndef SEC_E_CERT_UNKNOWN
#define SEC_E_CERT_UNKNOWN ((HRESULT)0x80090327L)
#endif
#ifndef SEC_E_CERT_EXPIRED
#define SEC_E_CERT_EXPIRED ((HRESULT)0x80090328L)
#endif
#ifndef SEC_E_ENCRYPT_FAILURE
#define SEC_E_ENCRYPT_FAILURE ((HRESULT)0x80090329L)
#endif
#ifndef SEC_E_DECRYPT_FAILURE
#define SEC_E_DECRYPT_FAILURE ((HRESULT)0x80090330L)
#endif
#ifndef SEC_E_ALGORITHM_MISMATCH
#define SEC_E_ALGORITHM_MISMATCH ((HRESULT)0x80090331L)
#endif
#ifndef SEC_E_SECURITY_QOS_FAILED
#define SEC_E_SECURITY_QOS_FAILED ((HRESULT)0x80090332L)
#endif
#ifndef SEC_E_UNFINISHED_CONTEXT_DELETED
#define SEC_E_UNFINISHED_CONTEXT_DELETED ((HRESULT)0x80090333L)
#endif
#ifndef SEC_E_NO_TGT_REPLY
#define SEC_E_NO_TGT_REPLY ((HRESULT)0x80090334L)
#endif
#ifndef SEC_E_NO_IP_ADDRESSES
#define SEC_E_NO_IP_ADDRESSES ((HRESULT)0x80090335L)
#endif
#ifndef SEC_E_WRONG_CREDENTIAL_HANDLE
#define SEC_E_WRONG_CREDENTIAL_HANDLE ((HRESULT)0x80090336L)
#endif
#ifndef SEC_E_CRYPTO_SYSTEM_INVALID
#define SEC_E_CRYPTO_SYSTEM_INVALID ((HRESULT)0x80090337L)
#endif
#ifndef SEC_E_MAX_REFERRALS_EXCEEDED
#define SEC_E_MAX_REFERRALS_EXCEEDED ((HRESULT)0x80090338L)
#endif
#ifndef SEC_E_MUST_BE_KDC
#define SEC_E_MUST_BE_KDC ((HRESULT)0x80090339L)
#endif
#ifndef SEC_E_STRONG_CRYPTO_NOT_SUPPORTED
#define SEC_E_STRONG_CRYPTO_NOT_SUPPORTED ((HRESULT)0x8009033AL)
#endif
#ifndef SEC_E_TOO_MANY_PRINCIPALS
#define SEC_E_TOO_MANY_PRINCIPALS ((HRESULT)0x8009033BL)
#endif
#ifndef SEC_E_NO_PA_DATA
#define SEC_E_NO_PA_DATA ((HRESULT)0x8009033CL)
#endif
#ifndef SEC_E_PKINIT_NAME_MISMATCH
#define SEC_E_PKINIT_NAME_MISMATCH ((HRESULT)0x8009033DL)
#endif
#ifndef SEC_E_SMARTCARD_LOGON_REQUIRED
#define SEC_E_SMARTCARD_LOGON_REQUIRED ((HRESULT)0x8009033EL)
#endif
#ifndef SEC_E_SHUTDOWN_IN_PROGRESS
#define SEC_E_SHUTDOWN_IN_PROGRESS ((HRESULT)0x8009033FL)
#endif
#ifndef SEC_E_KDC_INVALID_REQUEST
#define SEC_E_KDC_INVALID_REQUEST ((HRESULT)0x80090340L)
#endif
#ifndef SEC_E_KDC_UNABLE_TO_REFER
#define SEC_E_KDC_UNABLE_TO_REFER ((HRESULT)0x80090341L)
#endif
#ifndef SEC_E_KDC_UNKNOWN_ETYPE
#define SEC_E_KDC_UNKNOWN_ETYPE ((HRESULT)0x80090342L)
#endif
#ifndef SEC_E_UNSUPPORTED_PREAUTH
#define SEC_E_UNSUPPORTED_PREAUTH ((HRESULT)0x80090343L)
#endif
#ifndef SEC_E_DELEGATION_REQUIRED
#define SEC_E_DELEGATION_REQUIRED ((HRESULT)0x80090345L)
#endif
#ifndef SEC_E_BAD_BINDINGS
#define SEC_E_BAD_BINDINGS ((HRESULT)0x80090346L)
#endif
#ifndef SEC_E_MULTIPLE_ACCOUNTS
#define SEC_E_MULTIPLE_ACCOUNTS ((HRESULT)0x80090347L)
#endif
#ifndef SEC_E_NO_KERB_KEY
#define SEC_E_NO_KERB_KEY ((HRESULT)0x80090348L)
#endif
#ifndef SEC_E_CERT_WRONG_USAGE
#define SEC_E_CERT_WRONG_USAGE ((HRESULT)0x80090349L)
#endif
#ifndef SEC_E_DOWNGRADE_DETECTED
#define SEC_E_DOWNGRADE_DETECTED ((HRESULT)0x80090350L)
#endif
#ifndef SEC_E_SMARTCARD_CERT_REVOKED
#define SEC_E_SMARTCARD_CERT_REVOKED ((HRESULT)0x80090351L)
#endif
#ifndef SEC_E_ISSUING_CA_UNTRUSTED
#define SEC_E_ISSUING_CA_UNTRUSTED ((HRESULT)0x80090352L)
#endif
#ifndef SEC_E_REVOCATION_OFFLINE_C
#define SEC_E_REVOCATION_OFFLINE_C ((HRESULT)0x80090353L)
#endif
#ifndef SEC_E_PKINIT_CLIENT_FAILURE
#define SEC_E_PKINIT_CLIENT_FAILURE ((HRESULT)0x80090354L)
#endif
#ifndef SEC_E_SMARTCARD_CERT_EXPIRED
#define SEC_E_SMARTCARD_CERT_EXPIRED ((HRESULT)0x80090355L)
#endif
#ifndef SEC_E_NO_S4U_PROT_SUPPORT
#define SEC_E_NO_S4U_PROT_SUPPORT ((HRESULT)0x80090356L)
#endif
#ifndef SEC_E_CROSSREALM_DELEGATION_FAILURE
#define SEC_E_CROSSREALM_DELEGATION_FAILURE ((HRESULT)0x80090357L)
#endif
#ifndef SEC_E_REVOCATION_OFFLINE_KDC
#define SEC_E_REVOCATION_OFFLINE_KDC ((HRESULT)0x80090358L)
#endif
#ifndef SEC_E_ISSUING_CA_UNTRUSTED_KDC
#define SEC_E_ISSUING_CA_UNTRUSTED_KDC ((HRESULT)0x80090359L)
#endif
#ifndef SEC_E_KDC_CERT_EXPIRED
#define SEC_E_KDC_CERT_EXPIRED ((HRESULT)0x8009035AL)
#endif
#ifndef SEC_E_KDC_CERT_REVOKED
#define SEC_E_KDC_CERT_REVOKED ((HRESULT)0x8009035BL)
#endif
#endif /* __MINGW32CE__ */
/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */
#ifndef SEC_E_INVALID_PARAMETER
#define SEC_E_INVALID_PARAMETER ((HRESULT)0x8009035DL)
#endif
/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */
#ifndef SEC_E_DELEGATION_POLICY
#define SEC_E_DELEGATION_POLICY ((HRESULT)0x8009035EL)
#endif
/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */
#ifndef SEC_E_POLICY_NLTM_ONLY
#define SEC_E_POLICY_NLTM_ONLY ((HRESULT)0x8009035FL)
#endif
#ifdef __MINGW32CE__
#ifndef SEC_I_CONTINUE_NEEDED
#define SEC_I_CONTINUE_NEEDED ((HRESULT)0x00090312L)
#endif
#ifndef SEC_I_COMPLETE_NEEDED
#define SEC_I_COMPLETE_NEEDED ((HRESULT)0x00090313L)
#endif
#ifndef SEC_I_COMPLETE_AND_CONTINUE
#define SEC_I_COMPLETE_AND_CONTINUE ((HRESULT)0x00090314L)
#endif
#ifndef SEC_I_LOCAL_LOGON
#define SEC_I_LOCAL_LOGON ((HRESULT)0x00090315L)
#endif
#ifndef SEC_I_CONTEXT_EXPIRED
#define SEC_I_CONTEXT_EXPIRED ((HRESULT)0x00090317L)
#endif
#ifndef SEC_I_INCOMPLETE_CREDENTIALS
#define SEC_I_INCOMPLETE_CREDENTIALS ((HRESULT)0x00090320L)
#endif
#ifndef SEC_I_RENEGOTIATE
#define SEC_I_RENEGOTIATE ((HRESULT)0x00090321L)
#endif
#ifndef SEC_I_NO_LSA_CONTEXT
#define SEC_I_NO_LSA_CONTEXT ((HRESULT)0x00090323L)
#endif
#endif /* __MINGW32CE__ */
/* Offered by mingw-w64 v8+. MS SDK 6.0A+. */
#ifndef SEC_I_SIGNATURE_NEEDED
#define SEC_I_SIGNATURE_NEEDED ((HRESULT)0x0009035CL)
#endif
#ifdef __MINGW32CE__
#ifndef CRYPT_E_NOT_IN_REVOCATION_DATABASE
#define CRYPT_E_NOT_IN_REVOCATION_DATABASE ((HRESULT)0x80092014L)
#endif
#endif /* __MINGW32CE__ */
/*
* Definitions required from ntsecapi.h are directly provided below this point
* to avoid including ntsecapi.h due to a conflict with OpenSSL's safestack.h
*/
#define KERB_WRAP_NO_ENCRYPT 0x80000001
#endif /* USE_WINDOWS_SSPI */
#endif /* HEADER_CURL_SSPI_H */
+142
View File
@@ -0,0 +1,142 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include <curl/curl.h>
#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
#include <pthread.h>
#endif
#include "curl_threads.h"
#include "curl_memory.h"
/* The last #include FILE should be: */
#include "memdebug.h"
#ifdef USE_THREADS_POSIX
struct Curl_actual_call {
unsigned int (*func)(void *);
void *arg;
};
static void *curl_thread_create_thunk(void *arg)
{
struct Curl_actual_call *ac = arg;
unsigned int (*func)(void *) = ac->func;
void *real_arg = ac->arg;
free(ac);
(*func)(real_arg);
return 0;
}
curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T
(CURL_STDCALL *func) (void *), void *arg)
{
curl_thread_t t = malloc(sizeof(pthread_t));
struct Curl_actual_call *ac = malloc(sizeof(struct Curl_actual_call));
int rc;
if(!(ac && t))
goto err;
ac->func = func;
ac->arg = arg;
rc = pthread_create(t, NULL, curl_thread_create_thunk, ac);
if(rc) {
CURL_SETERRNO(rc);
goto err;
}
return t;
err:
free(t);
free(ac);
return curl_thread_t_null;
}
void Curl_thread_destroy(curl_thread_t *hnd)
{
if(*hnd != curl_thread_t_null) {
pthread_detach(**hnd);
free(*hnd);
*hnd = curl_thread_t_null;
}
}
int Curl_thread_join(curl_thread_t *hnd)
{
int ret = (pthread_join(**hnd, NULL) == 0);
free(*hnd);
*hnd = curl_thread_t_null;
return ret;
}
#elif defined(USE_THREADS_WIN32)
curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T
(CURL_STDCALL *func) (void *), void *arg)
{
curl_thread_t t = CreateThread(NULL, 0, func, arg, 0, NULL);
if(!t) {
DWORD gle = GetLastError();
/* !checksrc! disable ERRNOVAR 1 */
int err = (gle == ERROR_ACCESS_DENIED ||
gle == ERROR_NOT_ENOUGH_MEMORY) ?
EACCES : EINVAL;
CURL_SETERRNO(err);
return curl_thread_t_null;
}
return t;
}
void Curl_thread_destroy(curl_thread_t *hnd)
{
if(*hnd != curl_thread_t_null) {
CloseHandle(*hnd);
*hnd = curl_thread_t_null;
}
}
int Curl_thread_join(curl_thread_t *hnd)
{
#ifdef UNDER_CE
int ret = (WaitForSingleObject(*hnd, INFINITE) == WAIT_OBJECT_0);
#else
int ret = (WaitForSingleObjectEx(*hnd, INFINITE, FALSE) == WAIT_OBJECT_0);
#endif
Curl_thread_destroy(hnd);
return ret;
}
#endif /* USE_THREADS_* */
+65
View File
@@ -0,0 +1,65 @@
#ifndef HEADER_CURL_THREADS_H
#define HEADER_CURL_THREADS_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#ifdef USE_THREADS_POSIX
# define CURL_THREAD_RETURN_T unsigned int
# define CURL_STDCALL
# define curl_mutex_t pthread_mutex_t
# define curl_thread_t pthread_t *
# define curl_thread_t_null (pthread_t *)0
# define Curl_mutex_init(m) pthread_mutex_init(m, NULL)
# define Curl_mutex_acquire(m) pthread_mutex_lock(m)
# define Curl_mutex_release(m) pthread_mutex_unlock(m)
# define Curl_mutex_destroy(m) pthread_mutex_destroy(m)
#elif defined(USE_THREADS_WIN32)
# define CURL_THREAD_RETURN_T DWORD
# define CURL_STDCALL WINAPI
# define curl_mutex_t CRITICAL_SECTION
# define curl_thread_t HANDLE
# define curl_thread_t_null (HANDLE)0
# if !defined(_WIN32_WINNT) || (_WIN32_WINNT < _WIN32_WINNT_VISTA)
# define Curl_mutex_init(m) InitializeCriticalSection(m)
# else
# define Curl_mutex_init(m) InitializeCriticalSectionEx(m, 0, 1)
# endif
# define Curl_mutex_acquire(m) EnterCriticalSection(m)
# define Curl_mutex_release(m) LeaveCriticalSection(m)
# define Curl_mutex_destroy(m) DeleteCriticalSection(m)
#endif
#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
curl_thread_t Curl_thread_create(CURL_THREAD_RETURN_T
(CURL_STDCALL *func) (void *), void *arg);
void Curl_thread_destroy(curl_thread_t *hnd);
int Curl_thread_join(curl_thread_t *hnd);
#endif /* USE_THREADS_POSIX || USE_THREADS_WIN32 */
#endif /* HEADER_CURL_THREADS_H */
+711
View File
@@ -0,0 +1,711 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "curl_setup.h"
#include <curl/curl.h>
#include "curl_trc.h"
#include "urldata.h"
#include "easyif.h"
#include "cfilters.h"
#include "multiif.h"
#include "cf-socket.h"
#include "connect.h"
#include "doh.h"
#include "http2.h"
#include "http_proxy.h"
#include "cf-h1-proxy.h"
#include "cf-h2-proxy.h"
#include "cf-haproxy.h"
#include "cf-https-connect.h"
#include "cf-ip-happy.h"
#include "socks.h"
#include "curlx/strparse.h"
#include "vtls/vtls.h"
#include "vquic/vquic.h"
/* The last 2 #include files should be in this order */
#include "curl_memory.h"
#include "memdebug.h"
static void trc_write(struct Curl_easy *data, curl_infotype type,
const char *ptr, size_t size)
{
if(data->set.verbose) {
if(data->set.fdebug) {
bool inCallback = Curl_is_in_callback(data);
Curl_set_in_callback(data, TRUE);
(void)(*data->set.fdebug)(data, type, CURL_UNCONST(ptr), size,
data->set.debugdata);
Curl_set_in_callback(data, inCallback);
}
else {
static const char s_infotype[CURLINFO_END][3] = {
"* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
switch(type) {
case CURLINFO_TEXT:
case CURLINFO_HEADER_OUT:
case CURLINFO_HEADER_IN:
fwrite(s_infotype[type], 2, 1, data->set.err);
fwrite(ptr, size, 1, data->set.err);
break;
default: /* nada */
break;
}
}
}
}
/* max length we trace before ending in '...' */
#define TRC_LINE_MAX 2048
#define CURL_TRC_FMT_IDSC "[x-%" CURL_FORMAT_CURL_OFF_T "] "
#define CURL_TRC_FMT_IDSD "[%" CURL_FORMAT_CURL_OFF_T "-x] "
#define CURL_TRC_FMT_IDSDC "[%" CURL_FORMAT_CURL_OFF_T "-%" \
CURL_FORMAT_CURL_OFF_T "] "
static struct curl_trc_feat Curl_trc_feat_ids = {
"LIB-IDS",
CURL_LOG_LVL_NONE,
};
#define CURL_TRC_IDS(data) \
(Curl_trc_is_verbose(data) && \
Curl_trc_feat_ids.log_level >= CURL_LOG_LVL_INFO)
static size_t trc_print_ids(struct Curl_easy *data, char *buf, size_t maxlen)
{
curl_off_t cid = data->conn ?
data->conn->connection_id : data->state.recent_conn_id;
if(data->id >= 0) {
if(cid >= 0)
return curl_msnprintf(buf, maxlen, CURL_TRC_FMT_IDSDC, data->id, cid);
else
return curl_msnprintf(buf, maxlen, CURL_TRC_FMT_IDSD, data->id);
}
else if(cid >= 0)
return curl_msnprintf(buf, maxlen, CURL_TRC_FMT_IDSC, cid);
else {
return curl_msnprintf(buf, maxlen, "[x-x] ");
}
}
static size_t trc_end_buf(char *buf, size_t len, size_t maxlen, bool addnl)
{
/* make sure we end the trace line in `buf` properly. It needs
* to end with a terminating '\0' or '\n\0' */
if(len >= (maxlen - (addnl ? 2 : 1))) {
len = maxlen - 5;
buf[len++] = '.';
buf[len++] = '.';
buf[len++] = '.';
buf[len++] = '\n';
}
else if(addnl)
buf[len++] = '\n';
buf[len] = '\0';
return len;
}
void Curl_debug(struct Curl_easy *data, curl_infotype type,
const char *ptr, size_t size)
{
if(data->set.verbose) {
static const char s_infotype[CURLINFO_END][3] = {
"* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
char buf[TRC_LINE_MAX];
size_t len;
if(data->set.fdebug) {
bool inCallback = Curl_is_in_callback(data);
if(CURL_TRC_IDS(data) && (size < TRC_LINE_MAX)) {
len = trc_print_ids(data, buf, TRC_LINE_MAX);
len += curl_msnprintf(buf + len, TRC_LINE_MAX - len, "%.*s",
(int)size, ptr);
len = trc_end_buf(buf, len, TRC_LINE_MAX, FALSE);
Curl_set_in_callback(data, TRUE);
(void)(*data->set.fdebug)(data, type, buf, len, data->set.debugdata);
Curl_set_in_callback(data, inCallback);
}
else {
Curl_set_in_callback(data, TRUE);
(void)(*data->set.fdebug)(data, type, CURL_UNCONST(ptr),
size, data->set.debugdata);
Curl_set_in_callback(data, inCallback);
}
}
else {
switch(type) {
case CURLINFO_TEXT:
case CURLINFO_HEADER_OUT:
case CURLINFO_HEADER_IN:
#ifndef CURL_DISABLE_VERBOSE_STRINGS
if(CURL_TRC_IDS(data)) {
len = trc_print_ids(data, buf, TRC_LINE_MAX);
fwrite(buf, len, 1, data->set.err);
}
#endif
fwrite(s_infotype[type], 2, 1, data->set.err);
fwrite(ptr, size, 1, data->set.err);
break;
default: /* nada */
break;
}
}
}
}
/* Curl_failf() is for messages stating why we failed.
* The message SHALL NOT include any LF or CR.
*/
void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
{
DEBUGASSERT(!strchr(fmt, '\n'));
if(data->set.verbose || data->set.errorbuffer) {
va_list ap;
size_t len;
char error[CURL_ERROR_SIZE + 2];
va_start(ap, fmt);
len = curl_mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
if(data->set.errorbuffer && !data->state.errorbuf) {
strcpy(data->set.errorbuffer, error);
data->state.errorbuf = TRUE; /* wrote error string */
}
error[len++] = '\n';
error[len] = '\0';
trc_write(data, CURLINFO_TEXT, error, len);
va_end(ap);
}
}
#ifndef CURL_DISABLE_VERBOSE_STRINGS
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);
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)
{
size_t len = 0;
char buf[TRC_LINE_MAX];
if(CURL_TRC_IDS(data))
len += trc_print_ids(data, buf + len, TRC_LINE_MAX - len);
if(feat)
len += curl_msnprintf(buf + len, TRC_LINE_MAX - len, "[%s] ", feat->name);
if(opt_id) {
if(opt_id_idx > 0)
len += curl_msnprintf(buf + len, TRC_LINE_MAX - len, "[%s-%d] ",
opt_id, opt_id_idx);
else
len += curl_msnprintf(buf + len, TRC_LINE_MAX - len, "[%s] ", opt_id);
}
len += curl_mvsnprintf(buf + len, TRC_LINE_MAX - len, fmt, ap);
len = trc_end_buf(buf, len, TRC_LINE_MAX, TRUE);
trc_write(data, CURLINFO_TEXT, buf, len);
}
void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
{
DEBUGASSERT(!strchr(fmt, '\n'));
if(Curl_trc_is_verbose(data)) {
va_list ap;
va_start(ap, fmt);
trc_infof(data, data->state.feat, NULL, 0, fmt, ap);
va_end(ap);
}
}
void Curl_trc_cf_infof(struct Curl_easy *data, const struct Curl_cfilter *cf,
const char *fmt, ...)
{
DEBUGASSERT(cf);
if(Curl_trc_cf_is_verbose(cf, data)) {
va_list ap;
va_start(ap, fmt);
trc_infof(data, data->state.feat, cf->cft->name, cf->sockindex, fmt, ap);
va_end(ap);
}
}
struct curl_trc_feat Curl_trc_feat_multi = {
"MULTI",
CURL_LOG_LVL_NONE,
};
struct curl_trc_feat Curl_trc_feat_read = {
"READ",
CURL_LOG_LVL_NONE,
};
struct curl_trc_feat Curl_trc_feat_write = {
"WRITE",
CURL_LOG_LVL_NONE,
};
struct curl_trc_feat Curl_trc_feat_dns = {
"DNS",
CURL_LOG_LVL_NONE,
};
struct curl_trc_feat Curl_trc_feat_timer = {
"TIMER",
CURL_LOG_LVL_NONE,
};
static const char * const Curl_trc_timer_names[]={
"100_TIMEOUT",
"ASYNC_NAME",
"CONNECTTIMEOUT",
"DNS_PER_NAME",
"DNS_PER_NAME2",
"HAPPY_EYEBALLS_DNS",
"HAPPY_EYEBALLS",
"MULTI_PENDING",
"SPEEDCHECK",
"TIMEOUT",
"TOOFAST",
"QUIC",
"FTP_ACCEPT",
"ALPN_EYEBALLS",
"SHUTDOWN",
};
static const char *trc_timer_name(int tid)
{
if((tid >= 0) && ((size_t)tid < CURL_ARRAYSIZE(Curl_trc_timer_names)))
return Curl_trc_timer_names[(size_t)tid];
return "UNKNOWN?";
}
void Curl_trc_timer(struct Curl_easy *data, int tid, const char *fmt, ...)
{
DEBUGASSERT(!strchr(fmt, '\n'));
if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_timer)) {
const char *tname = trc_timer_name(tid);
va_list ap;
va_start(ap, fmt);
trc_infof(data, &Curl_trc_feat_timer, tname, 0, fmt, ap);
va_end(ap);
}
}
void Curl_trc_easy_timers(struct Curl_easy *data)
{
if(CURL_TRC_TIMER_is_verbose(data)) {
struct Curl_llist_node *e = Curl_llist_head(&data->state.timeoutlist);
if(e) {
struct curltime now = curlx_now();
while(e) {
struct time_node *n = Curl_node_elem(e);
e = Curl_node_next(e);
CURL_TRC_TIMER(data, n->eid, "expires in %" FMT_TIMEDIFF_T "ns",
curlx_timediff_us(n->time, now));
}
}
}
}
static const char * const Curl_trc_mstate_names[]={
"INIT",
"PENDING",
"SETUP",
"CONNECT",
"RESOLVING",
"CONNECTING",
"TUNNELING",
"PROTOCONNECT",
"PROTOCONNECTING",
"DO",
"DOING",
"DOING_MORE",
"DID",
"PERFORMING",
"RATELIMITING",
"DONE",
"COMPLETED",
"MSGSENT",
};
const char *Curl_trc_mstate_name(int state)
{
if((state >= 0) && ((size_t)state < CURL_ARRAYSIZE(Curl_trc_mstate_names)))
return Curl_trc_mstate_names[(size_t)state];
return "?";
}
void Curl_trc_multi(struct Curl_easy *data, const char *fmt, ...)
{
DEBUGASSERT(!strchr(fmt, '\n'));
if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_multi)) {
const char *sname = (data->id >= 0) ?
Curl_trc_mstate_name(data->mstate) : NULL;
va_list ap;
va_start(ap, fmt);
trc_infof(data, &Curl_trc_feat_multi, sname, 0, fmt, ap);
va_end(ap);
}
}
void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...)
{
DEBUGASSERT(!strchr(fmt, '\n'));
if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_read)) {
va_list ap;
va_start(ap, fmt);
trc_infof(data, &Curl_trc_feat_read, NULL, 0, fmt, ap);
va_end(ap);
}
}
void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...)
{
DEBUGASSERT(!strchr(fmt, '\n'));
if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_write)) {
va_list ap;
va_start(ap, fmt);
trc_infof(data, &Curl_trc_feat_write, NULL, 0, fmt, ap);
va_end(ap);
}
}
void Curl_trc_dns(struct Curl_easy *data, const char *fmt, ...)
{
DEBUGASSERT(!strchr(fmt, '\n'));
if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) {
va_list ap;
va_start(ap, fmt);
trc_infof(data, &Curl_trc_feat_dns, NULL, 0, fmt, ap);
va_end(ap);
}
}
#ifndef CURL_DISABLE_FTP
struct curl_trc_feat Curl_trc_feat_ftp = {
"FTP",
CURL_LOG_LVL_NONE,
};
void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...)
{
DEBUGASSERT(!strchr(fmt, '\n'));
if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ftp)) {
va_list ap;
va_start(ap, fmt);
trc_infof(data, &Curl_trc_feat_ftp, NULL, 0, fmt, ap);
va_end(ap);
}
}
#endif /* !CURL_DISABLE_FTP */
#ifndef CURL_DISABLE_SMTP
struct curl_trc_feat Curl_trc_feat_smtp = {
"SMTP",
CURL_LOG_LVL_NONE,
};
void Curl_trc_smtp(struct Curl_easy *data, const char *fmt, ...)
{
DEBUGASSERT(!strchr(fmt, '\n'));
if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_smtp)) {
va_list ap;
va_start(ap, fmt);
trc_infof(data, &Curl_trc_feat_smtp, NULL, 0, fmt, ap);
va_end(ap);
}
}
#endif /* !CURL_DISABLE_SMTP */
#ifdef USE_SSL
struct curl_trc_feat Curl_trc_feat_ssls = {
"SSLS",
CURL_LOG_LVL_NONE,
};
void Curl_trc_ssls(struct Curl_easy *data, const char *fmt, ...)
{
DEBUGASSERT(!strchr(fmt, '\n'));
if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ssls)) {
va_list ap;
va_start(ap, fmt);
trc_infof(data, &Curl_trc_feat_ssls, NULL, 0, fmt, ap);
va_end(ap);
}
}
#endif /* USE_SSL */
#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
struct curl_trc_feat Curl_trc_feat_ws = {
"WS",
CURL_LOG_LVL_NONE,
};
void Curl_trc_ws(struct Curl_easy *data, const char *fmt, ...)
{
DEBUGASSERT(!strchr(fmt, '\n'));
if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ws)) {
va_list ap;
va_start(ap, fmt);
trc_infof(data, &Curl_trc_feat_ws, NULL, 0, fmt, ap);
va_end(ap);
}
}
#endif /* !CURL_DISABLE_WEBSOCKETS && !CURL_DISABLE_HTTP */
#define TRC_CT_NONE (0)
#define TRC_CT_PROTOCOL (1<<(0))
#define TRC_CT_NETWORK (1<<(1))
#define TRC_CT_PROXY (1<<(2))
#define TRC_CT_INTERNALS (1<<(3))
struct trc_feat_def {
struct curl_trc_feat *feat;
unsigned int category;
};
static struct trc_feat_def trc_feats[] = {
{ &Curl_trc_feat_ids, TRC_CT_INTERNALS },
{ &Curl_trc_feat_multi, TRC_CT_NETWORK },
{ &Curl_trc_feat_read, TRC_CT_NONE },
{ &Curl_trc_feat_write, TRC_CT_NONE },
{ &Curl_trc_feat_dns, TRC_CT_NETWORK },
{ &Curl_trc_feat_timer, TRC_CT_NETWORK },
#ifndef CURL_DISABLE_FTP
{ &Curl_trc_feat_ftp, TRC_CT_PROTOCOL },
#endif
#ifndef CURL_DISABLE_DOH
#endif
#ifndef CURL_DISABLE_SMTP
{ &Curl_trc_feat_smtp, TRC_CT_PROTOCOL },
#endif
#ifdef USE_SSL
{ &Curl_trc_feat_ssls, TRC_CT_NETWORK },
#endif
#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
{ &Curl_trc_feat_ws, TRC_CT_PROTOCOL },
#endif
};
struct trc_cft_def {
struct Curl_cftype *cft;
unsigned int category;
};
static struct trc_cft_def trc_cfts[] = {
{ &Curl_cft_tcp, TRC_CT_NETWORK },
{ &Curl_cft_udp, TRC_CT_NETWORK },
{ &Curl_cft_unix, TRC_CT_NETWORK },
{ &Curl_cft_tcp_accept, TRC_CT_NETWORK },
{ &Curl_cft_ip_happy, TRC_CT_NETWORK },
{ &Curl_cft_setup, TRC_CT_PROTOCOL },
#if !defined(CURL_DISABLE_HTTP) && defined(USE_NGHTTP2)
{ &Curl_cft_nghttp2, TRC_CT_PROTOCOL },
#endif
#ifdef USE_SSL
{ &Curl_cft_ssl, TRC_CT_NETWORK },
#ifndef CURL_DISABLE_PROXY
{ &Curl_cft_ssl_proxy, TRC_CT_PROXY },
#endif
#endif
#ifndef CURL_DISABLE_PROXY
#ifndef CURL_DISABLE_HTTP
{ &Curl_cft_h1_proxy, TRC_CT_PROXY },
#ifdef USE_NGHTTP2
{ &Curl_cft_h2_proxy, TRC_CT_PROXY },
#endif
{ &Curl_cft_http_proxy, TRC_CT_PROXY },
#endif /* !CURL_DISABLE_HTTP */
{ &Curl_cft_haproxy, TRC_CT_PROXY },
{ &Curl_cft_socks_proxy, TRC_CT_PROXY },
#endif /* !CURL_DISABLE_PROXY */
#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3)
{ &Curl_cft_http3, TRC_CT_PROTOCOL },
#endif
#ifndef CURL_DISABLE_HTTP
{ &Curl_cft_http_connect, TRC_CT_PROTOCOL },
#endif
};
static void trc_apply_level_by_name(struct Curl_str *token, int lvl)
{
size_t i;
for(i = 0; i < CURL_ARRAYSIZE(trc_cfts); ++i) {
if(curlx_str_casecompare(token, trc_cfts[i].cft->name)) {
trc_cfts[i].cft->log_level = lvl;
break;
}
}
for(i = 0; i < CURL_ARRAYSIZE(trc_feats); ++i) {
if(curlx_str_casecompare(token, trc_feats[i].feat->name)) {
trc_feats[i].feat->log_level = lvl;
break;
}
}
}
static void trc_apply_level_by_category(int category, int lvl)
{
size_t i;
for(i = 0; i < CURL_ARRAYSIZE(trc_cfts); ++i) {
if(!category || (trc_cfts[i].category & category))
trc_cfts[i].cft->log_level = lvl;
}
for(i = 0; i < CURL_ARRAYSIZE(trc_feats); ++i) {
if(!category || (trc_feats[i].category & category))
trc_feats[i].feat->log_level = lvl;
}
}
static CURLcode trc_opt(const char *config)
{
struct Curl_str out;
while(!curlx_str_until(&config, &out, 32, ',')) {
int lvl = CURL_LOG_LVL_INFO;
const char *token = curlx_str(&out);
if(*token == '-') {
lvl = CURL_LOG_LVL_NONE;
curlx_str_nudge(&out, 1);
}
else if(*token == '+')
curlx_str_nudge(&out, 1);
if(curlx_str_casecompare(&out, "all"))
trc_apply_level_by_category(TRC_CT_NONE, lvl);
else if(curlx_str_casecompare(&out, "protocol"))
trc_apply_level_by_category(TRC_CT_PROTOCOL, lvl);
else if(curlx_str_casecompare(&out, "network"))
trc_apply_level_by_category(TRC_CT_NETWORK, lvl);
else if(curlx_str_casecompare(&out, "proxy"))
trc_apply_level_by_category(TRC_CT_PROXY, lvl);
else if(curlx_str_casecompare(&out, "doh")) {
struct Curl_str dns = { "dns", 3 };
trc_apply_level_by_name(&dns, lvl);
}
else
trc_apply_level_by_name(&out, lvl);
if(curlx_str_single(&config, ','))
break;
}
return CURLE_OK;
}
CURLcode Curl_trc_opt(const char *config)
{
CURLcode result = config ? trc_opt(config) : CURLE_OK;
#ifdef DEBUGBUILD
/* CURL_DEBUG can override anything */
if(!result) {
const char *dbg_config = getenv("CURL_DEBUG");
if(dbg_config)
result = trc_opt(dbg_config);
}
#endif /* DEBUGBUILD */
return result;
}
CURLcode Curl_trc_init(void)
{
#ifdef DEBUGBUILD
return Curl_trc_opt(NULL);
#else
return CURLE_OK;
#endif
}
#else /* CURL_DISABLE_VERBOSE_STRINGS */
CURLcode Curl_trc_init(void)
{
return CURLE_OK;
}
void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
{
(void)data; (void)fmt;
}
void Curl_trc_cf_infof(struct Curl_easy *data, const struct Curl_cfilter *cf,
const char *fmt, ...)
{
(void)data; (void)cf; (void)fmt;
}
void Curl_trc_multi(struct Curl_easy *data, const char *fmt, ...)
{
(void)data; (void)fmt;
}
void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...)
{
(void)data; (void)fmt;
}
void Curl_trc_dns(struct Curl_easy *data, const char *fmt, ...)
{
(void)data; (void)fmt;
}
void Curl_trc_timer(struct Curl_easy *data, int tid, const char *fmt, ...)
{
(void)data; (void)tid; (void)fmt;
}
void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...)
{
(void)data; (void)fmt;
}
#ifndef CURL_DISABLE_FTP
void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...)
{
(void)data; (void)fmt;
}
#endif
#ifndef CURL_DISABLE_SMTP
void Curl_trc_smtp(struct Curl_easy *data, const char *fmt, ...)
{
(void)data; (void)fmt;
}
#endif
#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
void Curl_trc_ws(struct Curl_easy *data, const char *fmt, ...)
{
(void)data; (void)fmt;
}
#endif
#ifdef USE_SSL
void Curl_trc_ssls(struct Curl_easy *data, const char *fmt, ...)
{
(void)data;
(void)fmt;
}
#endif
#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
+237
View File
@@ -0,0 +1,237 @@
#ifndef HEADER_CURL_TRC_H
#define HEADER_CURL_TRC_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
struct Curl_easy;
struct Curl_cfilter;
/**
* Init logging, return != 0 on failure.
*/
CURLcode Curl_trc_init(void);
/**
* Configure tracing. May be called several times during global
* initialization. Later calls may not take effect.
*
* Configuration format supported:
* - comma-separated list of component names to enable logging on.
* E.g. 'http/2,ssl'. Unknown names are ignored. Names are compared
* case-insensitive.
* - component 'all' applies to all known log components
* - prefixing a component with '+' or '-' will en-/disable logging for
* that component
* Example: 'all,-ssl' would enable logging for all components but the
* SSL filters.
*
* @param config configuration string
*/
CURLcode Curl_trc_opt(const char *config);
/* the function used to output verbose information */
void Curl_debug(struct Curl_easy *data, curl_infotype type,
const char *ptr, size_t size);
/**
* Output a failure message on registered callbacks for transfer.
*/
void Curl_failf(struct Curl_easy *data,
const char *fmt, ...) CURL_PRINTF(2, 3);
#define failf Curl_failf
#define CURL_LOG_LVL_NONE 0
#define CURL_LOG_LVL_INFO 1
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define CURL_HAVE_C99
#endif
/**
* Output an informational message when transfer's verbose logging is enabled.
*/
void Curl_infof(struct Curl_easy *data,
const char *fmt, ...) CURL_PRINTF(2, 3);
/**
* Output an informational message when both transfer's verbose logging
* and connection filters verbose logging are enabled.
*/
void Curl_trc_cf_infof(struct Curl_easy *data, const struct Curl_cfilter *cf,
const char *fmt, ...) CURL_PRINTF(3, 4);
void Curl_trc_multi(struct Curl_easy *data,
const char *fmt, ...) CURL_PRINTF(2, 3);
const char *Curl_trc_mstate_name(int state);
const char *Curl_trc_timer_name(int tid);
void Curl_trc_easy_timers(struct Curl_easy *data);
void Curl_trc_write(struct Curl_easy *data,
const char *fmt, ...) CURL_PRINTF(2, 3);
void Curl_trc_read(struct Curl_easy *data,
const char *fmt, ...) CURL_PRINTF(2, 3);
void Curl_trc_dns(struct Curl_easy *data,
const char *fmt, ...) CURL_PRINTF(2, 3);
void Curl_trc_timer(struct Curl_easy *data, int tid,
const char *fmt, ...) CURL_PRINTF(3, 4);
struct curl_trc_feat {
const char *name;
int log_level;
};
#ifndef CURL_DISABLE_FTP
extern struct curl_trc_feat Curl_trc_feat_ftp;
void Curl_trc_ftp(struct Curl_easy *data,
const char *fmt, ...) CURL_PRINTF(2, 3);
#endif
#ifndef CURL_DISABLE_SMTP
extern struct curl_trc_feat Curl_trc_feat_smtp;
void Curl_trc_smtp(struct Curl_easy *data,
const char *fmt, ...) CURL_PRINTF(2, 3);
#endif
#ifdef USE_SSL
extern struct curl_trc_feat Curl_trc_feat_ssls;
void Curl_trc_ssls(struct Curl_easy *data,
const char *fmt, ...) CURL_PRINTF(2, 3);
#endif
#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
extern struct curl_trc_feat Curl_trc_feat_ws;
void Curl_trc_ws(struct Curl_easy *data,
const char *fmt, ...) CURL_PRINTF(2, 3);
#endif
#define CURL_TRC_M_is_verbose(data) \
Curl_trc_ft_is_verbose(data, &Curl_trc_feat_multi)
#define CURL_TRC_DNS_is_verbose(data) \
Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)
#define CURL_TRC_TIMER_is_verbose(data) \
Curl_trc_ft_is_verbose(data, &Curl_trc_feat_timer)
#if defined(CURL_HAVE_C99) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
#define infof(data, ...) \
do { if(Curl_trc_is_verbose(data)) \
Curl_infof(data, __VA_ARGS__); } while(0)
#define CURL_TRC_M(data, ...) \
do { if(CURL_TRC_M_is_verbose(data)) \
Curl_trc_multi(data, __VA_ARGS__); } while(0)
#define CURL_TRC_CF(data, cf, ...) \
do { if(Curl_trc_cf_is_verbose(cf, data)) \
Curl_trc_cf_infof(data, cf, __VA_ARGS__); } while(0)
#define CURL_TRC_WRITE(data, ...) \
do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_write)) \
Curl_trc_write(data, __VA_ARGS__); } while(0)
#define CURL_TRC_READ(data, ...) \
do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_read)) \
Curl_trc_read(data, __VA_ARGS__); } while(0)
#define CURL_TRC_DNS(data, ...) \
do { if(CURL_TRC_DNS_is_verbose(data)) \
Curl_trc_dns(data, __VA_ARGS__); } while(0)
#define CURL_TRC_TIMER(data, tid, ...) \
do { if(CURL_TRC_TIMER_is_verbose(data)) \
Curl_trc_timer(data, tid, __VA_ARGS__); } while(0)
#ifndef CURL_DISABLE_FTP
#define CURL_TRC_FTP(data, ...) \
do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ftp)) \
Curl_trc_ftp(data, __VA_ARGS__); } while(0)
#endif /* !CURL_DISABLE_FTP */
#ifndef CURL_DISABLE_SMTP
#define CURL_TRC_SMTP(data, ...) \
do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_smtp)) \
Curl_trc_smtp(data, __VA_ARGS__); } while(0)
#endif /* !CURL_DISABLE_SMTP */
#ifdef USE_SSL
#define CURL_TRC_SSLS(data, ...) \
do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ssls)) \
Curl_trc_ssls(data, __VA_ARGS__); } while(0)
#endif /* USE_SSL */
#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
#define CURL_TRC_WS(data, ...) \
do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ws)) \
Curl_trc_ws(data, __VA_ARGS__); } while(0)
#endif /* !CURL_DISABLE_WEBSOCKETS && !CURL_DISABLE_HTTP */
#else /* CURL_HAVE_C99 */
#define infof Curl_infof
#define CURL_TRC_M Curl_trc_multi
#define CURL_TRC_CF Curl_trc_cf_infof
#define CURL_TRC_WRITE Curl_trc_write
#define CURL_TRC_READ Curl_trc_read
#define CURL_TRC_DNS Curl_trc_dns
#define CURL_TRC_TIMER Curl_trc_timer
#ifndef CURL_DISABLE_FTP
#define CURL_TRC_FTP Curl_trc_ftp
#endif
#ifndef CURL_DISABLE_SMTP
#define CURL_TRC_SMTP Curl_trc_smtp
#endif
#ifdef USE_SSL
#define CURL_TRC_SSLS Curl_trc_ssls
#endif
#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
#define CURL_TRC_WS Curl_trc_ws
#endif
#endif /* !CURL_HAVE_C99 */
#ifndef CURL_DISABLE_VERBOSE_STRINGS
/* informational messages enabled */
extern struct curl_trc_feat Curl_trc_feat_multi;
extern struct curl_trc_feat Curl_trc_feat_read;
extern struct curl_trc_feat Curl_trc_feat_write;
extern struct curl_trc_feat Curl_trc_feat_dns;
extern struct curl_trc_feat Curl_trc_feat_timer;
#define Curl_trc_is_verbose(data) \
((data) && (data)->set.verbose && \
(!(data)->state.feat || \
((data)->state.feat->log_level >= CURL_LOG_LVL_INFO)))
#define Curl_trc_cf_is_verbose(cf, data) \
(Curl_trc_is_verbose(data) && \
(cf) && (cf)->cft->log_level >= CURL_LOG_LVL_INFO)
#define Curl_trc_ft_is_verbose(data, ft) \
(Curl_trc_is_verbose(data) && \
(ft)->log_level >= CURL_LOG_LVL_INFO)
#define CURL_MSTATE_NAME(s) Curl_trc_mstate_name((int)(s))
#define CURL_TRC_EASY_TIMERS(data) \
do { if(CURL_TRC_TIMER_is_verbose(data)) \
Curl_trc_easy_timers(data); } while(0)
#else /* CURL_DISABLE_VERBOSE_STRINGS */
/* All informational messages are not compiled in for size savings */
#define Curl_trc_is_verbose(d) (FALSE)
#define Curl_trc_cf_is_verbose(x,y) (FALSE)
#define Curl_trc_ft_is_verbose(x,y) (FALSE)
#define CURL_MSTATE_NAME(x) ((void)(x), "-")
#define CURL_TRC_EASY_TIMERS(x) Curl_nop_stmt
#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
#endif /* HEADER_CURL_TRC_H */
+274
View File
@@ -0,0 +1,274 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
/* Base64 encoding/decoding */
#include "../curl_setup.h"
#include <curl/curl.h>
#include "warnless.h"
#include "base64.h"
/* The last 2 #include files should be in this order */
#ifdef BUILDING_LIBCURL
#include "../curl_memory.h"
#endif
#include "../memdebug.h"
/* ---- Base64 Encoding/Decoding Table --- */
const char Curl_base64encdec[]=
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/* The Base 64 encoding with a URL and filename safe alphabet, RFC 4648
section 5 */
static const char base64url[]=
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
static const unsigned char decodetable[] =
{ 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255,
255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28,
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51 };
/*
* curlx_base64_decode()
*
* Given a base64 null-terminated string at src, decode it and return a
* pointer in *outptr to a newly allocated memory area holding decoded data.
* Size of decoded data is returned in variable pointed by outlen.
*
* Returns CURLE_OK on success, otherwise specific error code. Function
* output shall not be considered valid unless CURLE_OK is returned.
*
* When decoded data length is 0, returns NULL in *outptr.
*
* @unittest: 1302
*/
CURLcode curlx_base64_decode(const char *src,
unsigned char **outptr, size_t *outlen)
{
size_t srclen = 0;
size_t padding = 0;
size_t i;
size_t numQuantums;
size_t fullQuantums;
size_t rawlen = 0;
unsigned char *pos;
unsigned char *newstr;
unsigned char lookup[256];
*outptr = NULL;
*outlen = 0;
srclen = strlen(src);
/* Check the length of the input string is valid */
if(!srclen || srclen % 4)
return CURLE_BAD_CONTENT_ENCODING;
/* srclen is at least 4 here */
while(src[srclen - 1 - padding] == '=') {
/* count padding characters */
padding++;
/* A maximum of two = padding characters is allowed */
if(padding > 2)
return CURLE_BAD_CONTENT_ENCODING;
}
/* Calculate the number of quantums */
numQuantums = srclen / 4;
fullQuantums = numQuantums - (padding ? 1 : 0);
/* Calculate the size of the decoded string */
rawlen = (numQuantums * 3) - padding;
/* Allocate our buffer including room for a null-terminator */
newstr = malloc(rawlen + 1);
if(!newstr)
return CURLE_OUT_OF_MEMORY;
pos = newstr;
memset(lookup, 0xff, sizeof(lookup));
memcpy(&lookup['+'], decodetable, sizeof(decodetable));
/* Decode the complete quantums first */
for(i = 0; i < fullQuantums; i++) {
unsigned char val;
unsigned int x = 0;
int j;
for(j = 0; j < 4; j++) {
val = lookup[(unsigned char)*src++];
if(val == 0xff) /* bad symbol */
goto bad;
x = (x << 6) | val;
}
pos[2] = x & 0xff;
pos[1] = (x >> 8) & 0xff;
pos[0] = (x >> 16) & 0xff;
pos += 3;
}
if(padding) {
/* this means either 8 or 16 bits output */
unsigned char val;
unsigned int x = 0;
int j;
size_t padc = 0;
for(j = 0; j < 4; j++) {
if(*src == '=') {
x <<= 6;
src++;
if(++padc > padding)
/* this is a badly placed '=' symbol! */
goto bad;
}
else {
val = lookup[(unsigned char)*src++];
if(val == 0xff) /* bad symbol */
goto bad;
x = (x << 6) | val;
}
}
if(padding == 1)
pos[1] = (x >> 8) & 0xff;
pos[0] = (x >> 16) & 0xff;
pos += 3 - padding;
}
/* Zero terminate */
*pos = '\0';
/* Return the decoded data */
*outptr = newstr;
*outlen = rawlen;
return CURLE_OK;
bad:
free(newstr);
return CURLE_BAD_CONTENT_ENCODING;
}
static CURLcode base64_encode(const char *table64,
unsigned char padbyte,
const char *inputbuff, size_t insize,
char **outptr, size_t *outlen)
{
char *output;
char *base64data;
const unsigned char *in = (const unsigned char *)inputbuff;
*outptr = NULL;
*outlen = 0;
if(!insize)
return CURLE_OK;
/* safety precaution */
DEBUGASSERT(insize <= CURL_MAX_BASE64_INPUT);
if(insize > CURL_MAX_BASE64_INPUT)
return CURLE_TOO_LARGE;
base64data = output = malloc((insize + 2) / 3 * 4 + 1);
if(!output)
return CURLE_OUT_OF_MEMORY;
while(insize >= 3) {
*output++ = table64[ in[0] >> 2 ];
*output++ = table64[ ((in[0] & 0x03) << 4) | (in[1] >> 4) ];
*output++ = table64[ ((in[1] & 0x0F) << 2) | ((in[2] & 0xC0) >> 6) ];
*output++ = table64[ in[2] & 0x3F ];
insize -= 3;
in += 3;
}
if(insize) {
/* this is only one or two bytes now */
*output++ = table64[ in[0] >> 2 ];
if(insize == 1) {
*output++ = table64[ ((in[0] & 0x03) << 4) ];
if(padbyte) {
*output++ = padbyte;
*output++ = padbyte;
}
}
else {
/* insize == 2 */
*output++ = table64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4) ];
*output++ = table64[ ((in[1] & 0x0F) << 2) ];
if(padbyte)
*output++ = padbyte;
}
}
/* Zero terminate */
*output = '\0';
/* Return the pointer to the new data (allocated memory) */
*outptr = base64data;
/* Return the length of the new data */
*outlen = (size_t)(output - base64data);
return CURLE_OK;
}
/*
* curlx_base64_encode()
*
* Given a pointer to an input buffer and an input size, encode it and
* return a pointer in *outptr to a newly allocated memory area holding
* encoded data. Size of encoded data is returned in variable pointed by
* outlen.
*
* Returns CURLE_OK on success, otherwise specific error code. Function
* output shall not be considered valid unless CURLE_OK is returned.
*
* @unittest: 1302
*/
CURLcode curlx_base64_encode(const char *inputbuff, size_t insize,
char **outptr, size_t *outlen)
{
return base64_encode(Curl_base64encdec, '=',
inputbuff, insize, outptr, outlen);
}
/*
* curlx_base64url_encode()
*
* Given a pointer to an input buffer and an input size, encode it and
* return a pointer in *outptr to a newly allocated memory area holding
* encoded data. Size of encoded data is returned in variable pointed by
* outlen.
*
* Input length of 0 indicates input buffer holds a null-terminated string.
*
* Returns CURLE_OK on success, otherwise specific error code. Function
* output shall not be considered valid unless CURLE_OK is returned.
*
* @unittest: 1302
*/
CURLcode curlx_base64url_encode(const char *inputbuff, size_t insize,
char **outptr, size_t *outlen)
{
return base64_encode(base64url, 0, inputbuff, insize, outptr, outlen);
}
+40
View File
@@ -0,0 +1,40 @@
#ifndef HEADER_CURL_BASE64_H
#define HEADER_CURL_BASE64_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
CURLcode curlx_base64_encode(const char *inputbuff, size_t insize,
char **outptr, size_t *outlen);
CURLcode curlx_base64url_encode(const char *inputbuff, size_t insize,
char **outptr, size_t *outlen);
CURLcode curlx_base64_decode(const char *src,
unsigned char **outptr, size_t *outlen);
extern const char Curl_base64encdec[];
/* maximum input length acceptable to base64 encode, here to catch and prevent
mistakes */
#define CURL_MAX_BASE64_INPUT 16000000
#endif /* HEADER_CURL_BASE64_H */
+39
View File
@@ -0,0 +1,39 @@
#ifndef HEADER_CURL_TOOL_BINMODE_H
#define HEADER_CURL_TOOL_BINMODE_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "../curl_setup.h"
#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
#endif /* HEADER_CURL_TOOL_BINMODE_H */
+89
View File
@@ -0,0 +1,89 @@
#ifndef HEADER_CURL_CURLX_H
#define HEADER_CURL_CURLX_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
/*
* Defines protos and includes all header files that provide the curlx_*
* functions. The curlx_* functions are not part of the libcurl API, but are
* stand-alone functions whose sources can be built and linked by apps if need
* be.
*/
#include "binmode.h"
/* "binmode.h" provides macro CURLX_SET_BINMODE() */
#include "nonblock.h"
/* "nonblock.h" provides curlx_nonblock() */
#include "warnless.h"
/* "warnless.h" provides functions:
curlx_ultous()
curlx_ultouc()
curlx_uztosi()
*/
#include "multibyte.h"
/* "multibyte.h" provides these functions and macros:
curlx_convert_UTF8_to_wchar()
curlx_convert_wchar_to_UTF8()
curlx_convert_UTF8_to_tchar()
curlx_convert_tchar_to_UTF8()
curlx_unicodefree()
*/
#include "version_win32.h"
/* provides curlx_verify_windows_version() */
#include "strerr.h"
/* The curlx_strerror() function */
#include "strparse.h"
/* The curlx_str_* parsing functions */
#include "dynbuf.h"
/* The curlx_dyn_* functions */
#include "fopen.h"
/* The curlx_f* functions */
#include "base64.h"
#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 */
+299
View File
@@ -0,0 +1,299 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "../curl_setup.h"
#include "dynbuf.h"
#include "../curl_printf.h"
#ifdef BUILDING_LIBCURL
#include "../curl_memory.h"
#endif
#include "../memdebug.h"
#define MIN_FIRST_ALLOC 32
#ifdef DEBUGBUILD
#define DYNINIT 0xbee51da /* random pattern */
#endif
/*
* Init a dynbuf struct.
*/
void curlx_dyn_init(struct dynbuf *s, size_t toobig)
{
DEBUGASSERT(s);
DEBUGASSERT(toobig);
DEBUGASSERT(toobig <= MAX_DYNBUF_SIZE); /* catch crazy mistakes */
s->bufr = NULL;
s->leng = 0;
s->allc = 0;
s->toobig = toobig;
#ifdef DEBUGBUILD
s->init = DYNINIT;
#endif
}
/*
* free the buffer and re-init the necessary fields. It does not touch the
* 'init' field and thus this buffer can be reused to add data to again.
*/
void curlx_dyn_free(struct dynbuf *s)
{
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
Curl_safefree(s->bufr);
s->leng = s->allc = 0;
}
/*
* Store/append an chunk of memory to the dynbuf.
*/
static CURLcode dyn_nappend(struct dynbuf *s,
const unsigned char *mem, size_t len)
{
size_t idx = s->leng;
size_t a = s->allc;
size_t fit = len + idx + 1; /* new string + old string + zero byte */
/* try to detect if there is rubbish in the struct */
DEBUGASSERT(s->init == DYNINIT);
DEBUGASSERT(s->toobig);
DEBUGASSERT(idx < s->toobig);
DEBUGASSERT(!s->leng || s->bufr);
DEBUGASSERT(a <= s->toobig);
DEBUGASSERT(!len || mem);
if(fit > s->toobig) {
curlx_dyn_free(s);
return CURLE_TOO_LARGE;
}
else if(!a) {
DEBUGASSERT(!idx);
/* first invoke */
if(MIN_FIRST_ALLOC > s->toobig)
a = s->toobig;
else if(fit < MIN_FIRST_ALLOC)
a = MIN_FIRST_ALLOC;
else
a = fit;
}
else {
while(a < fit)
a *= 2;
if(a > s->toobig)
/* no point in allocating a larger buffer than this is allowed to use */
a = s->toobig;
}
if(a != s->allc) {
/* this logic is not using Curl_saferealloc() to make the tool not have to
include that as well when it uses this code */
void *p = realloc(s->bufr, a);
if(!p) {
curlx_dyn_free(s);
return CURLE_OUT_OF_MEMORY;
}
s->bufr = p;
s->allc = a;
}
if(len)
memcpy(&s->bufr[idx], mem, len);
s->leng = idx + len;
s->bufr[s->leng] = 0;
return CURLE_OK;
}
/*
* Clears the string, keeps the allocation. This can also be called on a
* buffer that already was freed.
*/
void curlx_dyn_reset(struct dynbuf *s)
{
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
DEBUGASSERT(!s->leng || s->bufr);
if(s->leng)
s->bufr[0] = 0;
s->leng = 0;
}
/*
* Specify the size of the tail to keep (number of bytes from the end of the
* buffer). The rest will be dropped.
*/
CURLcode curlx_dyn_tail(struct dynbuf *s, size_t trail)
{
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
DEBUGASSERT(!s->leng || s->bufr);
if(trail > s->leng)
return CURLE_BAD_FUNCTION_ARGUMENT;
else if(trail == s->leng)
return CURLE_OK;
else if(!trail) {
curlx_dyn_reset(s);
}
else {
memmove(&s->bufr[0], &s->bufr[s->leng - trail], trail);
s->leng = trail;
s->bufr[s->leng] = 0;
}
return CURLE_OK;
}
/*
* Appends a buffer with length.
*/
CURLcode curlx_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
{
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
DEBUGASSERT(!s->leng || s->bufr);
return dyn_nappend(s, mem, len);
}
/*
* Append a null-terminated string at the end.
*/
CURLcode curlx_dyn_add(struct dynbuf *s, const char *str)
{
size_t n;
DEBUGASSERT(str);
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
DEBUGASSERT(!s->leng || s->bufr);
n = strlen(str);
return dyn_nappend(s, (const unsigned char *)str, n);
}
/*
* Append a string vprintf()-style
*/
CURLcode curlx_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
{
#ifdef BUILDING_LIBCURL
int rc;
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
DEBUGASSERT(!s->leng || s->bufr);
DEBUGASSERT(fmt);
rc = curlx_dyn_vprintf(s, fmt, ap);
if(!rc)
return CURLE_OK;
else if(rc == MERR_TOO_LARGE)
return CURLE_TOO_LARGE;
return CURLE_OUT_OF_MEMORY;
#else
char *str;
str = curl_mvaprintf(fmt, ap); /* this allocs a new string to append */
if(str) {
CURLcode result = dyn_nappend(s, (const unsigned char *)str, strlen(str));
free(str);
return result;
}
/* If we failed, we cleanup the whole buffer and return error */
curlx_dyn_free(s);
return CURLE_OUT_OF_MEMORY;
#endif
}
/*
* Append a string printf()-style
*/
CURLcode curlx_dyn_addf(struct dynbuf *s, const char *fmt, ...)
{
CURLcode result;
va_list ap;
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
DEBUGASSERT(!s->leng || s->bufr);
DEBUGASSERT(strcmp(fmt, "%s")); /* use curlx_dyn_add instead */
va_start(ap, fmt);
result = curlx_dyn_vaddf(s, fmt, ap);
va_end(ap);
return result;
}
/*
* Returns a pointer to the buffer.
*/
char *curlx_dyn_ptr(const struct dynbuf *s)
{
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
DEBUGASSERT(!s->leng || s->bufr);
return s->bufr;
}
char *curlx_dyn_take(struct dynbuf *s, size_t *plen)
{
char *ptr = s->bufr;
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
*plen = s->leng;
s->bufr = NULL;
s->leng = 0;
s->allc = 0;
return ptr;
}
/*
* Returns an unsigned pointer to the buffer.
*/
unsigned char *curlx_dyn_uptr(const struct dynbuf *s)
{
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
DEBUGASSERT(!s->leng || s->bufr);
return (unsigned char *)s->bufr;
}
/*
* Returns the length of the buffer.
*/
size_t curlx_dyn_len(const struct dynbuf *s)
{
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
DEBUGASSERT(!s->leng || s->bufr);
return s->leng;
}
/*
* Set a new (smaller) length.
*/
CURLcode curlx_dyn_setlen(struct dynbuf *s, size_t set)
{
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
DEBUGASSERT(!s->leng || s->bufr);
if(set > s->leng)
return CURLE_BAD_FUNCTION_ARGUMENT;
s->leng = set;
s->bufr[s->leng] = 0;
return CURLE_OK;
}
+85
View File
@@ -0,0 +1,85 @@
#ifndef HEADER_CURL_DYNBUF_H
#define HEADER_CURL_DYNBUF_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include <curl/curl.h>
struct dynbuf {
char *bufr; /* point to a null-terminated allocated buffer */
size_t leng; /* number of bytes *EXCLUDING* the null-terminator */
size_t allc; /* size of the current allocation */
size_t toobig; /* size limit for the buffer */
#ifdef DEBUGBUILD
int init; /* detect API usage mistakes */
#endif
};
void curlx_dyn_init(struct dynbuf *s, size_t toobig);
void curlx_dyn_free(struct dynbuf *s);
CURLcode curlx_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
WARN_UNUSED_RESULT;
CURLcode curlx_dyn_add(struct dynbuf *s, const char *str)
WARN_UNUSED_RESULT;
CURLcode curlx_dyn_addf(struct dynbuf *s, const char *fmt, ...)
WARN_UNUSED_RESULT CURL_PRINTF(2, 3);
CURLcode curlx_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
WARN_UNUSED_RESULT CURL_PRINTF(2, 0);
void curlx_dyn_reset(struct dynbuf *s);
CURLcode curlx_dyn_tail(struct dynbuf *s, size_t trail);
CURLcode curlx_dyn_setlen(struct dynbuf *s, size_t set);
char *curlx_dyn_ptr(const struct dynbuf *s);
unsigned char *curlx_dyn_uptr(const struct dynbuf *s);
size_t curlx_dyn_len(const struct dynbuf *s);
/* returns 0 on success, -1 on error */
/* The implementation of this function exists in mprintf.c */
int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save);
/* Take the buffer out of the dynbuf. Caller has ownership and
* dynbuf resets to initial state. */
char *curlx_dyn_take(struct dynbuf *s, size_t *plen);
/* Dynamic buffer max sizes */
#define MAX_DYNBUF_SIZE (SIZE_MAX/2)
#define DYN_DOH_RESPONSE 3000
#define DYN_DOH_CNAME 256
#define DYN_PAUSE_BUFFER (64 * 1024 * 1024)
#define DYN_HAXPROXY 2048
#define DYN_HTTP_REQUEST (1024*1024)
#define DYN_APRINTF 8000000
#define DYN_RTSP_REQ_HEADER (64*1024)
#define DYN_TRAILERS (64*1024)
#define DYN_PROXY_CONNECT_HEADERS 16384
#define DYN_QLOG_NAME 1024
#define DYN_H1_TRAILER 4096
#define DYN_PINGPPONG_CMD (64*1024)
#define DYN_IMAP_CMD (64*1024)
#define DYN_MQTT_RECV (64*1024)
#define DYN_MQTT_SEND 0xFFFFFFF
#define DYN_CRLFILE_SIZE (400*1024*1024) /* 400mb */
#define DYN_CERTFILE_SIZE (100*1024) /* 100KiB */
#define DYN_KEYFILE_SIZE (100*1024) /* 100KiB */
#endif
+324
View File
@@ -0,0 +1,324 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
/*
* This file is 'mem-include-scan' clean, which means its memory allocations
* are not tracked by the curl memory tracker memdebug, so they must not use
* `CURLDEBUG` macro replacements in memdebug.h for free, malloc, etc. To avoid
* these macro replacements, wrap the names in parentheses to call the original
* versions: `ptr = (malloc)(123)`, `(free)(ptr)`, etc.
*/
#include "../curl_setup.h"
#include "fopen.h"
int curlx_fseek(void *stream, curl_off_t offset, int whence)
{
#if defined(_WIN32) && defined(USE_WIN32_LARGE_FILES)
return _fseeki64(stream, (__int64)offset, whence);
#elif defined(HAVE_FSEEKO) && defined(HAVE_DECL_FSEEKO)
return fseeko(stream, (off_t)offset, whence);
#else
if(offset > LONG_MAX)
return -1;
return fseek(stream, (long)offset, whence);
#endif
}
#if defined(_WIN32) && !defined(UNDER_CE)
#include "multibyte.h"
/* declare GetFullPathNameW for mingw-w64 UWP builds targeting old windows */
#if defined(CURL_WINDOWS_UWP) && defined(__MINGW32__) && \
(_WIN32_WINNT < _WIN32_WINNT_WIN10)
WINBASEAPI DWORD WINAPI GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR *);
#endif
/* Fix excessive paths (paths that exceed MAX_PATH length of 260).
*
* This is a helper function to fix paths that would exceed the MAX_PATH
* limitation check done by Windows APIs. It does so by normalizing the passed
* in filename or path 'in' to its full canonical path, and if that path is
* longer than MAX_PATH then setting 'out' to "\\?\" prefix + that full path.
*
* For example 'in' filename255chars in current directory C:\foo\bar is
* fixed as \\?\C:\foo\bar\filename255chars for 'out' which will tell Windows
* it is ok to access that filename even though the actual full path is longer
* than 260 chars.
*
* For non-Unicode builds this function may fail sometimes because only the
* Unicode versions of some Windows API functions can access paths longer than
* MAX_PATH, for example GetFullPathNameW which is used in this function. When
* the full path is then converted from Unicode to multibyte that fails if any
* directories in the path contain characters not in the current codepage.
*/
static bool fix_excessive_path(const TCHAR *in, TCHAR **out)
{
size_t needed, count;
const wchar_t *in_w;
wchar_t *fbuf = NULL;
/* MS documented "approximate" limit for the maximum path length */
const size_t max_path_len = 32767;
#ifndef _UNICODE
wchar_t *ibuf = NULL;
char *obuf = NULL;
#endif
*out = NULL;
/* skip paths already normalized */
if(!_tcsncmp(in, _T("\\\\?\\"), 4))
goto cleanup;
#ifndef _UNICODE
/* convert multibyte input to unicode */
needed = mbstowcs(NULL, in, 0);
if(needed == (size_t)-1 || needed >= max_path_len)
goto cleanup;
++needed; /* for NUL */
ibuf = (malloc)(needed * sizeof(wchar_t));
if(!ibuf)
goto cleanup;
count = mbstowcs(ibuf, in, needed);
if(count == (size_t)-1 || count >= needed)
goto cleanup;
in_w = ibuf;
#else
in_w = in;
#endif
/* GetFullPathNameW returns the normalized full path in unicode. It converts
forward slashes to backslashes, processes .. to remove directory segments,
etc. Unlike GetFullPathNameA it can process paths that exceed MAX_PATH. */
needed = (size_t)GetFullPathNameW(in_w, 0, NULL, NULL);
if(!needed || needed > max_path_len)
goto cleanup;
/* skip paths that are not excessive and do not need modification */
if(needed <= MAX_PATH)
goto cleanup;
fbuf = (malloc)(needed * sizeof(wchar_t));
if(!fbuf)
goto cleanup;
count = (size_t)GetFullPathNameW(in_w, (DWORD)needed, fbuf, NULL);
if(!count || count >= needed)
goto cleanup;
/* prepend \\?\ or \\?\UNC\ to the excessively long path.
*
* c:\longpath ---> \\?\c:\longpath
* \\.\c:\longpath ---> \\?\c:\longpath
* \\?\c:\longpath ---> \\?\c:\longpath (unchanged)
* \\server\c$\longpath ---> \\?\UNC\server\c$\longpath
*
* https://learn.microsoft.com/dotnet/standard/io/file-path-formats
*/
if(!wcsncmp(fbuf, L"\\\\?\\", 4))
; /* do nothing */
else if(!wcsncmp(fbuf, L"\\\\.\\", 4))
fbuf[2] = '?';
else if(!wcsncmp(fbuf, L"\\\\.", 3) || !wcsncmp(fbuf, L"\\\\?", 3)) {
/* Unexpected, not UNC. The formatting doc doesn't allow this AFAICT. */
goto cleanup;
}
else {
wchar_t *temp;
if(!wcsncmp(fbuf, L"\\\\", 2)) {
/* "\\?\UNC\" + full path without "\\" + null */
needed = 8 + (count - 2) + 1;
if(needed > max_path_len)
goto cleanup;
temp = (malloc)(needed * sizeof(wchar_t));
if(!temp)
goto cleanup;
wcsncpy(temp, L"\\\\?\\UNC\\", 8);
wcscpy(temp + 8, fbuf + 2);
}
else {
/* "\\?\" + full path + null */
needed = 4 + count + 1;
if(needed > max_path_len)
goto cleanup;
temp = (malloc)(needed * sizeof(wchar_t));
if(!temp)
goto cleanup;
wcsncpy(temp, L"\\\\?\\", 4);
wcscpy(temp + 4, fbuf);
}
(free)(fbuf);
fbuf = temp;
}
#ifndef _UNICODE
/* convert unicode full path to multibyte output */
needed = wcstombs(NULL, fbuf, 0);
if(needed == (size_t)-1 || needed >= max_path_len)
goto cleanup;
++needed; /* for NUL */
obuf = (malloc)(needed);
if(!obuf)
goto cleanup;
count = wcstombs(obuf, fbuf, needed);
if(count == (size_t)-1 || count >= needed)
goto cleanup;
*out = obuf;
obuf = NULL;
#else
*out = fbuf;
fbuf = NULL;
#endif
cleanup:
(free)(fbuf);
#ifndef _UNICODE
(free)(ibuf);
(free)(obuf);
#endif
return *out ? true : false;
}
int curlx_win32_open(const char *filename, int oflag, ...)
{
int pmode = 0;
int result = -1;
TCHAR *fixed = NULL;
const TCHAR *target = NULL;
#ifdef _UNICODE
wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename);
#endif
va_list param;
va_start(param, oflag);
if(oflag & O_CREAT)
pmode = va_arg(param, int);
va_end(param);
#ifdef _UNICODE
if(filename_w) {
if(fix_excessive_path(filename_w, &fixed))
target = fixed;
else
target = filename_w;
result = _wopen(target, oflag, pmode);
curlx_unicodefree(filename_w);
}
else
/* !checksrc! disable ERRNOVAR 1 */
CURL_SETERRNO(EINVAL);
#else
if(fix_excessive_path(filename, &fixed))
target = fixed;
else
target = filename;
result = _open(target, oflag, pmode);
#endif
(free)(fixed);
return result;
}
FILE *curlx_win32_fopen(const char *filename, const char *mode)
{
FILE *result = NULL;
TCHAR *fixed = NULL;
const TCHAR *target = NULL;
#ifdef _UNICODE
wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename);
wchar_t *mode_w = curlx_convert_UTF8_to_wchar(mode);
if(filename_w && mode_w) {
if(fix_excessive_path(filename_w, &fixed))
target = fixed;
else
target = filename_w;
result = _wfopen(target, mode_w);
}
else
/* !checksrc! disable ERRNOVAR 1 */
CURL_SETERRNO(EINVAL);
curlx_unicodefree(filename_w);
curlx_unicodefree(mode_w);
#else
if(fix_excessive_path(filename, &fixed))
target = fixed;
else
target = filename;
/* !checksrc! disable BANNEDFUNC 1 */
result = fopen(target, mode);
#endif
(free)(fixed);
return result;
}
int curlx_win32_stat(const char *path, struct_stat *buffer)
{
int result = -1;
TCHAR *fixed = NULL;
const TCHAR *target = NULL;
#ifdef _UNICODE
wchar_t *path_w = curlx_convert_UTF8_to_wchar(path);
if(path_w) {
if(fix_excessive_path(path_w, &fixed))
target = fixed;
else
target = path_w;
#ifndef USE_WIN32_LARGE_FILES
result = _wstat(target, buffer);
#else
result = _wstati64(target, buffer);
#endif
curlx_unicodefree(path_w);
}
else
/* !checksrc! disable ERRNOVAR 1 */
CURL_SETERRNO(EINVAL);
#else
if(fix_excessive_path(path, &fixed))
target = fixed;
else
target = path;
#ifndef USE_WIN32_LARGE_FILES
result = _stat(target, buffer);
#else
result = _stati64(target, buffer);
#endif
#endif
(free)(fixed);
return result;
}
#endif /* _WIN32 && !UNDER_CE */
+60
View File
@@ -0,0 +1,60 @@
#ifndef HEADER_CURLX_FOPEN_H
#define HEADER_CURLX_FOPEN_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* SPDX-License-Identifier: curl
*
***************************************************************************/
#include "../curl_setup.h"
#include "multibyte.h"
#ifdef HAVE_FCNTL_H
#include <fcntl.h> /* for open() and attributes */
#endif
int curlx_fseek(void *stream, curl_off_t offset, int whence);
#if defined(_WIN32) && !defined(UNDER_CE)
FILE *curlx_win32_fopen(const char *filename, const char *mode);
int curlx_win32_stat(const char *path, struct_stat *buffer);
int curlx_win32_open(const char *filename, int oflag, ...);
#define CURLX_FOPEN_LOW(fname, mode) curlx_win32_fopen(fname, mode)
#define curlx_stat(fname, stp) curlx_win32_stat(fname, stp)
#define curlx_open curlx_win32_open
#else
#define CURLX_FOPEN_LOW fopen
#define curlx_stat(fname, stp) stat(fname, stp)
#define curlx_open open
#endif
#ifdef CURLDEBUG
#define curlx_fopen(file,mode) curl_dbg_fopen(file,mode,__LINE__,__FILE__)
#define curlx_fdopen(file,mode) curl_dbg_fdopen(file,mode,__LINE__,__FILE__)
#define curlx_fclose(file) curl_dbg_fclose(file,__LINE__,__FILE__)
#else
#define curlx_fopen CURLX_FOPEN_LOW
#define curlx_fdopen fdopen
#define curlx_fclose fclose
#endif
#endif /* HEADER_CURLX_FOPEN_H */

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